Skip to content

Commit 7bc14b9

Browse files
authored
Move all of assembly store building code to a separate class (#9382)
Simplify the task of constructing assembly stores by moving all the code which actually stashes assemblies (and their config and debug files, if found and required) in the assembly store to a separate utility class, `AssemblyStoreBuilder`. Additionally, move a portion of assembly packaging code from `BuildApk` to a helper class. This makes it easier to build the stores outside of the `BuildApk` task and further simplifies `BuildApk` code.
1 parent ac8bf22 commit 7bc14b9

File tree

3 files changed

+144
-107
lines changed

3 files changed

+144
-107
lines changed

src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs

Lines changed: 54 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -425,27 +425,23 @@ void AddAssemblies (ZipArchiveEx apk, bool debug, bool compress, IDictionary<And
425425
string sourcePath;
426426
AssemblyCompression.AssemblyData compressedAssembly = null;
427427
string compressedOutputDir = Path.GetFullPath (Path.Combine (Path.GetDirectoryName (ApkOutputPath), "..", "lz4"));
428-
AssemblyStoreGenerator? storeGenerator;
428+
AssemblyStoreBuilder? storeBuilder = null;
429429

430430
if (UseAssemblyStore) {
431-
storeGenerator = new AssemblyStoreGenerator (Log);
432-
} else {
433-
storeGenerator = null;
431+
storeBuilder = new AssemblyStoreBuilder (Log);
434432
}
435433

436-
AssemblyStoreAssemblyInfo? storeAssemblyInfo = null;
437-
438434
// Add user assemblies
439-
AddAssembliesFromCollection (ResolvedUserAssemblies);
435+
AssemblyPackagingHelper.AddAssembliesFromCollection (Log, SupportedAbis, ResolvedUserAssemblies, DoAddAssembliesFromArchCollection);
440436

441437
// Add framework assemblies
442-
AddAssembliesFromCollection (ResolvedFrameworkAssemblies);
438+
AssemblyPackagingHelper.AddAssembliesFromCollection (Log, SupportedAbis, ResolvedFrameworkAssemblies, DoAddAssembliesFromArchCollection);
443439

444440
if (!UseAssemblyStore) {
445441
return;
446442
}
447443

448-
Dictionary<AndroidTargetArch, string> assemblyStorePaths = storeGenerator.Generate (AppSharedLibrariesDir);
444+
Dictionary<AndroidTargetArch, string> assemblyStorePaths = storeBuilder.Generate (AppSharedLibrariesDir);
449445

450446
if (assemblyStorePaths.Count == 0) {
451447
throw new InvalidOperationException ("Assembly store generator did not generate any stores");
@@ -463,90 +459,46 @@ void AddAssemblies (ZipArchiveEx apk, bool debug, bool compress, IDictionary<And
463459
AddFileToArchiveIfNewer (apk, wrappedSourcePath, inArchivePath, GetCompressionMethod (inArchivePath));
464460
}
465461

466-
void AddAssembliesFromCollection (ITaskItem[] assemblies)
467-
{
468-
Dictionary<AndroidTargetArch, Dictionary<string, ITaskItem>> perArchAssemblies = MonoAndroidHelper.GetPerArchAssemblies (
469-
assemblies,
470-
SupportedAbis,
471-
validate: true,
472-
shouldSkip: (ITaskItem asm) => {
473-
if (bool.TryParse (asm.GetMetadata ("AndroidSkipAddToPackage"), out bool value) && value) {
474-
Log.LogDebugMessage ($"Skipping {asm.ItemSpec} due to 'AndroidSkipAddToPackage' == 'true' ");
475-
return true;
476-
}
477-
478-
return false;
479-
}
480-
);
481-
482-
foreach (var kvp in perArchAssemblies) {
483-
Log.LogDebugMessage ($"Adding assemblies for architecture '{kvp.Key}'");
484-
DoAddAssembliesFromArchCollection (kvp.Key, kvp.Value);
485-
}
486-
}
487-
488-
void DoAddAssembliesFromArchCollection (AndroidTargetArch arch, Dictionary<string, ITaskItem> assemblies)
462+
void DoAddAssembliesFromArchCollection (TaskLoggingHelper log, AndroidTargetArch arch, ITaskItem assembly)
489463
{
490464
// In the "all assemblies are per-RID" world, assemblies, pdb and config are disguised as shared libraries (that is,
491465
// their names end with the .so extension) so that Android allows us to put them in the `lib/{ARCH}` directory.
492466
// For this reason, they have to be treated just like other .so files, as far as compression rules are concerned.
493467
// Thus, we no longer just store them in the apk but we call the `GetCompressionMethod` method to find out whether
494468
// or not we're supposed to compress .so files.
495-
foreach (ITaskItem assembly in assemblies.Values) {
496-
if (MonoAndroidHelper.IsReferenceAssembly (assembly.ItemSpec, Log)) {
497-
Log.LogCodedWarning ("XA0107", assembly.ItemSpec, 0, Properties.Resources.XA0107, assembly.ItemSpec);
498-
}
499-
500-
sourcePath = CompressAssembly (assembly);
501-
502-
// Add assembly
503-
(string assemblyPath, string assemblyDirectory) = GetInArchiveAssemblyPath (assembly);
504-
if (UseAssemblyStore) {
505-
storeAssemblyInfo = new AssemblyStoreAssemblyInfo (sourcePath, assembly);
506-
} else {
507-
string wrappedSourcePath = DSOWrapperGenerator.WrapIt (arch, sourcePath, Path.GetFileName (assemblyPath), this);
508-
AddFileToArchiveIfNewer (apk, wrappedSourcePath, assemblyPath, compressionMethod: GetCompressionMethod (assemblyPath));
509-
}
510-
511-
// Try to add config if exists
512-
var config = Path.ChangeExtension (assembly.ItemSpec, "dll.config");
513-
if (UseAssemblyStore) {
514-
if (File.Exists (config)) {
515-
storeAssemblyInfo.ConfigFile = new FileInfo (config);
516-
}
517-
} else {
518-
AddAssemblyConfigEntry (apk, arch, assemblyDirectory, config);
519-
}
469+
sourcePath = CompressAssembly (assembly);
470+
if (UseAssemblyStore) {
471+
storeBuilder.AddAssembly (sourcePath, assembly, includeDebugSymbols: debug);
472+
return;
473+
}
520474

521-
// Try to add symbols if Debug
522-
if (debug) {
523-
var symbols = Path.ChangeExtension (assembly.ItemSpec, "pdb");
524-
string? symbolsPath = null;
475+
// Add assembly
476+
(string assemblyPath, string assemblyDirectory) = GetInArchiveAssemblyPath (assembly);
477+
string wrappedSourcePath = DSOWrapperGenerator.WrapIt (arch, sourcePath, Path.GetFileName (assemblyPath), this);
478+
AddFileToArchiveIfNewer (apk, wrappedSourcePath, assemblyPath, compressionMethod: GetCompressionMethod (assemblyPath));
525479

526-
if (File.Exists (symbols)) {
527-
symbolsPath = symbols;
528-
}
480+
// Try to add config if exists
481+
var config = Path.ChangeExtension (assembly.ItemSpec, "dll.config");
482+
AddAssemblyConfigEntry (apk, arch, assemblyDirectory, config);
529483

530-
if (!String.IsNullOrEmpty (symbolsPath)) {
531-
if (UseAssemblyStore) {
532-
storeAssemblyInfo.SymbolsFile = new FileInfo (symbolsPath);
533-
} else {
534-
string archiveSymbolsPath = assemblyDirectory + MonoAndroidHelper.MakeDiscreteAssembliesEntryName (Path.GetFileName (symbols));
535-
string wrappedSymbolsPath = DSOWrapperGenerator.WrapIt (arch, symbolsPath, Path.GetFileName (archiveSymbolsPath), this);
536-
AddFileToArchiveIfNewer (
537-
apk,
538-
wrappedSymbolsPath,
539-
archiveSymbolsPath,
540-
compressionMethod: GetCompressionMethod (archiveSymbolsPath)
541-
);
542-
}
543-
}
544-
}
484+
// Try to add symbols if Debug
485+
if (!debug) {
486+
return;
487+
}
545488

546-
if (UseAssemblyStore) {
547-
storeGenerator.Add (storeAssemblyInfo);
548-
}
489+
string symbols = Path.ChangeExtension (assembly.ItemSpec, "pdb");
490+
if (!File.Exists (symbols)) {
491+
return;
549492
}
493+
494+
string archiveSymbolsPath = assemblyDirectory + MonoAndroidHelper.MakeDiscreteAssembliesEntryName (Path.GetFileName (symbols));
495+
string wrappedSymbolsPath = DSOWrapperGenerator.WrapIt (arch, symbols, Path.GetFileName (archiveSymbolsPath), this);
496+
AddFileToArchiveIfNewer (
497+
apk,
498+
wrappedSymbolsPath,
499+
archiveSymbolsPath,
500+
compressionMethod: GetCompressionMethod (archiveSymbolsPath)
501+
);
550502
}
551503

552504
void EnsureCompressedAssemblyData (string sourcePath, uint descriptorIndex)
@@ -655,33 +607,28 @@ void AddAssemblyConfigEntry (ZipArchiveEx apk, AndroidTargetArch arch, string as
655607
}
656608

657609
string assemblyName = Path.GetFileName (assembly.ItemSpec);
658-
if (UseAssemblyStore) {
610+
// For discrete assembly entries we need to treat assemblies specially.
611+
// All of the assemblies have their names mangled so that the possibility to clash with "real" shared
612+
// library names is minimized. All of the assembly entries will start with a special character:
613+
//
614+
// `_` - for regular assemblies (e.g. `_Mono.Android.dll.so`)
615+
// `-` - for satellite assemblies (e.g. `-es-Mono.Android.dll.so`)
616+
//
617+
// Second of all, we need to treat satellite assemblies with even more care.
618+
// If we encounter one of them, we will return the culture as part of the path transformed
619+
// so that it forms a `-culture-` assembly file name prefix, not a `culture/` subdirectory.
620+
// This is necessary because Android doesn't allow subdirectories in `lib/{ABI}/`
621+
//
622+
string[] subdirParts = subDirectory.TrimEnd ('/').Split ('/');
623+
if (subdirParts.Length == 1) {
624+
// Not a satellite assembly
659625
parts.Add (subDirectory);
660-
parts.Add (assemblyName);
626+
parts.Add (MonoAndroidHelper.MakeDiscreteAssembliesEntryName (assemblyName));
627+
} else if (subdirParts.Length == 2) {
628+
parts.Add (subdirParts[0]);
629+
parts.Add (MonoAndroidHelper.MakeDiscreteAssembliesEntryName (assemblyName, subdirParts[1]));
661630
} else {
662-
// For discrete assembly entries we need to treat assemblies specially.
663-
// All of the assemblies have their names mangled so that the possibility to clash with "real" shared
664-
// library names is minimized. All of the assembly entries will start with a special character:
665-
//
666-
// `_` - for regular assemblies (e.g. `_Mono.Android.dll.so`)
667-
// `-` - for satellite assemblies (e.g. `-es-Mono.Android.dll.so`)
668-
//
669-
// Second of all, we need to treat satellite assemblies with even more care.
670-
// If we encounter one of them, we will return the culture as part of the path transformed
671-
// so that it forms a `-culture-` assembly file name prefix, not a `culture/` subdirectory.
672-
// This is necessary because Android doesn't allow subdirectories in `lib/{ABI}/`
673-
//
674-
string[] subdirParts = subDirectory.TrimEnd ('/').Split ('/');
675-
if (subdirParts.Length == 1) {
676-
// Not a satellite assembly
677-
parts.Add (subDirectory);
678-
parts.Add (MonoAndroidHelper.MakeDiscreteAssembliesEntryName (assemblyName));
679-
} else if (subdirParts.Length == 2) {
680-
parts.Add (subdirParts[0]);
681-
parts.Add (MonoAndroidHelper.MakeDiscreteAssembliesEntryName (assemblyName, subdirParts[1]));
682-
} else {
683-
throw new InvalidOperationException ($"Internal error: '{assembly}' `DestinationSubDirectory` metadata has too many components ({parts.Count} instead of 1 or 2)");
684-
}
631+
throw new InvalidOperationException ($"Internal error: '{assembly}' `DestinationSubDirectory` metadata has too many components ({parts.Count} instead of 1 or 2)");
685632
}
686633

687634
string assemblyFilePath = MonoAndroidHelper.MakeZipArchivePath (ArchiveAssembliesPath, parts);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
using Microsoft.Android.Build.Tasks;
5+
using Microsoft.Build.Framework;
6+
using Microsoft.Build.Utilities;
7+
using Xamarin.Android.Tools;
8+
9+
namespace Xamarin.Android.Tasks;
10+
11+
static class AssemblyPackagingHelper
12+
{
13+
public static void AddAssembliesFromCollection (TaskLoggingHelper Log, ICollection<string> SupportedAbis, ICollection<ITaskItem> assemblies, Action<TaskLoggingHelper, AndroidTargetArch, ITaskItem> doAddAssembly)
14+
{
15+
Dictionary<AndroidTargetArch, Dictionary<string, ITaskItem>> perArchAssemblies = MonoAndroidHelper.GetPerArchAssemblies (
16+
assemblies,
17+
SupportedAbis,
18+
validate: true,
19+
shouldSkip: (ITaskItem asm) => {
20+
if (bool.TryParse (asm.GetMetadata ("AndroidSkipAddToPackage"), out bool value) && value) {
21+
Log.LogDebugMessage ($"Skipping {asm.ItemSpec} due to 'AndroidSkipAddToPackage' == 'true' ");
22+
return true;
23+
}
24+
25+
return false;
26+
}
27+
);
28+
29+
foreach (var kvp in perArchAssemblies) {
30+
Log.LogDebugMessage ($"Adding assemblies for architecture '{kvp.Key}'");
31+
DoAddAssembliesFromArchCollection (Log, kvp.Key, kvp.Value, doAddAssembly);
32+
}
33+
}
34+
35+
static void DoAddAssembliesFromArchCollection (TaskLoggingHelper Log, AndroidTargetArch arch, Dictionary<string, ITaskItem> assemblies, Action<TaskLoggingHelper, AndroidTargetArch, ITaskItem> doAddAssembly)
36+
{
37+
foreach (ITaskItem assembly in assemblies.Values) {
38+
if (MonoAndroidHelper.IsReferenceAssembly (assembly.ItemSpec, Log)) {
39+
Log.LogCodedWarning ("XA0107", assembly.ItemSpec, 0, Properties.Resources.XA0107, assembly.ItemSpec);
40+
}
41+
42+
doAddAssembly (Log, arch, assembly);
43+
}
44+
}
45+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
5+
using Microsoft.Android.Build.Tasks;
6+
using Microsoft.Build.Utilities;
7+
using Microsoft.Build.Framework;
8+
using Xamarin.Android.Tools;
9+
10+
namespace Xamarin.Android.Tasks;
11+
12+
class AssemblyStoreBuilder
13+
{
14+
readonly TaskLoggingHelper log;
15+
readonly AssemblyStoreGenerator storeGenerator;
16+
17+
public AssemblyStoreBuilder (TaskLoggingHelper log)
18+
{
19+
this.log = log;
20+
storeGenerator = new (log);
21+
}
22+
23+
public void AddAssembly (string assemblySourcePath, ITaskItem assemblyItem, bool includeDebugSymbols)
24+
{
25+
var storeAssemblyInfo = new AssemblyStoreAssemblyInfo (assemblySourcePath, assemblyItem);
26+
27+
// Try to add config if exists. We use assemblyItem, because `sourcePath` might refer to a compressed
28+
// assembly file in a different location.
29+
var config = Path.ChangeExtension (assemblyItem.ItemSpec, "dll.config");
30+
if (File.Exists (config)) {
31+
storeAssemblyInfo.ConfigFile = new FileInfo (config);
32+
}
33+
34+
if (includeDebugSymbols) {
35+
string debugSymbolsPath = Path.ChangeExtension (assemblyItem.ItemSpec, "pdb");
36+
if (File.Exists (debugSymbolsPath)) {
37+
storeAssemblyInfo.SymbolsFile = new FileInfo (debugSymbolsPath);
38+
}
39+
}
40+
41+
storeGenerator.Add (storeAssemblyInfo);
42+
}
43+
44+
public Dictionary<AndroidTargetArch, string> Generate (string outputDirectoryPath) => storeGenerator.Generate (outputDirectoryPath);
45+
}

0 commit comments

Comments
 (0)