diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs index 9786e8a3762..ceb4948aacb 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs @@ -193,20 +193,23 @@ public void WarnAboutAppDomains ([Values (true, false)] bool isRelease) } [Test] - public void RemoveDesigner () + public void RemoveDesigner ([Values (true, false)] bool usesAssemblyBlobs) { var proj = new XamarinAndroidApplicationProject { IsRelease = true, }; proj.SetProperty ("AndroidEnableAssemblyCompression", "False"); proj.SetProperty ("AndroidLinkResources", "True"); + proj.SetProperty ("AndroidUseAssembliesBlob", usesAssemblyBlobs.ToString ()); string assemblyName = proj.ProjectName; using (var b = CreateApkBuilder ()) { Assert.IsTrue (b.Build (proj), "build should have succeeded."); var apk = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, $"{proj.PackageName}-Signed.apk"); FileAssert.Exists (apk); + var helper = new ArchiveAssemblyHelper (apk, usesAssemblyBlobs); + Assert.IsTrue (helper.Exists ($"assemblies/{assemblyName}.dll"), $"{assemblyName}.dll should exist in apk!"); + // TODO: helper must be able to extract assembly from the blob using (var zip = ZipHelper.OpenZip (apk)) { - Assert.IsTrue (zip.ContainsEntry ($"assemblies/{assemblyName}.dll"), $"{assemblyName}.dll should exist in apk!"); var entry = zip.ReadEntry ($"assemblies/{assemblyName}.dll"); using (var stream = new MemoryStream ()) { entry.Extract (stream); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ArchiveAssemblyHelper.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ArchiveAssemblyHelper.cs index 40838f70c47..5bc9e160ac8 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ArchiveAssemblyHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ArchiveAssemblyHelper.cs @@ -9,7 +9,7 @@ namespace Xamarin.Android.Build.Tests { - class ArchiveAssemblyHelper + public class ArchiveAssemblyHelper { public const string DefaultBlobEntryPrefix = "{blob}"; @@ -20,11 +20,20 @@ class ArchiveAssemblyHelper ".mdb", }; + static readonly Dictionary ArchToAbi = new Dictionary (StringComparer.OrdinalIgnoreCase) { + {"x86", "x86"}, + {"x86_64", "x86_64"}, + {"armeabi_v7a", "armeabi-v7a"}, + {"arm64_v8a", "arm64-v8a"}, + }; + readonly string archivePath; readonly string assembliesRootDir; bool useAssemblyBlobs; List archiveContents; + public string ArchivePath => archivePath; + public ArchiveAssemblyHelper (string archivePath, bool useAssemblyBlobs) { if (String.IsNullOrEmpty (archivePath)) { @@ -70,26 +79,35 @@ public List ListArchiveContents (string blobEntryPrefix = DefaultBlobEnt } var explorer = new BlobExplorer (archivePath); - foreach (var kvp in explorer.AssembliesByName) { - string name = kvp.Key; - BlobAssembly asm = kvp.Value; + foreach (var asm in explorer.Assemblies) { + string prefix = blobEntryPrefix; - entries.Add ($"{blobEntryPrefix}{name}.dll"); + if (!String.IsNullOrEmpty (asm.Blob.Arch)) { + string arch = ArchToAbi[asm.Blob.Arch]; + prefix = $"{prefix}{arch}/"; + } + + entries.Add ($"{prefix}{asm.Name}.dll"); if (asm.DebugDataOffset > 0) { - entries.Add ($"{blobEntryPrefix}{name}.pdb"); + entries.Add ($"{prefix}{asm.Name}.pdb"); } if (asm.ConfigDataOffset > 0) { - entries.Add ($"{blobEntryPrefix}{name}.dll.config"); + entries.Add ($"{prefix}{asm.Name}.dll.config"); } } + Console.WriteLine ("Archive entries with synthetised assembly blob entries:"); + foreach (string e in entries) { + Console.WriteLine ($" {e}"); + } + return entries; } - public bool Exists (string entryPath) + public bool Exists (string entryPath, bool forceRefresh = false) { - List contents = ListArchiveContents (assembliesRootDir); + List contents = ListArchiveContents (assembliesRootDir, forceRefresh); if (contents.Count == 0) { return false; } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/AssertionExtensions.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/AssertionExtensions.cs index 740bb880c4e..8552a37a22b 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/AssertionExtensions.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/AssertionExtensions.cs @@ -60,12 +60,24 @@ public static void AssertContainsEntry (this ZipArchive zip, string zipPath, str Assert.IsTrue (zip.ContainsEntry (archivePath), $"{zipPath} should contain {archivePath}"); } + [DebuggerHidden] + public static void AssertContainsEntry (this ArchiveAssemblyHelper helper, string archivePath) + { + Assert.IsTrue (helper.Exists (archivePath), $"{helper.ArchivePath} should contain {archivePath}"); + } + [DebuggerHidden] public static void AssertDoesNotContainEntry (this ZipArchive zip, string zipPath, string archivePath) { Assert.IsFalse (zip.ContainsEntry (archivePath), $"{zipPath} should *not* contain {archivePath}"); } + [DebuggerHidden] + public static void AssertDoesNotContainEntry (this ArchiveAssemblyHelper helper, string archivePath) + { + Assert.IsFalse (helper.Exists (archivePath), $"{helper.ArchivePath} should *not* contain {archivePath}"); + } + [DebuggerHidden] public static void AssertContainsEntry (this ZipArchive zip, string zipPath, string archivePath, bool shouldContainEntry) { @@ -76,6 +88,16 @@ public static void AssertContainsEntry (this ZipArchive zip, string zipPath, str } } + [DebuggerHidden] + public static void AssertContainsEntry (this ArchiveAssemblyHelper helper, string archivePath, bool shouldContainEntry) + { + if (shouldContainEntry) { + helper.AssertContainsEntry (archivePath); + } else { + helper.AssertDoesNotContainEntry (archivePath); + } + } + [DebuggerHidden] public static void AssertEntryContents (this ZipArchive zip, string zipPath, string archivePath, string contents) { diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs index cd6d63d48c8..b81c071dba0 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs @@ -398,63 +398,104 @@ public void DotNetBuildBinding () /* runtimeIdentifiers */ "android-arm", /* isRelease */ false, /* aot */ false, + /* usesAssemblyBlobs */ false, + }, + new object [] { + /* runtimeIdentifiers */ "android-arm", + /* isRelease */ false, + /* aot */ false, + /* usesAssemblyBlobs */ true, }, new object [] { /* runtimeIdentifiers */ "android-arm64", /* isRelease */ false, /* aot */ false, + /* usesAssemblyBlobs */ false, }, new object [] { /* runtimeIdentifiers */ "android-x86", /* isRelease */ false, /* aot */ false, + /* usesAssemblyBlobs */ false, }, new object [] { /* runtimeIdentifiers */ "android-x64", /* isRelease */ false, /* aot */ false, + /* usesAssemblyBlobs */ false, + }, + new object [] { + /* runtimeIdentifiers */ "android-arm", + /* isRelease */ true, + /* aot */ false, + /* usesAssemblyBlobs */ false, }, new object [] { /* runtimeIdentifiers */ "android-arm", /* isRelease */ true, /* aot */ false, + /* usesAssemblyBlobs */ true, }, new object [] { /* runtimeIdentifiers */ "android-arm", /* isRelease */ true, /* aot */ true, + /* usesAssemblyBlobs */ false, + }, + new object [] { + /* runtimeIdentifiers */ "android-arm", + /* isRelease */ true, + /* aot */ true, + /* usesAssemblyBlobs */ true, }, new object [] { /* runtimeIdentifiers */ "android-arm64", /* isRelease */ true, /* aot */ false, + /* usesAssemblyBlobs */ false, }, new object [] { /* runtimeIdentifiers */ "android-arm;android-arm64;android-x86;android-x64", /* isRelease */ false, /* aot */ false, + /* usesAssemblyBlobs */ false, + }, + new object [] { + /* runtimeIdentifiers */ "android-arm;android-arm64;android-x86;android-x64", + /* isRelease */ false, + /* aot */ false, + /* usesAssemblyBlobs */ true, }, new object [] { /* runtimeIdentifiers */ "android-arm;android-arm64;android-x86", /* isRelease */ true, /* aot */ false, + /* usesAssemblyBlobs */ false, }, new object [] { /* runtimeIdentifiers */ "android-arm;android-arm64;android-x86;android-x64", /* isRelease */ true, /* aot */ false, + /* usesAssemblyBlobs */ false, + }, + new object [] { + /* runtimeIdentifiers */ "android-arm;android-arm64;android-x86;android-x64", + /* isRelease */ true, + /* aot */ false, + /* usesAssemblyBlobs */ true, }, new object [] { /* runtimeIdentifiers */ "android-arm;android-arm64;android-x86;android-x64", /* isRelease */ true, /* aot */ true, + /* usesAssemblyBlobs */ false, }, }; [Test] [Category ("SmokeTests")] [TestCaseSource (nameof (DotNetBuildSource))] - public void DotNetBuild (string runtimeIdentifiers, bool isRelease, bool aot) + public void DotNetBuild (string runtimeIdentifiers, bool isRelease, bool aot, bool usesAssemblyBlobs) { var proj = new XASdkProject { IsRelease = isRelease, @@ -489,6 +530,7 @@ public void DotNetBuild (string runtimeIdentifiers, bool isRelease, bool aot) } }; proj.MainActivity = proj.DefaultMainActivity.Replace (": Activity", ": AndroidX.AppCompat.App.AppCompatActivity"); + proj.SetProperty ("AndroidUseAssembliesBlob", usesAssemblyBlobs.ToString ()); if (aot) { proj.SetProperty ("RunAOTCompilation", "true"); } @@ -577,21 +619,22 @@ public void DotNetBuild (string runtimeIdentifiers, bool isRelease, bool aot) bool expectEmbeddedAssembies = !(CommercialBuildAvailable && !isRelease); var apkPath = Path.Combine (outputPath, $"{proj.PackageName}-Signed.apk"); FileAssert.Exists (apkPath); - using (var apk = ZipHelper.OpenZip (apkPath)) { - apk.AssertContainsEntry (apkPath, $"assemblies/{proj.ProjectName}.dll", shouldContainEntry: expectEmbeddedAssembies); - apk.AssertContainsEntry (apkPath, $"assemblies/{proj.ProjectName}.pdb", shouldContainEntry: !CommercialBuildAvailable && !isRelease); - apk.AssertContainsEntry (apkPath, $"assemblies/System.Linq.dll", shouldContainEntry: expectEmbeddedAssembies); - apk.AssertContainsEntry (apkPath, $"assemblies/es/{proj.ProjectName}.resources.dll", shouldContainEntry: expectEmbeddedAssembies); + var helper = new ArchiveAssemblyHelper (apkPath, usesAssemblyBlobs); + helper.AssertContainsEntry ($"assemblies/{proj.ProjectName}.dll", shouldContainEntry: expectEmbeddedAssembies); + helper.AssertContainsEntry ($"assemblies/{proj.ProjectName}.pdb", shouldContainEntry: !CommercialBuildAvailable && !isRelease); + helper.AssertContainsEntry ($"assemblies/System.Linq.dll", shouldContainEntry: expectEmbeddedAssembies); + helper.AssertContainsEntry ($"assemblies/es/{proj.ProjectName}.resources.dll", shouldContainEntry: expectEmbeddedAssembies); +// using (var apk = ZipHelper.OpenZip (apkPath)) { foreach (var abi in rids.Select (AndroidRidAbiHelper.RuntimeIdentifierToAbi)) { - apk.AssertContainsEntry (apkPath, $"lib/{abi}/libmonodroid.so"); - apk.AssertContainsEntry (apkPath, $"lib/{abi}/libmonosgen-2.0.so"); + helper.AssertContainsEntry ($"lib/{abi}/libmonodroid.so"); + helper.AssertContainsEntry ($"lib/{abi}/libmonosgen-2.0.so"); if (rids.Length > 1) { - apk.AssertContainsEntry (apkPath, $"assemblies/{abi}/System.Private.CoreLib.dll", shouldContainEntry: expectEmbeddedAssembies); + helper.AssertContainsEntry ($"assemblies/{abi}/System.Private.CoreLib.dll", shouldContainEntry: expectEmbeddedAssembies); } else { - apk.AssertContainsEntry (apkPath, "assemblies/System.Private.CoreLib.dll", shouldContainEntry: expectEmbeddedAssembies); + helper.AssertContainsEntry ("assemblies/System.Private.CoreLib.dll", shouldContainEntry: expectEmbeddedAssembies); } } - } +// } }