From dc8613c68cf391a1de2dacbff0ae627f493f732c Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Thu, 19 Dec 2024 09:04:12 -1000 Subject: [PATCH 1/2] =?UTF-8?q?=EF=BB=BF[XABT]=20Create=20separate=20compr?= =?UTF-8?q?ess=20assemblies=20task.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Tasks/CollectAssemblyFilesForArchive.cs | 42 ++----- .../Tasks/CompressAssemblies.cs | 107 ++++++++++++++++++ .../Tasks/MavenDownload.cs | 2 +- .../Utilities/ITaskItemExtensions.cs | 4 +- .../Utilities/MonoAndroidHelper.cs | 10 ++ .../Xamarin.Android.Common.targets | 36 +++++- 6 files changed, 160 insertions(+), 41 deletions(-) create mode 100644 src/Xamarin.Android.Build.Tasks/Tasks/CompressAssemblies.cs diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CollectAssemblyFilesForArchive.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CollectAssemblyFilesForArchive.cs index 84d66ef827b..7914e4b7f3e 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/CollectAssemblyFilesForArchive.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/CollectAssemblyFilesForArchive.cs @@ -37,9 +37,6 @@ public class CollectAssemblyFilesForArchive : AndroidTask [Required] public string IntermediateOutputPath { get; set; } = ""; - [Required] - public string ProjectFullPath { get; set; } = ""; - [Required] public ITaskItem [] ResolvedFrameworkAssemblies { get; set; } = []; @@ -58,26 +55,16 @@ public override bool RunTask () { var files = new PackageFileListBuilder (); - DSOWrapperGenerator.Config dsoWrapperConfig = DSOWrapperGenerator.GetConfig (Log, AndroidBinUtilsDirectory, IntermediateOutputPath); - bool compress = !IncludeDebugSymbols && EnableCompression; - IDictionary>? compressedAssembliesInfo = null; + var dsoWrapperConfig = DSOWrapperGenerator.GetConfig (Log, AndroidBinUtilsDirectory, IntermediateOutputPath); - if (compress) { - string key = CompressedAssemblyInfo.GetKey (ProjectFullPath); - Log.LogDebugMessage ($"Retrieving assembly compression info with key '{key}'"); - compressedAssembliesInfo = BuildEngine4.UnregisterTaskObjectAssemblyLocal>> (key, RegisteredTaskObjectLifetime.Build); - if (compressedAssembliesInfo == null) - throw new InvalidOperationException ($"Assembly compression info not found for key '{key}'. Compression will not be performed."); - } - - AddAssemblies (dsoWrapperConfig, files, IncludeDebugSymbols, compress, compressedAssembliesInfo, assemblyStoreApkName: null); + AddAssemblies (dsoWrapperConfig, files, assemblyStoreApkName: null); FilesToAddToArchive = files.ToArray (); return !Log.HasLoggedErrors; } - void AddAssemblies (DSOWrapperGenerator.Config dsoWrapperConfig, PackageFileListBuilder files, bool debug, bool compress, IDictionary>? compressedAssembliesInfo, string? assemblyStoreApkName) + void AddAssemblies (DSOWrapperGenerator.Config dsoWrapperConfig, PackageFileListBuilder files, string? assemblyStoreApkName) { string compressedOutputDir = Path.GetFullPath (Path.Combine (Path.GetDirectoryName (ApkOutputPath), "..", "lz4")); AssemblyStoreBuilder? storeBuilder = null; @@ -87,10 +74,10 @@ void AddAssemblies (DSOWrapperGenerator.Config dsoWrapperConfig, PackageFileList } // Add user assemblies - AssemblyPackagingHelper.AddAssembliesFromCollection (Log, SupportedAbis, ResolvedUserAssemblies, (TaskLoggingHelper log, AndroidTargetArch arch, ITaskItem assembly) => DoAddAssembliesFromArchCollection (log, arch, assembly, dsoWrapperConfig, files, debug, compress, compressedAssembliesInfo, compressedOutputDir, storeBuilder)); + AssemblyPackagingHelper.AddAssembliesFromCollection (Log, SupportedAbis, ResolvedUserAssemblies, (TaskLoggingHelper log, AndroidTargetArch arch, ITaskItem assembly) => DoAddAssembliesFromArchCollection (log, arch, assembly, dsoWrapperConfig, files, storeBuilder)); // Add framework assemblies - AssemblyPackagingHelper.AddAssembliesFromCollection (Log, SupportedAbis, ResolvedFrameworkAssemblies, (TaskLoggingHelper log, AndroidTargetArch arch, ITaskItem assembly) => DoAddAssembliesFromArchCollection (log, arch, assembly, dsoWrapperConfig, files, debug, compress, compressedAssembliesInfo, compressedOutputDir, storeBuilder)); + AssemblyPackagingHelper.AddAssembliesFromCollection (Log, SupportedAbis, ResolvedFrameworkAssemblies, (TaskLoggingHelper log, AndroidTargetArch arch, ITaskItem assembly) => DoAddAssembliesFromArchCollection (log, arch, assembly, dsoWrapperConfig, files, storeBuilder)); if (!UseAssemblyStore) { return; @@ -115,16 +102,17 @@ void AddAssemblies (DSOWrapperGenerator.Config dsoWrapperConfig, PackageFileList } } - void DoAddAssembliesFromArchCollection (TaskLoggingHelper log, AndroidTargetArch arch, ITaskItem assembly, DSOWrapperGenerator.Config dsoWrapperConfig, PackageFileListBuilder files, bool debug, bool compress, IDictionary>? compressedAssembliesInfo, string compressedOutputDir, AssemblyStoreBuilder? storeBuilder) + void DoAddAssembliesFromArchCollection (TaskLoggingHelper log, AndroidTargetArch arch, ITaskItem assembly, DSOWrapperGenerator.Config dsoWrapperConfig, PackageFileListBuilder files, AssemblyStoreBuilder? storeBuilder) { // In the "all assemblies are per-RID" world, assemblies, pdb and config are disguised as shared libraries (that is, // their names end with the .so extension) so that Android allows us to put them in the `lib/{ARCH}` directory. // For this reason, they have to be treated just like other .so files, as far as compression rules are concerned. // Thus, we no longer just store them in the apk but we call the `GetCompressionMethod` method to find out whether // or not we're supposed to compress .so files. - var sourcePath = CompressAssembly (assembly, compress, compressedAssembliesInfo, compressedOutputDir); + var sourcePath = assembly.GetMetadataOrDefault ("CompressedAssembly", assembly.ItemSpec); + if (UseAssemblyStore) { - storeBuilder!.AddAssembly (sourcePath, assembly, includeDebugSymbols: debug); + storeBuilder!.AddAssembly (sourcePath, assembly, includeDebugSymbols: IncludeDebugSymbols); return; } @@ -138,7 +126,7 @@ void DoAddAssembliesFromArchCollection (TaskLoggingHelper log, AndroidTargetArch AddAssemblyConfigEntry (dsoWrapperConfig, files, arch, assemblyDirectory, config); // Try to add symbols if Debug - if (!debug) { + if (!IncludeDebugSymbols) { return; } @@ -207,15 +195,5 @@ void AddAssemblyConfigEntry (DSOWrapperGenerator.Config dsoWrapperConfig, Packag files.AddItem (wrappedConfigFile, inArchivePath); } - string CompressAssembly (ITaskItem assembly, bool compress, IDictionary>? compressedAssembliesInfo, string compressedOutputDir) - { - if (!compress) { - return assembly.ItemSpec; - } - - // NRT: compressedAssembliesInfo is guaranteed to be non-null if compress is true - return AssemblyCompression.Compress (Log, assembly, compressedAssembliesInfo!, compressedOutputDir); - } - static string MakeArchiveLibPath (string abi, string fileName) => MonoAndroidHelper.MakeZipArchivePath (ArchiveLibPath, abi, fileName); } diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CompressAssemblies.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CompressAssemblies.cs new file mode 100644 index 00000000000..ccd6822fa00 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Tasks/CompressAssemblies.cs @@ -0,0 +1,107 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.Android.Build.Tasks; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Xamarin.Android.Tools; + +namespace Xamarin.Android.Tasks; + +/// +/// Compresses assemblies using LZ4 compression before placing them in the APK. +/// Note this is independent of whether they are stored compressed with ZIP in the APK. +/// Our runtime bits will LZ4 decompress them at assembly load time. +/// +public class CompressAssemblies : AndroidTask +{ + public override string TaskPrefix => "CAS"; + + [Required] + public string ApkOutputPath { get; set; } = ""; + + [Required] + public bool EnableCompression { get; set; } + + public bool IncludeDebugSymbols { get; set; } + + [Required] + public string ProjectFullPath { get; set; } = ""; + + [Required] + public ITaskItem [] ResolvedFrameworkAssemblies { get; set; } = []; + + [Required] + public ITaskItem [] ResolvedUserAssemblies { get; set; } = []; + + [Required] + public string [] SupportedAbis { get; set; } = []; + + [Output] + public ITaskItem [] ResolvedFrameworkAssembliesOutput { get; set; } = []; + + [Output] + public ITaskItem [] ResolvedUserAssembliesOutput { get; set; } = []; + + public override bool RunTask () + { + if (IncludeDebugSymbols || !EnableCompression) { + ResolvedFrameworkAssembliesOutput = ResolvedFrameworkAssemblies; + ResolvedUserAssembliesOutput = ResolvedUserAssemblies; + return true; + } + + var compressed_assemblies_info = GetCompressedAssemblyInfo (); + + // Get all the user and framework assemblies we may need to compresss + var assemblies = ResolvedFrameworkAssemblies.Concat (ResolvedUserAssemblies).Where (asm => !(ShouldSkipAssembly (asm))).ToArray (); + var per_arch_assemblies = MonoAndroidHelper.GetPerArchAssemblies (assemblies, SupportedAbis, true); + var compressed_output_dir = Path.GetFullPath (Path.Combine (Path.GetDirectoryName (ApkOutputPath), "..", "lz4")); + + foreach (var kvp in per_arch_assemblies) { + Log.LogDebugMessage ($"Compressing assemblies for architecture '{kvp.Key}'"); + + foreach (var asm in kvp.Value.Values) { + MonoAndroidHelper.LogIfReferenceAssembly (asm, Log); + + var compressed_assembly = AssemblyCompression.Compress (Log, asm, compressed_assemblies_info, compressed_output_dir); + + if (compressed_assembly.HasValue ()) { + Log.LogDebugMessage ($"Compressed '{asm.ItemSpec}' to '{compressed_assembly}'."); + asm.SetMetadata ("CompressedAssembly", compressed_assembly); + } + } + } + + ResolvedFrameworkAssembliesOutput = ResolvedFrameworkAssemblies; + ResolvedUserAssembliesOutput = ResolvedUserAssemblies; + + return !Log.HasLoggedErrors; + } + + IDictionary> GetCompressedAssemblyInfo () + { + var key = CompressedAssemblyInfo.GetKey (ProjectFullPath); + Log.LogDebugMessage ($"Retrieving assembly compression info with key '{key}'"); + + var compressedAssembliesInfo = BuildEngine4.UnregisterTaskObjectAssemblyLocal>> (key, RegisteredTaskObjectLifetime.Build); + + if (compressedAssembliesInfo is null) + throw new InvalidOperationException ($"Assembly compression info not found for key '{key}'. Compression will not be performed."); + + return compressedAssembliesInfo; + } + + bool ShouldSkipAssembly (ITaskItem asm) + { + var should_skip = asm.GetMetadataOrDefault ("AndroidSkipAddToPackage", false); + + if (should_skip) + Log.LogDebugMessage ($"Skipping {asm.ItemSpec} due to 'AndroidSkipAddToPackage' == 'true' "); + + return should_skip; + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/MavenDownload.cs b/src/Xamarin.Android.Build.Tasks/Tasks/MavenDownload.cs index c746d26c5a1..924f5d14a32 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/MavenDownload.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/MavenDownload.cs @@ -88,7 +88,7 @@ public async override System.Threading.Tasks.Task RunTaskAsync () return null; // Allow user to override the Maven filename of the artifact - var maven_override_filename = item.GetMetadataOrDefault ("ArtifactFilename", null); + var maven_override_filename = item.GetMetadataOrDefault ("ArtifactFilename", null); // Download artifact var artifact_file = await MavenExtensions.DownloadPayload (repository, artifact, MavenCacheDirectory, maven_override_filename, Log, CancellationToken); diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ITaskItemExtensions.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ITaskItemExtensions.cs index a04c8203548..40521623cfa 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ITaskItemExtensions.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ITaskItemExtensions.cs @@ -25,14 +25,14 @@ public static IEnumerable ToXElements (this ICollection ite } [return: NotNullIfNotNull (nameof (defaultValue))] - public static string? GetMetadataOrDefault (this ITaskItem item, string name, string? defaultValue) + public static T? GetMetadataOrDefault (this ITaskItem item, string name, T? defaultValue) { var value = item.GetMetadata (name); if (string.IsNullOrWhiteSpace (value)) return defaultValue; - return value; + return (T?)Convert.ChangeType (value, typeof (T)); } public static string? GetRequiredMetadata (this ITaskItem item, string itemName, string name, TaskLoggingHelper log) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs index b4daa6d8bc0..a261e923baf 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs @@ -380,6 +380,16 @@ public static bool IsReferenceAssembly (string assembly, TaskLoggingHelper log) } } + public static bool LogIfReferenceAssembly (ITaskItem assembly, TaskLoggingHelper log) + { + if (IsReferenceAssembly (assembly.ItemSpec, log)) { + log.LogCodedWarning ("XA0107", assembly.ItemSpec, 0, Properties.Resources.XA0107, assembly.ItemSpec); + return true; + } + + return false; + } + public static bool IsForceRetainedAssembly (string assembly) { switch (assembly) { diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index ba56eff5b92..fb68fbb8810 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -48,16 +48,17 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. + - - - + + + @@ -2097,6 +2098,18 @@ because xbuild doesn't support framework reference assemblies. also need to have the args added to Xamarin.Android.Common.Debugging.targets in monodroid. --> + + + + + @@ -2120,9 +2133,8 @@ because xbuild doesn't support framework reference assemblies. EnableCompression="$(AndroidEnableAssemblyCompression)" IncludeDebugSymbols="$(AndroidIncludeDebugSymbols)" IntermediateOutputPath="$(IntermediateOutputPath)" - ProjectFullPath="$(MSBuildProjectFullPath)" - ResolvedFrameworkAssemblies="@(_ShrunkFrameworkAssemblies)" - ResolvedUserAssemblies="@(_ShrunkUserAssemblies);@(_AndroidResolvedSatellitePaths)" + ResolvedFrameworkAssemblies="@(_BuildApkResolvedFrameworkAssemblies)" + ResolvedUserAssemblies="@(_BuildApkResolvedUserAssemblies)" SupportedAbis="@(_BuildTargetAbis)" UseAssemblyStore="$(AndroidUseAssemblyStore)"> @@ -2224,6 +2236,18 @@ because xbuild doesn't support framework reference assemblies. + + + + + From ea10b6f0e0ad6e1a63d60b9b9c26f19bc7c2575b Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Tue, 14 Jan 2025 11:34:17 -1000 Subject: [PATCH 2/2] Create separate 'CreateAssemblyStore' and 'WrapAssembliesAsSharedLibraries' tasks. --- .../Tasks/CompressAssemblies.cs | 5 +- .../Tasks/CreateAssemblyStore.cs | 86 ++++++++++++++ ....cs => WrapAssembliesAsSharedLibraries.cs} | 112 +++++++----------- .../Xamarin.Android.Common.targets | 39 +++--- 4 files changed, 153 insertions(+), 89 deletions(-) create mode 100644 src/Xamarin.Android.Build.Tasks/Tasks/CreateAssemblyStore.cs rename src/Xamarin.Android.Build.Tasks/Tasks/{CollectAssemblyFilesForArchive.cs => WrapAssembliesAsSharedLibraries.cs} (50%) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CompressAssemblies.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CompressAssemblies.cs index ccd6822fa00..e611e92186c 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/CompressAssemblies.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/CompressAssemblies.cs @@ -6,7 +6,6 @@ using System.Linq; using Microsoft.Android.Build.Tasks; using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; using Xamarin.Android.Tools; namespace Xamarin.Android.Tasks; @@ -23,6 +22,8 @@ public class CompressAssemblies : AndroidTask [Required] public string ApkOutputPath { get; set; } = ""; + public bool EmbedAssemblies { get; set; } + [Required] public bool EnableCompression { get; set; } @@ -48,7 +49,7 @@ public class CompressAssemblies : AndroidTask public override bool RunTask () { - if (IncludeDebugSymbols || !EnableCompression) { + if (IncludeDebugSymbols || !EnableCompression || !EmbedAssemblies) { ResolvedFrameworkAssembliesOutput = ResolvedFrameworkAssemblies; ResolvedUserAssembliesOutput = ResolvedUserAssemblies; return true; diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CreateAssemblyStore.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CreateAssemblyStore.cs new file mode 100644 index 00000000000..b4f913beb84 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Tasks/CreateAssemblyStore.cs @@ -0,0 +1,86 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Android.Build.Tasks; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Xamarin.Android.Tasks; + +/// +/// If using $(AndroidUseAssemblyStore), place all the assemblies in a single .blob file. +/// +public class CreateAssemblyStore : AndroidTask +{ + public override string TaskPrefix => "CST"; + + [Required] + public string AppSharedLibrariesDir { get; set; } = ""; + + public bool IncludeDebugSymbols { get; set; } + + [Required] + public ITaskItem [] ResolvedFrameworkAssemblies { get; set; } = []; + + [Required] + public ITaskItem [] ResolvedUserAssemblies { get; set; } = []; + + [Required] + public string [] SupportedAbis { get; set; } = []; + + public bool UseAssemblyStore { get; set; } + + [Output] + public ITaskItem [] AssembliesToAddToArchive { get; set; } = []; + + public override bool RunTask () + { + // Get all the user and framework assemblies we may need to package + var assemblies = ResolvedFrameworkAssemblies.Concat (ResolvedUserAssemblies).Where (asm => !(ShouldSkipAssembly (asm))).ToArray (); + + if (!UseAssemblyStore) { + AssembliesToAddToArchive = assemblies; + return !Log.HasLoggedErrors; + } + + var store_builder = new AssemblyStoreBuilder (Log); + var per_arch_assemblies = MonoAndroidHelper.GetPerArchAssemblies (assemblies, SupportedAbis, true); + + foreach (var kvp in per_arch_assemblies) { + Log.LogDebugMessage ($"Adding assemblies for architecture '{kvp.Key}'"); + + foreach (var assembly in kvp.Value.Values) { + var sourcePath = assembly.GetMetadataOrDefault ("CompressedAssembly", assembly.ItemSpec); + store_builder.AddAssembly (sourcePath, assembly, includeDebugSymbols: IncludeDebugSymbols); + + Log.LogDebugMessage ($"Added '{sourcePath}' to assembly store."); + } + } + + var assembly_store_paths = store_builder.Generate (AppSharedLibrariesDir); + + if (assembly_store_paths.Count == 0) { + throw new InvalidOperationException ("Assembly store generator did not generate any stores"); + } + + if (assembly_store_paths.Count != SupportedAbis.Length) { + throw new InvalidOperationException ("Internal error: assembly store did not generate store for each supported ABI"); + } + + AssembliesToAddToArchive = assembly_store_paths.Select (kvp => new TaskItem (kvp.Value, new Dictionary { { "Abi", MonoAndroidHelper.ArchToAbi (kvp.Key) } })).ToArray (); + + return !Log.HasLoggedErrors; + } + + bool ShouldSkipAssembly (ITaskItem asm) + { + var should_skip = asm.GetMetadataOrDefault ("AndroidSkipAddToPackage", false); + + if (should_skip) + Log.LogDebugMessage ($"Skipping {asm.ItemSpec} due to 'AndroidSkipAddToPackage' == 'true' "); + + return should_skip; + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CollectAssemblyFilesForArchive.cs b/src/Xamarin.Android.Build.Tasks/Tasks/WrapAssembliesAsSharedLibraries.cs similarity index 50% rename from src/Xamarin.Android.Build.Tasks/Tasks/CollectAssemblyFilesForArchive.cs rename to src/Xamarin.Android.Build.Tasks/Tasks/WrapAssembliesAsSharedLibraries.cs index 7914e4b7f3e..ab297dd6d29 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/CollectAssemblyFilesForArchive.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/WrapAssembliesAsSharedLibraries.cs @@ -11,98 +11,69 @@ namespace Xamarin.Android.Tasks; /// -/// Collects managed assemblies to be added to the final archive. +/// In the "all assemblies are per-RID" world, assembly stores, assemblies, pdb and config are disguised as shared libraries (that is, +/// their names end with the .so extension) so that Android allows us to put them in the `lib/{ARCH}` directory. /// -public class CollectAssemblyFilesForArchive : AndroidTask +public class WrapAssembliesAsSharedLibraries : AndroidTask { const string ArchiveAssembliesPath = "lib"; const string ArchiveLibPath = "lib"; - public override string TaskPrefix => "CAF"; + public override string TaskPrefix => "WAS"; [Required] public string AndroidBinUtilsDirectory { get; set; } = ""; - [Required] - public string ApkOutputPath { get; set; } = ""; - - [Required] - public string AppSharedLibrariesDir { get; set; } = ""; - - [Required] - public bool EnableCompression { get; set; } - public bool IncludeDebugSymbols { get; set; } [Required] public string IntermediateOutputPath { get; set; } = ""; - [Required] - public ITaskItem [] ResolvedFrameworkAssemblies { get; set; } = []; + public bool UseAssemblyStore { get; set; } [Required] - public ITaskItem [] ResolvedUserAssemblies { get; set; } = []; + public ITaskItem [] ResolvedAssemblies { get; set; } = []; [Required] public string [] SupportedAbis { get; set; } = []; - public bool UseAssemblyStore { get; set; } - [Output] - public ITaskItem [] FilesToAddToArchive { get; set; } = []; + public ITaskItem [] WrappedAssemblies { get; set; } = []; public override bool RunTask () { + var wrapper_config = DSOWrapperGenerator.GetConfig (Log, AndroidBinUtilsDirectory, IntermediateOutputPath); var files = new PackageFileListBuilder (); - var dsoWrapperConfig = DSOWrapperGenerator.GetConfig (Log, AndroidBinUtilsDirectory, IntermediateOutputPath); + if (UseAssemblyStore) + WrapAssemblyStores (wrapper_config, files); + else + AssemblyPackagingHelper.AddAssembliesFromCollection (Log, SupportedAbis, ResolvedAssemblies, (TaskLoggingHelper log, AndroidTargetArch arch, ITaskItem assembly) => WrapAssembly (log, arch, assembly, wrapper_config, files)); - AddAssemblies (dsoWrapperConfig, files, assemblyStoreApkName: null); - - FilesToAddToArchive = files.ToArray (); + WrappedAssemblies = files.ToArray (); return !Log.HasLoggedErrors; } - void AddAssemblies (DSOWrapperGenerator.Config dsoWrapperConfig, PackageFileListBuilder files, string? assemblyStoreApkName) + void WrapAssemblyStores (DSOWrapperGenerator.Config dsoWrapperConfig, PackageFileListBuilder files) { - string compressedOutputDir = Path.GetFullPath (Path.Combine (Path.GetDirectoryName (ApkOutputPath), "..", "lz4")); - AssemblyStoreBuilder? storeBuilder = null; - - if (UseAssemblyStore) { - storeBuilder = new AssemblyStoreBuilder (Log); - } - - // Add user assemblies - AssemblyPackagingHelper.AddAssembliesFromCollection (Log, SupportedAbis, ResolvedUserAssemblies, (TaskLoggingHelper log, AndroidTargetArch arch, ITaskItem assembly) => DoAddAssembliesFromArchCollection (log, arch, assembly, dsoWrapperConfig, files, storeBuilder)); - - // Add framework assemblies - AssemblyPackagingHelper.AddAssembliesFromCollection (Log, SupportedAbis, ResolvedFrameworkAssemblies, (TaskLoggingHelper log, AndroidTargetArch arch, ITaskItem assembly) => DoAddAssembliesFromArchCollection (log, arch, assembly, dsoWrapperConfig, files, storeBuilder)); + foreach (var store in ResolvedAssemblies) { + var store_path = store.ItemSpec; + var abi = store.GetRequiredMetadata ("ResolvedAssemblies", "Abi", Log); - if (!UseAssemblyStore) { - return; - } - - Dictionary assemblyStorePaths = storeBuilder!.Generate (AppSharedLibrariesDir); - - if (assemblyStorePaths.Count == 0) { - throw new InvalidOperationException ("Assembly store generator did not generate any stores"); - } + // An error will already have been logged in GetRequiredMetadata + if (abi is null) + return; - if (assemblyStorePaths.Count != SupportedAbis.Length) { - throw new InvalidOperationException ("Internal error: assembly store did not generate store for each supported ABI"); - } + var arch = MonoAndroidHelper.AbiToTargetArch (abi); + var archive_path = MakeArchiveLibPath (abi, "lib" + Path.GetFileName (store_path)); + var wrapped_source_path = DSOWrapperGenerator.WrapIt (Log, dsoWrapperConfig, arch, store_path, Path.GetFileName (archive_path)); - string inArchivePath; - foreach (var kvp in assemblyStorePaths) { - string abi = MonoAndroidHelper.ArchToAbi (kvp.Key); - inArchivePath = MakeArchiveLibPath (abi, "lib" + Path.GetFileName (kvp.Value)); - string wrappedSourcePath = DSOWrapperGenerator.WrapIt (Log, dsoWrapperConfig, kvp.Key, kvp.Value, Path.GetFileName (inArchivePath)); - files.AddItem (wrappedSourcePath, inArchivePath); + files.AddItem (wrapped_source_path, archive_path); } } - void DoAddAssembliesFromArchCollection (TaskLoggingHelper log, AndroidTargetArch arch, ITaskItem assembly, DSOWrapperGenerator.Config dsoWrapperConfig, PackageFileListBuilder files, AssemblyStoreBuilder? storeBuilder) + void WrapAssembly (TaskLoggingHelper log, AndroidTargetArch arch, ITaskItem assembly, DSOWrapperGenerator.Config dsoWrapperConfig, PackageFileListBuilder files) { // In the "all assemblies are per-RID" world, assemblies, pdb and config are disguised as shared libraries (that is, // their names end with the .so extension) so that Android allows us to put them in the `lib/{ARCH}` directory. @@ -111,14 +82,9 @@ void DoAddAssembliesFromArchCollection (TaskLoggingHelper log, AndroidTargetArch // or not we're supposed to compress .so files. var sourcePath = assembly.GetMetadataOrDefault ("CompressedAssembly", assembly.ItemSpec); - if (UseAssemblyStore) { - storeBuilder!.AddAssembly (sourcePath, assembly, includeDebugSymbols: IncludeDebugSymbols); - return; - } - // Add assembly - (string assemblyPath, string assemblyDirectory) = GetInArchiveAssemblyPath (assembly); - string wrappedSourcePath = DSOWrapperGenerator.WrapIt (Log, dsoWrapperConfig, arch, sourcePath, Path.GetFileName (assemblyPath)); + (var assemblyPath, var assemblyDirectory) = GetInArchiveAssemblyPath (assembly); + var wrappedSourcePath = DSOWrapperGenerator.WrapIt (log, dsoWrapperConfig, arch, sourcePath, Path.GetFileName (assemblyPath)); files.AddItem (wrappedSourcePath, assemblyPath); // Try to add config if exists @@ -130,16 +96,18 @@ void DoAddAssembliesFromArchCollection (TaskLoggingHelper log, AndroidTargetArch return; } - string symbols = Path.ChangeExtension (assembly.ItemSpec, "pdb"); + var symbols = Path.ChangeExtension (assembly.ItemSpec, "pdb"); if (!File.Exists (symbols)) { return; } - string archiveSymbolsPath = assemblyDirectory + MonoAndroidHelper.MakeDiscreteAssembliesEntryName (Path.GetFileName (symbols)); - string wrappedSymbolsPath = DSOWrapperGenerator.WrapIt (Log, dsoWrapperConfig, arch, symbols, Path.GetFileName (archiveSymbolsPath)); + var archiveSymbolsPath = assemblyDirectory + MonoAndroidHelper.MakeDiscreteAssembliesEntryName (Path.GetFileName (symbols)); + var wrappedSymbolsPath = DSOWrapperGenerator.WrapIt (log, dsoWrapperConfig, arch, symbols, Path.GetFileName (archiveSymbolsPath)); files.AddItem (wrappedSymbolsPath, archiveSymbolsPath); } + static string MakeArchiveLibPath (string abi, string fileName) => MonoAndroidHelper.MakeZipArchivePath (ArchiveLibPath, abi, fileName); + /// /// Returns the in-archive path for an assembly /// @@ -148,12 +116,13 @@ void DoAddAssembliesFromArchCollection (TaskLoggingHelper log, AndroidTargetArch var parts = new List (); // The PrepareSatelliteAssemblies task takes care of properly setting `DestinationSubDirectory`, so we can just use it here. - string? subDirectory = assembly.GetMetadata ("DestinationSubDirectory")?.Replace ('\\', '/'); + var subDirectory = assembly.GetMetadata ("DestinationSubDirectory")?.Replace ('\\', '/'); + if (string.IsNullOrEmpty (subDirectory)) { throw new InvalidOperationException ($"Internal error: assembly '{assembly}' lacks the required `DestinationSubDirectory` metadata"); } - string assemblyName = Path.GetFileName (assembly.ItemSpec); + var assemblyName = Path.GetFileName (assembly.ItemSpec); // For discrete assembly entries we need to treat assemblies specially. // All of the assemblies have their names mangled so that the possibility to clash with "real" shared // library names is minimized. All of the assembly entries will start with a special character: @@ -166,7 +135,8 @@ void DoAddAssembliesFromArchCollection (TaskLoggingHelper log, AndroidTargetArch // so that it forms a `-culture-` assembly file name prefix, not a `culture/` subdirectory. // This is necessary because Android doesn't allow subdirectories in `lib/{ABI}/` // - string [] subdirParts = subDirectory!.TrimEnd ('/').Split ('/'); + var subdirParts = subDirectory!.TrimEnd ('/').Split ('/'); + if (subdirParts.Length == 1) { // Not a satellite assembly parts.Add (subDirectory); @@ -178,22 +148,20 @@ void DoAddAssembliesFromArchCollection (TaskLoggingHelper log, AndroidTargetArch throw new InvalidOperationException ($"Internal error: '{assembly}' `DestinationSubDirectory` metadata has too many components ({parts.Count} instead of 1 or 2)"); } - string assemblyFilePath = MonoAndroidHelper.MakeZipArchivePath (ArchiveAssembliesPath, parts); + var assemblyFilePath = MonoAndroidHelper.MakeZipArchivePath (ArchiveAssembliesPath, parts); return (assemblyFilePath, Path.GetDirectoryName (assemblyFilePath) + "/"); } void AddAssemblyConfigEntry (DSOWrapperGenerator.Config dsoWrapperConfig, PackageFileListBuilder files, AndroidTargetArch arch, string assemblyPath, string configFile) { - string inArchivePath = MonoAndroidHelper.MakeDiscreteAssembliesEntryName (assemblyPath + Path.GetFileName (configFile)); + var inArchivePath = MonoAndroidHelper.MakeDiscreteAssembliesEntryName (assemblyPath + Path.GetFileName (configFile)); if (!File.Exists (configFile)) { return; } - string wrappedConfigFile = DSOWrapperGenerator.WrapIt (Log, dsoWrapperConfig, arch, configFile, Path.GetFileName (inArchivePath)); + var wrappedConfigFile = DSOWrapperGenerator.WrapIt (Log, dsoWrapperConfig, arch, configFile, Path.GetFileName (inArchivePath)); files.AddItem (wrappedConfigFile, inArchivePath); } - - static string MakeArchiveLibPath (string abi, string fileName) => MonoAndroidHelper.MakeZipArchivePath (ArchiveLibPath, abi, fileName); } diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index fb68fbb8810..3f2a00f4317 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -40,7 +40,6 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. - @@ -55,6 +54,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. + @@ -97,6 +97,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. + @@ -2100,6 +2101,7 @@ because xbuild doesn't support framework reference assemblies. --> + + + + + + + + @@ -2125,20 +2147,6 @@ because xbuild doesn't support framework reference assemblies. LibraryProjectJars="@(ExtractedJarImports)"> - - - -