[Xamarin.Android.Build.Tasks] Use libZipSharp to build the .apk (#89)
Commit 4ec06ac9
*broke* generation of `.apk` files when migrating
from `Ionic.Zip.dll` to `System.IO.Compression` because this:
apk.AddFile (assembly.ItemSpec, "assemblies", compressionLevel: CompressionLevel.NoCompression);
doesn't work the way we thought/hoped it would.
Specifically, we require that assemblies be *stored*, uncompressed,
within the `.apk`, as we **mmap**(2) the `.apk` and hand off the
memory addresses of the loaded assemblies to mono for execution.
`CompressionLevel.NoCompression`, despite saying "no compression",
does *not* mean "store". It means "add the file 'normally', but tell
`DeflateStream` to use 'no compression'."
The result is that the zip entry doesn't say it's stored:
$ unzip -lv bin/Debug/*-Signed.apk | grep assemblies/
64512 Defl:N 64512 67% 06-16-16 10:38 6d65706b assemblies/Scratch.DebugRelease.dll
Which in turn means that the resulting `.apk` is *unusable*.
The fix? `System.IO.Compression` can't be used for this, and
`Ionic.Zip.dll` had other problems (why we tried to use
`System.IO.Compression` in the first place!), so instead we'll
use [libZip][0] and [LibZipSharp][1] to handle `.apk` files.
libzip is a well-maintained OSS library for manipulating zip files,
and LibZipSharp is a C# wrapper around libzip.
Add libzip and LibZipSharp to the build system so that they're
available on required paltforms, and update the `BuildApk` and
related tasks to use LibZipSharp instead of System.IO.Compression.
This allows assemblies to be properly stored in the `.apk`,
allowing apps to execute as intended.
$ unzip -lv bin/Debug/*-Signed.apk | grep assembl
64512 Stored 64512 0% 06-29-16 08:31 6d65706b assemblies/Scratch.DebugRelease.dll
[0]: http://www.nih.at/libzip/
[1]: https://github.com/grendello/LibZipSharp/
This commit is contained in:
Родитель
762deb947a
Коммит
f3d62b6168
|
@ -17,3 +17,11 @@
|
|||
path = external/opentk
|
||||
url = https://github.com/mono/opentk.git
|
||||
branch = master
|
||||
[submodule "external/libzip"]
|
||||
path = external/libzip
|
||||
url = https://github.com/nih-at/libzip.git
|
||||
branch = master
|
||||
[submodule "external/LibZipSharp"]
|
||||
path = external/LibZipSharp
|
||||
url = https://github.com/grendello/LibZipSharp.git
|
||||
branch = master
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
<JavaInteropSourceDirectory Condition=" '$(JavaInteropSourceDirectory)' == '' ">$(MSBuildThisFileDirectory)external\Java.Interop</JavaInteropSourceDirectory>
|
||||
<MonoSourceDirectory>$(MSBuildThisFileDirectory)external\mono</MonoSourceDirectory>
|
||||
<OpenTKSourceDirectory>$(MSBuildThisFileDirectory)external\opentk</OpenTKSourceDirectory>
|
||||
<LibZipSourceDirectory Condition=" '$(LibZipSourceDirectory)' == '' ">$(MSBuildThisFileDirectory)external\libzip</LibZipSourceDirectory>
|
||||
<LibZipSharpSourceDirectory Condition=" '$(LibZipSharpSourceDirectory)' == '' ">$(MSBuildThisFileDirectory)external\LibZipSharp</LibZipSharpSourceDirectory>
|
||||
<SqliteSourceDirectory Condition=" '$(SqliteSourceDirectory)' == '' ">$(MSBuildThisFileDirectory)external\sqlite</SqliteSourceDirectory>
|
||||
<XamarinAndroidSourcePath>$(MSBuildThisFileDirectory)</XamarinAndroidSourcePath>
|
||||
<AllSupported32BitTargetAndroidAbis>armeabi;armeabi-v7a;x86</AllSupported32BitTargetAndroidAbis>
|
||||
|
@ -40,6 +42,8 @@
|
|||
<MonoSourceFullPath>$([System.IO.Path]::GetFullPath ('$(MonoSourceDirectory)'))</MonoSourceFullPath>
|
||||
<SqliteSourceFullPath>$([System.IO.Path]::GetFullPath ('$(SqliteSourceDirectory)'))</SqliteSourceFullPath>
|
||||
<OpenTKSourceFullPath>$([System.IO.Path]::GetFullPath ('$(OpenTKSourceDirectory)'))</OpenTKSourceFullPath>
|
||||
<LibZipSourceFullPath>$([System.IO.Path]::GetFullPath ('$(LibZipSourceDirectory)'))</LibZipSourceFullPath>
|
||||
<LibZipSharpSourceFullPath>$([System.IO.Path]::GetFullPath ('$(LibZipSharpSourceDirectory)'))</LibZipSharpSourceFullPath>
|
||||
</PropertyGroup>
|
||||
<!--
|
||||
"Fixup" $(AndroidSupportedHostJitAbis) so that Condition attributes elsewhere
|
||||
|
|
|
@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Android.Tools.Boots
|
|||
EndProject
|
||||
Project("{9344BDBB-3E7F-41FC-A0DD-8665D75EE146}") = "mono-runtimes", "build-tools\mono-runtimes\mono-runtimes.mdproj", "{C03E6CF1-7460-4CDC-A4AB-292BBC0F61F2}"
|
||||
EndProject
|
||||
Project("{9344BDBB-3E7F-41FC-A0DD-8665D75EE146}") = "libzip", "build-tools\libzip\libzip.mdproj", "{900A0F71-BAAD-417A-8D1A-8D330297CDD0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "jnienv-gen", "build-tools\jnienv-gen\jnienv-gen.csproj", "{AFB8F6D1-6EA9-42C3-950B-98F34C669AD2}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "api-merge", "build-tools\api-merge\api-merge.csproj", "{3FC3E78B-F7D4-42EA-BBE8-4535DF42BFF8}"
|
||||
|
@ -79,6 +81,8 @@ Project("{9344BDBB-3E7F-41FC-A0DD-8665D75EE146}") = "sqlite-xamarin", "src\sqlit
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTK", "src\OpenTK-1.0\OpenTK.csproj", "{5EB9E888-E357-417E-9F39-DDEC195CE47F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "libZipSharp", "external\LibZipSharp\libZipSharp.csproj", "{E248B2CA-303B-4645-ADDC-9D4459D550FD}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|AnyCPU = Debug|AnyCPU
|
||||
|
@ -358,6 +362,22 @@ Global
|
|||
{5EB9E888-E357-417E-9F39-DDEC195CE47F}.XAIntegrationDebug|AnyCPU.Build.0 = Debug|Any CPU
|
||||
{5EB9E888-E357-417E-9F39-DDEC195CE47F}.XAIntegrationRelease|AnyCPU.ActiveCfg = Debug|Any CPU
|
||||
{5EB9E888-E357-417E-9F39-DDEC195CE47F}.XAIntegrationRelease|AnyCPU.Build.0 = Debug|Any CPU
|
||||
{900A0F71-BAAD-417A-8D1A-8D330297CDD0}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU
|
||||
{900A0F71-BAAD-417A-8D1A-8D330297CDD0}.Debug|AnyCPU.Build.0 = Debug|Any CPU
|
||||
{900A0F71-BAAD-417A-8D1A-8D330297CDD0}.Release|AnyCPU.ActiveCfg = Debug|Any CPU
|
||||
{900A0F71-BAAD-417A-8D1A-8D330297CDD0}.Release|AnyCPU.Build.0 = Debug|Any CPU
|
||||
{900A0F71-BAAD-417A-8D1A-8D330297CDD0}.XAIntegrationDebug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{900A0F71-BAAD-417A-8D1A-8D330297CDD0}.XAIntegrationDebug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{900A0F71-BAAD-417A-8D1A-8D330297CDD0}.XAIntegrationRelease|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{900A0F71-BAAD-417A-8D1A-8D330297CDD0}.XAIntegrationRelease|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E248B2CA-303B-4645-ADDC-9D4459D550FD}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU
|
||||
{E248B2CA-303B-4645-ADDC-9D4459D550FD}.Debug|AnyCPU.Build.0 = Debug|Any CPU
|
||||
{E248B2CA-303B-4645-ADDC-9D4459D550FD}.Release|AnyCPU.ActiveCfg = Release|Any CPU
|
||||
{E248B2CA-303B-4645-ADDC-9D4459D550FD}.Release|AnyCPU.Build.0 = Release|Any CPU
|
||||
{E248B2CA-303B-4645-ADDC-9D4459D550FD}.XAIntegrationDebug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E248B2CA-303B-4645-ADDC-9D4459D550FD}.XAIntegrationDebug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E248B2CA-303B-4645-ADDC-9D4459D550FD}.XAIntegrationRelease|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E248B2CA-303B-4645-ADDC-9D4459D550FD}.XAIntegrationRelease|Any CPU.Build.0 = Debug|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{8FF78EB6-6FC8-46A7-8A15-EBBA9045C5FA} = {E351F97D-EA4F-4E7F-AAA0-8EBB1F2A4A62}
|
||||
|
@ -395,6 +415,8 @@ Global
|
|||
{26781D3A-FF20-4F55-9824-C8A06AA9E484} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
|
||||
{B8F799C5-D7CE-4E09-9CE6-BAA4173E7EC8} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
|
||||
{5EB9E888-E357-417E-9F39-DDEC195CE47F} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
|
||||
{900A0F71-BAAD-417A-8D1A-8D330297CDD0} = {E351F97D-EA4F-4E7F-AAA0-8EBB1F2A4A62}
|
||||
{E248B2CA-303B-4645-ADDC-9D4459D550FD} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
|
||||
EndGlobalSection
|
||||
GlobalSection(MonoDevelopProperties) = preSolution
|
||||
Policies = $0
|
||||
|
|
|
@ -122,9 +122,9 @@
|
|||
DependsOnTargets="_SetMxeToolchainMakefileTimeToLastCommitTimestamp"
|
||||
Condition="$(AndroidSupportedHostJitAbisForConditionalChecks.Contains (':mxe-Win64:'))"
|
||||
Inputs="..\..\external\mxe\Makefile"
|
||||
Outputs="$(AndroidMxeFullPath)\bin\i686-w64-mingw32.static-gcc">
|
||||
Outputs="$(AndroidMxeFullPath)\bin\i686-w64-mingw32.static-gcc;$(AndroidMxeFullPath)\bin\i686-w64-mingw32.static-cmake">
|
||||
<Exec
|
||||
Command="make $(MAKEFLAGS) MXE_TARGETS="i686-w64-mingw32.static" gcc PREFIX="$(AndroidMxeFullPath)""
|
||||
Command="make $(MAKEFLAGS) MXE_TARGETS="i686-w64-mingw32.static" gcc cmake zlib PREFIX="$(AndroidMxeFullPath)""
|
||||
WorkingDirectory="..\..\external\mxe"
|
||||
/>
|
||||
</Target>
|
||||
|
@ -132,9 +132,9 @@
|
|||
DependsOnTargets="_SetMxeToolchainMakefileTimeToLastCommitTimestamp"
|
||||
Condition="$(AndroidSupportedHostJitAbisForConditionalChecks.Contains (':mxe-Win64:'))"
|
||||
Inputs="..\..\external\mxe\Makefile"
|
||||
Outputs="$(AndroidMxeFullPath)\bin\x86_64-w64-mingw32.static-gcc">
|
||||
Outputs="$(AndroidMxeFullPath)\bin\x86_64-w64-mingw32.static-gcc;$(AndroidMxeFullPath)\bin\x86_64-w64-mingw32.static-cmake">
|
||||
<Exec
|
||||
Command="make $(MAKEFLAGS) MXE_TARGETS="x86_64-w64-mingw32.static" gcc PREFIX="$(AndroidMxeFullPath)""
|
||||
Command="make $(MAKEFLAGS) MXE_TARGETS="x86_64-w64-mingw32.static" gcc cmake zlib PREFIX="$(AndroidMxeFullPath)""
|
||||
WorkingDirectory="..\..\external\mxe"
|
||||
/>
|
||||
</Target>
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ItemType>GenericProject</ItemType>
|
||||
<ProjectGuid>{900A0F71-BAAD-417A-8D1A-8D330297CDD0}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\Configuration.props" />
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<OutputPath>..\..\bin\$(Configuration)</OutputPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<OutputPath>..\..\bin\$(Configuration)</OutputPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<BuildDependsOn>
|
||||
ResolveReferences;
|
||||
_Configure;
|
||||
_Make
|
||||
</BuildDependsOn>
|
||||
</PropertyGroup>
|
||||
<Import Project="libzip.targets" />
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\android-toolchain\android-toolchain.mdproj">
|
||||
<Project>{8FF78EB6-6FC8-46A7-8A15-EBBA9045C5FA}</Project>
|
||||
<Name>android-toolchain</Name>
|
||||
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<_LibZipTarget Include="host-mxe-Win64" Condition="$(AndroidSupportedHostJitAbisForConditionalChecks.Contains (':mxe-Win64:'))">
|
||||
<CMake>$(AndroidMxeFullPath)\bin\x86_64-w64-mingw32.static-cmake</CMake>
|
||||
<CMakeFlags></CMakeFlags>
|
||||
<OutputLibrary>libzip.dll</OutputLibrary>
|
||||
<OutputLibraryPath>lib/libzip.dll</OutputLibraryPath>
|
||||
</_LibZipTarget>
|
||||
<_LibZipTarget Include="host-Darwin" Condition="$(AndroidSupportedHostJitAbisForConditionalChecks.Contains (':Darwin:'))">
|
||||
<CMake>cmake</CMake>
|
||||
<CMakeFlags>-DCMAKE_OSX_ARCHITECTURES="i386;x86_64"</CMakeFlags>
|
||||
<OutputLibrary>libzip.3.0.dylib</OutputLibrary>
|
||||
<OutputLibraryPath>lib/libzip.3.0.dylib</OutputLibraryPath>
|
||||
</_LibZipTarget>
|
||||
<_LibZipTarget Include="host-Linux" Condition="$(AndroidSupportedHostJitAbisForConditionalChecks.Contains (':Linux:'))">
|
||||
<CMake>cmake</CMake>
|
||||
<CMakeFlags></CMakeFlags>
|
||||
<OutputLibrary>libzip.so</OutputLibrary>
|
||||
<OutputLibraryPath>libzip.so</OutputLibraryPath>
|
||||
</_LibZipTarget>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
</PropertyGroup>
|
||||
</Project>
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<_SourceTopDir>..\..</_SourceTopDir>
|
||||
</PropertyGroup>
|
||||
<UsingTask AssemblyFile="$(_SourceTopDir)\bin\Build$(Configuration)\Xamarin.Android.Tools.BootstrapTasks.dll" TaskName="Xamarin.Android.Tools.BootstrapTasks.GetNugetPackageBasePath" />
|
||||
<Import Project="libzip.props" />
|
||||
<Import Project="libzip.projitems" />
|
||||
<Target Name="_SetCMakeListsTxtTimeToLastCommitTimestamp">
|
||||
<Exec
|
||||
Command="touch -m -t `git log -1 --format=%25cd --date=format-local:%25Y%25m%25d%25H%25M.%25S` CMakeLists.txt"
|
||||
WorkingDirectory="$(LibZipSourceFullPath)"
|
||||
/>
|
||||
</Target>
|
||||
<Target Name="_Configure"
|
||||
DependsOnTargets="_SetCMakeListsTxtTimeToLastCommitTimestamp"
|
||||
Inputs="$(LibZipSourceFullPath\CMakeLists.txt"
|
||||
Outputs="$(IntermediateOutputPath)\%(_LibZipTarget.Identity)\Makefile">
|
||||
<MakeDir Directories="@(_LibZipTarget->'$(IntermediateOutputPath)\%(Identity)')" />
|
||||
<Exec
|
||||
Command="%(_LibZipTarget.CMake) %(_LibZipTarget.CMakeFlags) $(LibZipSourceFullPath)"
|
||||
WorkingDirectory="@(_LibZipTarget->'$(IntermediateOutputPath)\%(Identity)')"
|
||||
/>
|
||||
</Target>
|
||||
<Target Name="_Make"
|
||||
Inputs="$(IntermediateOutputPath)\%(_LibZipTarget.Identity)\Makefile"
|
||||
Outputs="$(IntermediateOutputPath)\%(_LibZipTarget.Identity)\%(_LibZipTarget.OutputLibraryPath)">
|
||||
<Exec
|
||||
Command="make"
|
||||
WorkingDirectory="$(IntermediateOutputPath)\%(_LibZipTarget.Identity)"
|
||||
/>
|
||||
<Copy SourceFiles="@(_LibZipTarget->'$(IntermediateOutputPath)\%(Identity)\%(_LibZipTarget.OutputLibraryPath)')"
|
||||
DestinationFiles="@(_LibZipTarget->'$(OutputPath)\lib\xbuild\Xamarin\Android\%(_LibZipTarget.OutputLibrary)')"/>
|
||||
</Target>
|
||||
</Project>
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 16c468310c2fefc8d8e2dfb5aff1e00cf024d5d2
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 1d8b1ac4d20b8ef8d3f5d496dabebaa0ff9019ff
|
|
@ -8,8 +8,6 @@ using System.Reflection;
|
|||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using System.IO.Compression;
|
||||
|
||||
using Microsoft.Build.Utilities;
|
||||
using Microsoft.Build.Framework;
|
||||
|
||||
|
@ -18,6 +16,7 @@ using Java.Interop.Tools.Cecil;
|
|||
using ArchiveFileList = System.Collections.Generic.List<System.Tuple<string, string>>;
|
||||
using Mono.Cecil;
|
||||
using Xamarin.Android.Build.Utilities;
|
||||
using Xamarin.Tools.Zip;
|
||||
|
||||
namespace Xamarin.Android.Tasks
|
||||
{
|
||||
|
@ -111,12 +110,12 @@ namespace Xamarin.Android.Tasks
|
|||
ArchiveFileList files = new ArchiveFileList ();
|
||||
if (apkInputPath != null)
|
||||
File.Copy (apkInputPath, apkOutputPath + "new", overwrite: true);
|
||||
using (var apk = ZipFile.Open (apkOutputPath + "new", apkInputPath != null ? ZipArchiveMode.Update : ZipArchiveMode.Create)) {
|
||||
using (var apk = ZipArchive.Open (apkOutputPath + "new", apkInputPath != null ? FileMode.Open : FileMode.Create )) {
|
||||
apk.AddEntry ("NOTICE",
|
||||
Assembly.GetExecutingAssembly ().GetManifestResourceStream ("NOTICE.txt"));
|
||||
|
||||
// Add classes.dx
|
||||
apk.AddFiles (DalvikClasses, string.Empty);
|
||||
apk.AddFiles (DalvikClasses);
|
||||
|
||||
if (EmbedAssemblies && !BundleAssemblies)
|
||||
AddAssemblies (apk);
|
||||
|
@ -128,7 +127,7 @@ namespace Xamarin.Android.Tasks
|
|||
AddNativeLibrariesFromAssemblies (apk, supportedAbis);
|
||||
|
||||
foreach (ITaskItem typemap in TypeMappings) {
|
||||
apk.AddFile (typemap.ItemSpec, directoryPathInZip: "", compressionLevel: CompressionLevel.NoCompression);
|
||||
apk.AddFile (typemap.ItemSpec, compressionMethod: CompressionMethod.Store);
|
||||
}
|
||||
|
||||
foreach (var file in files) {
|
||||
|
@ -138,7 +137,7 @@ namespace Xamarin.Android.Tasks
|
|||
Log.LogWarning (null, "XA4301", null, file.Item1, 0, 0, 0, 0, "Apk already contains the item {0}; ignoring.", item);
|
||||
continue;
|
||||
}
|
||||
apk.AddFile (file.Item1, file.Item2);
|
||||
apk.AddFile (file.Item1, item);
|
||||
}
|
||||
if (_Debug)
|
||||
AddGdbservers (apk, files, supportedAbis, debugServer);
|
||||
|
@ -156,23 +155,22 @@ namespace Xamarin.Android.Tasks
|
|||
jarFilePaths = MonoAndroidHelper.DistinctFilesByContent (jarFilePaths);
|
||||
|
||||
foreach (var jarFile in jarFilePaths) {
|
||||
using (var jar = new ZipArchive (File.Open (jarFile, FileMode.Open), ZipArchiveMode.Read)) {
|
||||
foreach (var jarItem in jar.Entries.Where (ze => !ze.IsDirectory () && !ze.FullName.StartsWith ("META-INF") && !ze.FullName.EndsWith (".class") && !ze.FullName.EndsWith (".java") && !ze.FullName.EndsWith ("MANIFEST.MF"))) {
|
||||
byte[] data;
|
||||
using (var d = new System.IO.MemoryStream ())
|
||||
using (var i = jarItem.Open ()) {
|
||||
i.CopyTo (d);
|
||||
using (var jar = ZipArchive.Open (File.Open (jarFile, FileMode.Open))) {
|
||||
foreach (var jarItem in jar.Where (ze => !ze.IsDirectory && !ze.FullName.StartsWith ("META-INF") && !ze.FullName.EndsWith (".class") && !ze.FullName.EndsWith (".java") && !ze.FullName.EndsWith ("MANIFEST.MF"))) {
|
||||
byte [] data;
|
||||
using (var d = new System.IO.MemoryStream ()) {
|
||||
jarItem.Extract (d);
|
||||
data = d.ToArray ();
|
||||
}
|
||||
if (apk.Entries.Any (e => e.FullName == jarItem.FullName))
|
||||
if (apk.Any (e => e.FullName == jarItem.FullName))
|
||||
Log.LogMessage ("Warning: failed to add jar entry {0} from {1}: the same file already exists in the apk", jarItem.FullName, Path.GetFileName (jarFile));
|
||||
else
|
||||
apk.AddEntry (jarItem.FullName, data);
|
||||
apk.AddEntry (data, jarItem.FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (StubApplicationDataFile != null && File.Exists (StubApplicationDataFile))
|
||||
AddZipEntry (apk, StubApplicationDataFile, string.Empty);
|
||||
apk.AddFile (StubApplicationDataFile, Path.GetFileName (StubApplicationDataFile));
|
||||
}
|
||||
MonoAndroidHelper.CopyIfZipChanged (apkOutputPath + "new", apkOutputPath);
|
||||
File.Delete (apkOutputPath + "new");
|
||||
|
@ -244,11 +242,6 @@ namespace Xamarin.Android.Tasks
|
|||
return !Log.HasLoggedErrors;
|
||||
}
|
||||
|
||||
void AddZipEntry (ZipArchive apk, string file, string path)
|
||||
{
|
||||
apk.AddFile (file, path);
|
||||
}
|
||||
|
||||
private void AddAssemblies (ZipArchive apk)
|
||||
{
|
||||
bool debug = _Debug;
|
||||
|
@ -256,7 +249,7 @@ namespace Xamarin.Android.Tasks
|
|||
|
||||
foreach (ITaskItem assembly in ResolvedUserAssemblies) {
|
||||
// Add assembly
|
||||
apk.AddFile (assembly.ItemSpec, GetTargetDirectory (assembly.ItemSpec), compressionLevel: CompressionLevel.NoCompression);
|
||||
apk.AddFile (assembly.ItemSpec, GetTargetDirectory (assembly.ItemSpec), compressionMethod: CompressionMethod.Store);
|
||||
|
||||
// Try to add config if exists
|
||||
var config = Path.ChangeExtension (assembly.ItemSpec, "dll.config");
|
||||
|
@ -267,7 +260,7 @@ namespace Xamarin.Android.Tasks
|
|||
var symbols = Path.ChangeExtension (assembly.ItemSpec, "dll.mdb");
|
||||
|
||||
if (File.Exists (symbols))
|
||||
apk.AddFile (symbols, "assemblies", compressionLevel: CompressionLevel.NoCompression);
|
||||
apk.AddFile (symbols, "assemblies", compressionMethod: CompressionMethod.Store);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -276,7 +269,7 @@ namespace Xamarin.Android.Tasks
|
|||
|
||||
// Add framework assemblies
|
||||
foreach (ITaskItem assembly in ResolvedFrameworkAssemblies) {
|
||||
apk.AddFile (assembly.ItemSpec, "assemblies", compressionLevel: CompressionLevel.NoCompression);
|
||||
apk.AddFile (assembly.ItemSpec, "assemblies", compressionMethod: CompressionMethod.Store);
|
||||
var config = Path.ChangeExtension (assembly.ItemSpec, "dll.config");
|
||||
AddAssemblyConfigEntry (apk, config);
|
||||
// Try to add symbols if Debug
|
||||
|
@ -284,7 +277,7 @@ namespace Xamarin.Android.Tasks
|
|||
var symbols = Path.ChangeExtension (assembly.ItemSpec, "dll.mdb");
|
||||
|
||||
if (File.Exists (symbols))
|
||||
apk.AddFile (symbols, "assemblies", compressionLevel: CompressionLevel.NoCompression);
|
||||
apk.AddFile (symbols, "assemblies", compressionMethod: CompressionMethod.Store);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -299,7 +292,7 @@ namespace Xamarin.Android.Tasks
|
|||
source.CopyTo (dest);
|
||||
dest.WriteByte (0);
|
||||
dest.Position = 0;
|
||||
apk.AddEntry ("assemblies/" + Path.GetFileName (configFile), dest, compressionLevel: CompressionLevel.NoCompression);
|
||||
apk.AddEntry ("assemblies/" + Path.GetFileName (configFile), dest, compressionMethod: CompressionMethod.Store);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -462,18 +455,19 @@ namespace Xamarin.Android.Tasks
|
|||
continue;
|
||||
var data = ressozip.GetResourceData ();
|
||||
using (var ms = new MemoryStream (data)) {
|
||||
using (var zip = new ZipArchive (ms, ZipArchiveMode.Read)) {
|
||||
foreach (var e in zip.Entries.Where (x => abis.Any (a => x.FullName.Contains (a)))) {
|
||||
if (e.IsDirectory ())
|
||||
using (var zip = ZipArchive.Open (ms)) {
|
||||
foreach (var e in zip.Where (x => abis.Any (a => x.FullName.Contains (a)))) {
|
||||
if (e.IsDirectory)
|
||||
continue;
|
||||
var key = e.FullName.Replace ("native_library_imports", "lib");
|
||||
if (apk.ContainsEntry (key)) {
|
||||
if (apk.Any(k => k.FullName == key)) {
|
||||
Log.LogCodedWarning ("4301", "Apk already contains the item {0}; ignoring.", key);
|
||||
continue;
|
||||
}
|
||||
using (var s = new MemoryStream ()) {
|
||||
e.Extract (s);
|
||||
apk.AddEntry (key, s.ToArray ());
|
||||
s.Position = 0;
|
||||
apk.AddEntry (s.ToArray (),key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ using System.Xml.Linq;
|
|||
using Microsoft.Build.Utilities;
|
||||
using Microsoft.Build.Framework;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.IO.Compression;
|
||||
using Xamarin.Tools.Zip;
|
||||
|
||||
using Xamarin.Android.Tools;
|
||||
|
||||
|
@ -79,7 +79,8 @@ namespace Xamarin.Android.Tasks
|
|||
string tmpname = Path.Combine (Path.GetTempPath (), "monodroid_import_" + Guid.NewGuid ().ToString ());
|
||||
try {
|
||||
Directory.CreateDirectory (tmpname);
|
||||
ZipFile.ExtractToDirectory (p, tmpname, new System.Text.UTF8Encoding (false));
|
||||
var archive = ZipArchive.Open (p, FileMode.Open);
|
||||
archive.ExtractAll (tmpname);
|
||||
|
||||
if (!CopyLibraryContent (tmpname, p.EndsWith (".aar", StringComparison.OrdinalIgnoreCase)))
|
||||
return false;
|
||||
|
@ -91,7 +92,7 @@ namespace Xamarin.Android.Tasks
|
|||
|
||||
// Archive them in a zip.
|
||||
using (var stream = new MemoryStream ()) {
|
||||
using (var zip = new ZipArchive (stream, ZipArchiveMode.Create, true, new System.Text.UTF8Encoding (false))) {
|
||||
using (var zip = ZipArchive.Create (stream)) {
|
||||
zip.AddDirectory (OutputDirectory, outDirInfo.Name);
|
||||
}
|
||||
stream.Position = 0;
|
||||
|
|
|
@ -8,7 +8,7 @@ using System.Xml.Linq;
|
|||
using Microsoft.Build.Utilities;
|
||||
using Microsoft.Build.Framework;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.IO.Compression;
|
||||
using Xamarin.Tools.Zip;
|
||||
|
||||
using Xamarin.Android.Tools;
|
||||
|
||||
|
@ -106,7 +106,8 @@ namespace Xamarin.Android.Tasks
|
|||
|
||||
// Archive them in a zip.
|
||||
using (var stream = new MemoryStream ()) {
|
||||
using (var zip = new ZipArchive (stream, ZipArchiveMode.Create, true, new System.Text.UTF8Encoding (false))) {
|
||||
using (var zip = ZipArchive.Create (stream)) {
|
||||
Log.LogDebugMessage ($" {OutputDirectory} {outDirInfo.Name} ");
|
||||
zip.AddDirectory (OutputDirectory, outDirInfo.Name);
|
||||
}
|
||||
stream.Position = 0;
|
||||
|
|
|
@ -8,7 +8,7 @@ using System.Xml.Linq;
|
|||
using Microsoft.Build.Utilities;
|
||||
using Microsoft.Build.Framework;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.IO.Compression;
|
||||
using Xamarin.Tools.Zip;
|
||||
|
||||
using Xamarin.Android.Tools;
|
||||
|
||||
|
@ -64,7 +64,7 @@ namespace Xamarin.Android.Tasks
|
|||
|
||||
// Archive native libraries in a zip.
|
||||
using (var stream = new MemoryStream ()) {
|
||||
using (var zip = new ZipArchive (stream, ZipArchiveMode.Create, true, new System.Text.UTF8Encoding (false))) {
|
||||
using (var zip = ZipArchive.Create (stream)) {
|
||||
zip.AddDirectory (OutputDirectory, outDirInfo.Name);
|
||||
}
|
||||
stream.Position = 0;
|
||||
|
|
|
@ -162,7 +162,7 @@ namespace Xamarin.Android.Tasks {
|
|||
LogMessage ("Extracting {0} to {1}", file, contentDir);
|
||||
using (var zip = MonoAndroidHelper.ReadZipFile (file)) {
|
||||
int extracted = 0;
|
||||
var o = Math.Max(1, (zip.Entries.Count / 10));
|
||||
var o = Math.Max(1, (zip.EntryCount / 10));
|
||||
Files.ExtractAll (zip, contentDir, (progress, total) => {
|
||||
if ((progress % o) != 0 || extracted == progress || progress == 0)
|
||||
return;
|
||||
|
|
|
@ -13,6 +13,7 @@ using Microsoft.Build.Framework;
|
|||
using System.Text.RegularExpressions;
|
||||
using Xamarin.Android.Build.Utilities;
|
||||
using System.IO.Compression;
|
||||
using Xamarin.Tools.Zip;
|
||||
|
||||
namespace Xamarin.Android.Tasks
|
||||
{
|
||||
|
@ -123,7 +124,7 @@ namespace Xamarin.Android.Tasks
|
|||
|
||||
if (File.Exists (ProguardJarInput))
|
||||
File.Delete (ProguardJarInput);
|
||||
using (var zip = ZipFile.Open (ProguardJarInput, ZipArchiveMode.Create, new System.Text.UTF8Encoding (false))) {
|
||||
using (var zip = ZipArchive.Open (ProguardJarInput, FileMode.Create)) {
|
||||
foreach (var file in Directory.GetFiles (classesFullPath, "*", SearchOption.AllDirectories))
|
||||
zip.AddFile (file, Path.GetDirectoryName (file.Substring (classesFullPath.Length)));
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ using System;
|
|||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
using System.IO.Compression;
|
||||
using Xamarin.Tools.Zip;
|
||||
#if MSBUILD
|
||||
using Microsoft.Build.Utilities;
|
||||
using Xamarin.Android.Tasks;
|
||||
|
@ -157,9 +157,9 @@ namespace Xamarin.Android.Tools {
|
|||
string hashes = String.Empty;
|
||||
|
||||
try {
|
||||
using (var zip = new ZipArchive (stream, ZipArchiveMode.Read)) {
|
||||
foreach (var item in zip.Entries) {
|
||||
hashes += String.Format ("{0}{1}", item.FullName, item.Hash());
|
||||
using (var zip = ZipArchive.Open (stream)) {
|
||||
foreach (var item in zip) {
|
||||
hashes += String.Format ("{0}{1}", item.FullName, item.CRC);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
|
@ -178,8 +178,8 @@ namespace Xamarin.Android.Tools {
|
|||
return File.ReadAllText (filename + ".hash");
|
||||
|
||||
using (var zip = ReadZipFile (filename)) {
|
||||
foreach (var item in zip.Entries) {
|
||||
hashes += String.Format ("{0}{1}", item.FullName, item.Hash());
|
||||
foreach (var item in zip) {
|
||||
hashes += String.Format ("{0}{1}", item.FullName, item.CRC);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
|
@ -190,26 +190,26 @@ namespace Xamarin.Android.Tools {
|
|||
|
||||
public static ZipArchive ReadZipFile (string filename)
|
||||
{
|
||||
return ZipFile.Open (filename, ZipArchiveMode.Read, new System.Text.UTF8Encoding (false));
|
||||
return ZipArchive.Open (filename, FileMode.Open);
|
||||
}
|
||||
|
||||
public static void ExtractAll(ZipArchive zip, string destination, Action<int, int> progressCallback = null)
|
||||
{
|
||||
int i = 0;
|
||||
int total = zip.Entries.Count;
|
||||
foreach (var entry in zip.Entries) {
|
||||
int total = (int)zip.EntryCount;
|
||||
foreach (var entry in zip) {
|
||||
if (entry.FullName.Contains ("/__MACOSX/") ||
|
||||
entry.FullName.EndsWith ("/__MACOSX", StringComparison.OrdinalIgnoreCase) ||
|
||||
entry.FullName.EndsWith ("/.DS_Store", StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
if (entry.IsDirectory ()) {
|
||||
if (entry.IsDirectory) {
|
||||
Directory.CreateDirectory (Path.Combine (destination, entry.FullName));
|
||||
continue;
|
||||
}
|
||||
if (progressCallback != null)
|
||||
progressCallback (i++, total);
|
||||
Directory.CreateDirectory (Path.Combine (destination, Path.GetDirectoryName (entry.FullName)));
|
||||
entry.ExtractToFile (Path.Combine (destination, entry.FullName), overwrite: true);
|
||||
entry.Extract (destination, entry.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,8 +7,7 @@ using System.IO;
|
|||
using System.Security.Cryptography;
|
||||
using Mono.Security.Cryptography;
|
||||
using Xamarin.Android.Build.Utilities;
|
||||
using System.IO.Compression;
|
||||
|
||||
using Xamarin.Tools.Zip;
|
||||
|
||||
#if MSBUILD
|
||||
using Microsoft.Build.Framework;
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Xamarin.Android.Tasks
|
||||
{
|
||||
static class ZipArchiveExtensions
|
||||
{
|
||||
public static void AddEntry (this ZipArchive archive, string entryName, Stream data,
|
||||
CompressionLevel compressionLevel = CompressionLevel.Optimal )
|
||||
{
|
||||
ZipArchiveEntry entry = archive.CreateEntry(entryName, compressionLevel: compressionLevel);
|
||||
using (StreamWriter writer = new StreamWriter(entry.Open()))
|
||||
{
|
||||
data.CopyTo (writer.BaseStream);
|
||||
writer.Flush ();
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddEntry (this ZipArchive archive, string entryName, byte[] data,
|
||||
CompressionLevel compressionLevel = CompressionLevel.Optimal)
|
||||
{
|
||||
ZipArchiveEntry entry = archive.CreateEntry (entryName, compressionLevel: compressionLevel);
|
||||
using (StreamWriter writer = new StreamWriter (entry.Open ())) {
|
||||
writer.BaseStream.Write (data, 0, data.Length);
|
||||
writer.Flush ();
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddEntry (this ZipArchive archive, string entryName, string s, Encoding encoding,
|
||||
CompressionLevel compressionLevel = CompressionLevel.Optimal)
|
||||
{
|
||||
ZipArchiveEntry entry = archive.CreateEntry (entryName, compressionLevel: compressionLevel);
|
||||
using (StreamWriter writer = new StreamWriter (entry.Open ())) {
|
||||
var data = encoding.GetBytes (s);
|
||||
writer.BaseStream.Write (data, 0, data.Length);
|
||||
writer.Flush ();
|
||||
}
|
||||
}
|
||||
|
||||
internal static string ArchiveNameForFile (string filename, string directoryPathInZip)
|
||||
{
|
||||
string pathName;
|
||||
if (string.IsNullOrEmpty (directoryPathInZip)) {
|
||||
pathName = Path.GetFileName (filename);
|
||||
}
|
||||
else {
|
||||
pathName = Path.Combine (directoryPathInZip, Path.GetFileName (filename));
|
||||
}
|
||||
return pathName.Replace ("\\", "/");
|
||||
}
|
||||
|
||||
public static ZipArchiveEntry AddFile (this ZipArchive archive, string fileName, string directoryPathInZip = null, CompressionLevel compressionLevel = CompressionLevel.Optimal )
|
||||
{
|
||||
ZipArchiveEntry entry = archive.CreateEntry(ArchiveNameForFile(fileName, directoryPathInZip), compressionLevel: compressionLevel);
|
||||
using (StreamWriter writer = new StreamWriter(entry.Open()))
|
||||
{
|
||||
var data = File.ReadAllBytes (fileName);
|
||||
writer.BaseStream.Write (data, 0, data.Length);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
public static void AddFiles (this ZipArchive archive, IEnumerable<string> fileNames, string directoryPathInZip)
|
||||
{
|
||||
foreach (var file in fileNames) {
|
||||
AddFile (archive, file, directoryPathInZip);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ContainsEntry (this ZipArchive archive, string entryName)
|
||||
{
|
||||
return archive.Entries.Any (x => string.Compare (x.FullName, entryName, StringComparison.OrdinalIgnoreCase) == 0);
|
||||
}
|
||||
|
||||
public static bool IsDirectory (this ZipArchiveEntry entry)
|
||||
{
|
||||
return entry.FullName.EndsWith ("/", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static void Extract (this ZipArchiveEntry entry, Stream stream)
|
||||
{
|
||||
entry.Open ().CopyTo (stream);
|
||||
}
|
||||
|
||||
public static void Extract (this ZipArchiveEntry entry, string destination)
|
||||
{
|
||||
entry.ExtractToFile (destination, overwrite: true);
|
||||
}
|
||||
|
||||
public static void AddDirectory (this ZipArchive archive, string folder, string folderInArchive)
|
||||
{
|
||||
string root = folderInArchive;
|
||||
foreach(var fileName in Directory.GetFiles (folder)) {
|
||||
archive.AddFile (fileName, root);
|
||||
}
|
||||
foreach (var dir in Directory.GetDirectories (folder)) {
|
||||
var internalDir = dir.Replace ("./", string.Empty).Replace (folder, string.Empty);
|
||||
archive.AddDirectory (dir, folderInArchive + internalDir);
|
||||
}
|
||||
}
|
||||
|
||||
public static string Hash (this ZipArchiveEntry entry)
|
||||
{
|
||||
using (var stream = entry.Open ())
|
||||
using (var sha1 = SHA1.Create ()) {
|
||||
return Convert.ToBase64String (sha1.ComputeHash (stream));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -34,15 +34,13 @@
|
|||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<DefineConstants Condition="'$(__XA_NO_PREVIEW_L_SUPPORT__)' != ''">$(DefineConstants);XA_NO_PREVIEW_L_SUPPORT</DefineConstants>
|
||||
<AndroidGeneratedClassDirectory Condition=" '$(AndroidGeneratedClassDirectory)' == '' " >..\..\src\Mono.Android\obj\$(Configuration)\android-$(AndroidApiLevel)</AndroidGeneratedClassDirectory>
|
||||
<AndroidGeneratedClassDirectory Condition=" '$(AndroidGeneratedClassDirectory)' == '' ">..\..\src\Mono.Android\obj\$(Configuration)\android-$(AndroidApiLevel)</AndroidGeneratedClassDirectory>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Build.Framework" />
|
||||
<Reference Include="Microsoft.Build.Utilities.v4.0" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Mono.Cecil">
|
||||
|
@ -63,8 +61,6 @@
|
|||
<Reference Include="FSharp.Compiler.CodeDom">
|
||||
<HintPath>..\..\packages\FSharp.Compiler.CodeDom.1.0.0.1\lib\net40\FSharp.Compiler.CodeDom.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="$(IntermediateOutputPath)Profile.g.cs" />
|
||||
|
@ -117,7 +113,6 @@
|
|||
<Compile Include="Utilities\JavaResourceParser.cs" />
|
||||
<Compile Include="Utilities\ManifestDocumentElement.cs" />
|
||||
<Compile Include="Utilities\ManifestDocument.cs" />
|
||||
<Compile Include="Utilities\ZipArchiveExtensions.cs" />
|
||||
<Compile Include="Mono.Android\MetaDataAttribute.Partial.cs" />
|
||||
<Compile Include="Utilities\MonoAndroidHelper.cs" />
|
||||
<None Include="Linker\MonoDroid.Tuner\PreserveCode.cs" />
|
||||
|
@ -640,6 +635,10 @@
|
|||
<Name>class-parse</Name>
|
||||
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(LibZipSharpSourceFullPath)\libZipSharp.csproj">
|
||||
<Project>{E248B2CA-303B-4645-ADDC-9D4459D550FD}</Project>
|
||||
<Name>libZipSharp</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="pdb2mdb\" />
|
||||
|
|
|
@ -31,8 +31,6 @@
|
|||
<Reference Include="System" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
|
@ -58,4 +56,4 @@
|
|||
<ItemGroup>
|
||||
<Folder Include="Sdks\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
Загрузка…
Ссылка в новой задаче