Skip to content

Commit

Permalink
[Xamarin.Android.Build.Tasks] Move DSO wrapping code to BuildApk (#9349)
Browse files Browse the repository at this point in the history
In order to avoid having to register a task object in the
`<PrepareDSOWrapperState/>` task, move the DSO wrapper stub library
discovery code into the `<BuildApk/>` task.
  • Loading branch information
grendello authored Oct 15, 2024
1 parent f09caed commit 1857f28
Show file tree
Hide file tree
Showing 13 changed files with 96 additions and 105 deletions.
1 change: 0 additions & 1 deletion build-tools/create-packs/Microsoft.Android.Runtime.proj
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ projects that use the Microsoft.Android framework in .NET 6+.
<_AndroidRuntimePackAssets Include="$(MicrosoftAndroidSdkOutDir)lib\$(AndroidRID)\libmono-android.release.so" />
<_AndroidRuntimePackAssets Include="$(MicrosoftAndroidSdkOutDir)lib\$(AndroidRID)\libxamarin-debug-app-helper.so" />
<_AndroidRuntimePackAssets Include="$(MicrosoftAndroidSdkOutDir)lib\$(AndroidRID)\libxamarin-native-tracing.so" />
<_AndroidRuntimePackAssets Include="$(MicrosoftAndroidSdkOutDir)lib\$(AndroidRID)\libarchive-dso-stub.so" />
<_AndroidRuntimePackAssets Include="$(MicrosoftAndroidSdkOutDir)lib\$(AndroidRID)\libunwind_xamarin.a" />
<FrameworkListFileClass Include="@(_AndroidRuntimePackAssemblies->'%(Filename)%(Extension)')" Profile="Android" />
<FrameworkListFileClass Include="@(_AndroidRuntimePackAssets->'%(Filename)%(Extension)')" Profile="Android" />
Expand Down
4 changes: 4 additions & 0 deletions build-tools/installers/create-installers.targets
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x64\libm.so" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x86\libc.so" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x86\libm.so" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)dsostubs\android-arm64\libarchive-dso-stub.so" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)dsostubs\android-arm\libarchive-dso-stub.so" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)dsostubs\android-x64\libarchive-dso-stub.so" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)dsostubs\android-x86\libarchive-dso-stub.so" />
</ItemGroup>
<ItemGroup>
<_MSBuildTargetsSrcFiles Include="$(MSBuildTargetsSrcDir)\Xamarin.Android.AvailableItems.targets" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ _ResolveAssemblies MSBuild target.
<UsingTask TaskName="Xamarin.Android.Tasks.ProcessAssemblies" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" />
<UsingTask TaskName="Xamarin.Android.Tasks.ProcessNativeLibraries" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" />
<UsingTask TaskName="Xamarin.Android.Tasks.StripNativeLibraries" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" />
<UsingTask TaskName="Xamarin.Android.Tasks.PrepareDSOWrapperState" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" />

<!-- HACK: workaround for: https://github.com/dotnet/sdk/issues/25679 -->
<Target Name="_RemoveLinuxFrameworkReferences"
Expand Down Expand Up @@ -124,16 +123,9 @@ _ResolveAssemblies MSBuild target.
</ItemGroup>

<ItemGroup>
<_ResolvedArchiveDSOStub Include="@(ResolvedFileToPublish)" Condition=" '%(ResolvedFileToPublish.Filename)%(ResolvedFileToPublish.Extension)' == 'libarchive-dso-stub.so' " />
<ResolvedFileToPublish Remove="@(_ResolvedArchiveDSOStub)" />
</ItemGroup>

<!-- This must run as early as possible, as soon as we have locations of the .so files and before any wrapping/packaging is done -->
<PrepareDSOWrapperState
ArchiveDSOStubs="@(_ResolvedArchiveDSOStub)"
AndroidBinUtilsDirectory="$(AndroidBinUtilsDirectory)"
BaseOutputDirectory="$(IntermediateOutputPath)" />

<!-- All assemblies must be per-RID, thus no `->Distinct()` on `InputAssemblies` or `ResolvedSymbols` items -->
<ProcessAssemblies
RuntimeIdentifiers="@(_RIDs)"
Expand Down
48 changes: 24 additions & 24 deletions src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ public class BuildApk : AndroidTask

public int ZipAlignmentPages { get; set; } = AndroidZipAlign.DefaultZipAlignment64Bit;

[Required]
public string AndroidBinUtilsDirectory { get; set; }

[Required]
public string IntermediateOutputPath { get; set; }

[Required]
public string ProjectFullPath { get; set; }

Expand Down Expand Up @@ -141,7 +147,7 @@ protected virtual void FixupArchive (ZipArchiveEx zip) { }

List<Regex> includePatterns = new List<Regex> ();

void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOutputPath, bool debug, bool compress, IDictionary<AndroidTargetArch, Dictionary<string, CompressedAssemblyInfo>> compressedAssembliesInfo, string assemblyStoreApkName)
void ExecuteWithAbi (DSOWrapperGenerator.Config dsoWrapperConfig, string [] supportedAbis, string apkInputPath, string apkOutputPath, bool debug, bool compress, IDictionary<AndroidTargetArch, Dictionary<string, CompressedAssemblyInfo>> compressedAssembliesInfo, string assemblyStoreApkName)
{
ArchiveFileList files = new ArchiveFileList ();
bool refresh = true;
Expand Down Expand Up @@ -208,11 +214,11 @@ void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOut
}

if (EmbedAssemblies) {
AddAssemblies (apk, debug, compress, compressedAssembliesInfo, assemblyStoreApkName);
AddAssemblies (dsoWrapperConfig, apk, debug, compress, compressedAssembliesInfo, assemblyStoreApkName);
apk.Flush ();
}

AddRuntimeConfigBlob (apk);
AddRuntimeConfigBlob (dsoWrapperConfig, apk);
AddRuntimeLibraries (apk, supportedAbis);
apk.Flush();
AddNativeLibraries (files, supportedAbis);
Expand Down Expand Up @@ -354,14 +360,17 @@ public override bool RunTask ()
throw new InvalidOperationException ($"Assembly compression info not found for key '{key}'. Compression will not be performed.");
}

ExecuteWithAbi (SupportedAbis, ApkInputPath, ApkOutputPath, debug, compress, compressedAssembliesInfo, assemblyStoreApkName: null);
DSOWrapperGenerator.Config dsoWrapperConfig = DSOWrapperGenerator.GetConfig (Log, AndroidBinUtilsDirectory, IntermediateOutputPath);
ExecuteWithAbi (dsoWrapperConfig, SupportedAbis, ApkInputPath, ApkOutputPath, debug, compress, compressedAssembliesInfo, assemblyStoreApkName: null);
outputFiles.Add (ApkOutputPath);
if (CreatePackagePerAbi && SupportedAbis.Length > 1) {
var abiArray = new string[] { String.Empty };
foreach (var abi in SupportedAbis) {
existingEntries.Clear ();
var path = Path.GetDirectoryName (ApkOutputPath);
var apk = Path.GetFileNameWithoutExtension (ApkOutputPath);
ExecuteWithAbi (new [] { abi }, String.Format ("{0}-{1}", ApkInputPath, abi),
abiArray[0] = abi;
ExecuteWithAbi (dsoWrapperConfig, abiArray, String.Format ("{0}-{1}", ApkInputPath, abi),
Path.Combine (path, String.Format ("{0}-{1}.apk", apk, abi)),
debug, compress, compressedAssembliesInfo, assemblyStoreApkName: abi);
outputFiles.Add (Path.Combine (path, String.Format ("{0}-{1}.apk", apk, abi)));
Expand All @@ -371,20 +380,11 @@ public override bool RunTask ()
OutputFiles = outputFiles.Select (a => new TaskItem (a)).ToArray ();

Log.LogDebugTaskItems (" [Output] OutputFiles :", OutputFiles);
DSOWrapperGenerator.CleanUp (this);
DSOWrapperGenerator.CleanUp (dsoWrapperConfig);

return !Log.HasLoggedErrors;
}

internal DSOWrapperGenerator.Config EnsureConfig ()
{
var config = BuildEngine4.GetRegisteredTaskObjectAssemblyLocal<DSOWrapperGenerator.Config> (ProjectSpecificTaskObjectKey (DSOWrapperGenerator.RegisteredConfigKey), RegisteredTaskObjectLifetime.Build);
if (config is null) {
throw new InvalidOperationException ("Internal error: no registered config found");
}
return config;
}

static Regex FileGlobToRegEx (string fileGlob, RegexOptions options)
{
StringBuilder sb = new StringBuilder ();
Expand All @@ -403,7 +403,7 @@ static Regex FileGlobToRegEx (string fileGlob, RegexOptions options)
return new Regex (sb.ToString (), options);
}

void AddRuntimeConfigBlob (ZipArchiveEx apk)
void AddRuntimeConfigBlob (DSOWrapperGenerator.Config dsoWrapperConfig, ZipArchiveEx apk)
{
// We will place rc.bin in the `lib` directory next to the blob, to make startup slightly faster, as we will find the config file right after we encounter
// our assembly store. Not only that, but also we'll be able to skip scanning the `base.apk` archive when split configs are enabled (which they are in 99%
Expand All @@ -414,13 +414,13 @@ void AddRuntimeConfigBlob (ZipArchiveEx apk)
// Prefix it with `a` because bundletool sorts entries alphabetically, and this will place it right next to `assemblies.*.blob.so`, which is what we
// like since we can finish scanning the zip central directory earlier at startup.
string inArchivePath = MakeArchiveLibPath (abi, "libarc.bin.so");
string wrappedSourcePath = DSOWrapperGenerator.WrapIt (MonoAndroidHelper.AbiToTargetArch (abi), RuntimeConfigBinFilePath, Path.GetFileName (inArchivePath), this);
string wrappedSourcePath = DSOWrapperGenerator.WrapIt (Log, dsoWrapperConfig, MonoAndroidHelper.AbiToTargetArch (abi), RuntimeConfigBinFilePath, Path.GetFileName (inArchivePath));
AddFileToArchiveIfNewer (apk, wrappedSourcePath, inArchivePath, compressionMethod: GetCompressionMethod (inArchivePath));
}
}
}

void AddAssemblies (ZipArchiveEx apk, bool debug, bool compress, IDictionary<AndroidTargetArch, Dictionary<string, CompressedAssemblyInfo>> compressedAssembliesInfo, string assemblyStoreApkName)
void AddAssemblies (DSOWrapperGenerator.Config dsoWrapperConfig, ZipArchiveEx apk, bool debug, bool compress, IDictionary<AndroidTargetArch, Dictionary<string, CompressedAssemblyInfo>> compressedAssembliesInfo, string assemblyStoreApkName)
{
string sourcePath;
AssemblyCompression.AssemblyData compressedAssembly = null;
Expand Down Expand Up @@ -455,7 +455,7 @@ void AddAssemblies (ZipArchiveEx apk, bool debug, bool compress, IDictionary<And
foreach (var kvp in assemblyStorePaths) {
string abi = MonoAndroidHelper.ArchToAbi (kvp.Key);
inArchivePath = MakeArchiveLibPath (abi, "lib" + Path.GetFileName (kvp.Value));
string wrappedSourcePath = DSOWrapperGenerator.WrapIt (kvp.Key, kvp.Value, Path.GetFileName (inArchivePath), this);
string wrappedSourcePath = DSOWrapperGenerator.WrapIt (Log, dsoWrapperConfig, kvp.Key, kvp.Value, Path.GetFileName (inArchivePath));
AddFileToArchiveIfNewer (apk, wrappedSourcePath, inArchivePath, GetCompressionMethod (inArchivePath));
}

Expand All @@ -474,12 +474,12 @@ void DoAddAssembliesFromArchCollection (TaskLoggingHelper log, AndroidTargetArch

// Add assembly
(string assemblyPath, string assemblyDirectory) = GetInArchiveAssemblyPath (assembly);
string wrappedSourcePath = DSOWrapperGenerator.WrapIt (arch, sourcePath, Path.GetFileName (assemblyPath), this);
string wrappedSourcePath = DSOWrapperGenerator.WrapIt (Log, dsoWrapperConfig, arch, sourcePath, Path.GetFileName (assemblyPath));
AddFileToArchiveIfNewer (apk, wrappedSourcePath, assemblyPath, compressionMethod: GetCompressionMethod (assemblyPath));

// Try to add config if exists
var config = Path.ChangeExtension (assembly.ItemSpec, "dll.config");
AddAssemblyConfigEntry (apk, arch, assemblyDirectory, config);
AddAssemblyConfigEntry (dsoWrapperConfig, apk, arch, assemblyDirectory, config);

// Try to add symbols if Debug
if (!debug) {
Expand All @@ -492,7 +492,7 @@ void DoAddAssembliesFromArchCollection (TaskLoggingHelper log, AndroidTargetArch
}

string archiveSymbolsPath = assemblyDirectory + MonoAndroidHelper.MakeDiscreteAssembliesEntryName (Path.GetFileName (symbols));
string wrappedSymbolsPath = DSOWrapperGenerator.WrapIt (arch, symbols, Path.GetFileName (archiveSymbolsPath), this);
string wrappedSymbolsPath = DSOWrapperGenerator.WrapIt (Log, dsoWrapperConfig, arch, symbols, Path.GetFileName (archiveSymbolsPath));
AddFileToArchiveIfNewer (
apk,
wrappedSymbolsPath,
Expand Down Expand Up @@ -531,7 +531,7 @@ bool AddFileToArchiveIfNewer (ZipArchiveEx apk, string file, string inArchivePat
return true;
}

void AddAssemblyConfigEntry (ZipArchiveEx apk, AndroidTargetArch arch, string assemblyPath, string configFile)
void AddAssemblyConfigEntry (DSOWrapperGenerator.Config dsoWrapperConfig, ZipArchiveEx apk, AndroidTargetArch arch, string assemblyPath, string configFile)
{
string inArchivePath = MonoAndroidHelper.MakeDiscreteAssembliesEntryName (assemblyPath + Path.GetFileName (configFile));
existingEntries.Remove (inArchivePath);
Expand All @@ -547,7 +547,7 @@ void AddAssemblyConfigEntry (ZipArchiveEx apk, AndroidTargetArch arch, string as
}

Log.LogDebugMessage ($"Adding {configFile} as the archive file is out of date.");
string wrappedConfigFile = DSOWrapperGenerator.WrapIt (arch, configFile, Path.GetFileName (inArchivePath), this);
string wrappedConfigFile = DSOWrapperGenerator.WrapIt (Log, dsoWrapperConfig, arch, configFile, Path.GetFileName (inArchivePath));
apk.AddFileAndFlush (wrappedConfigFile, inArchivePath, compressionMethod);
}

Expand Down
54 changes: 0 additions & 54 deletions src/Xamarin.Android.Build.Tasks/Tasks/PrepareDSOWrapperState.cs

This file was deleted.

40 changes: 30 additions & 10 deletions src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ class DSOWrapperGenerator

internal class Config
{
public Dictionary<AndroidTargetArch, ITaskItem> DSOStubPaths { get; }
public Dictionary<AndroidTargetArch, string> DSOStubPaths { get; }
public string AndroidBinUtilsDirectory { get; }
public string BaseOutputDirectory { get; }

public Config (Dictionary<AndroidTargetArch, ITaskItem> stubPaths, string androidBinUtilsDirectory, string baseOutputDirectory)
public Config (Dictionary<AndroidTargetArch, string> stubPaths, string androidBinUtilsDirectory, string baseOutputDirectory)
{
DSOStubPaths = stubPaths;
AndroidBinUtilsDirectory = androidBinUtilsDirectory;
Expand All @@ -53,6 +53,30 @@ public Config (Dictionary<AndroidTargetArch, ITaskItem> stubPaths, string androi
//
const ulong PayloadSectionAlignment = 0x4000;

public static Config GetConfig (TaskLoggingHelper log, string androidBinUtilsDirectory, string baseOutputDirectory)
{
var stubPaths = new Dictionary<AndroidTargetArch, string> ();
string archiveDSOStubsRootDir = MonoAndroidHelper.GetDSOStubsRootDirectoryPath (androidBinUtilsDirectory);

foreach (string dir in Directory.EnumerateDirectories (archiveDSOStubsRootDir, "android-*")) {
string rid = Path.GetFileName (dir);
AndroidTargetArch arch = MonoAndroidHelper.RidToArchMaybe (rid);
if (arch == AndroidTargetArch.None) {
log.LogDebugMessage ($"Unable to extract a supported RID name from directory path '{dir}'");
continue;
}

string stubPath = Path.Combine (dir, "libarchive-dso-stub.so");
if (!File.Exists (stubPath)) {
throw new InvalidOperationException ($"Internal error: archive DSO stub file '{stubPath}' does not exist");
}

stubPaths.Add (arch, stubPath);
}

return new Config (stubPaths, androidBinUtilsDirectory, baseOutputDirectory);
}

static string GetArchOutputPath (AndroidTargetArch targetArch, Config config)
{
return Path.Combine (config.BaseOutputDirectory, MonoAndroidHelper.ArchToRid (targetArch), "wrapped");
Expand All @@ -62,22 +86,20 @@ static string GetArchOutputPath (AndroidTargetArch targetArch, Config config)
/// Puts the indicated file (<paramref name="payloadFilePath"/>) inside an ELF shared library and returns
/// path to the wrapped file.
/// </summary>
public static string WrapIt (AndroidTargetArch targetArch, string payloadFilePath, string outputFileName, BuildApk task)
public static string WrapIt (TaskLoggingHelper log, Config config, AndroidTargetArch targetArch, string payloadFilePath, string outputFileName)
{
TaskLoggingHelper log = task.Log;
log.LogDebugMessage ($"[{targetArch}] Putting '{payloadFilePath}' inside ELF shared library '{outputFileName}'");
Config config = task.EnsureConfig ();
string outputDir = GetArchOutputPath (targetArch, config);
Directory.CreateDirectory (outputDir);

string outputFile = Path.Combine (outputDir, outputFileName);
log.LogDebugMessage ($" output file path: {outputFile}");

if (!config.DSOStubPaths.TryGetValue (targetArch, out ITaskItem? stubItem)) {
if (!config.DSOStubPaths.TryGetValue (targetArch, out string? stubPath)) {
throw new InvalidOperationException ($"Internal error: archive DSO stub location not known for architecture '{targetArch}'");
}

File.Copy (stubItem.ItemSpec, outputFile, overwrite: true);
File.Copy (stubPath, outputFile, overwrite: true);

string quotedOutputFile = MonoAndroidHelper.QuoteFileNameArgument (outputFile);
string objcopy = Path.Combine (config.AndroidBinUtilsDirectory, MonoAndroidHelper.GetExecutablePath (config.AndroidBinUtilsDirectory, "llvm-objcopy"));
Expand Down Expand Up @@ -107,10 +129,8 @@ public static string WrapIt (AndroidTargetArch targetArch, string payloadFilePat
/// created by this class. The reason to do so is to ensure that we don't package any "stale" content and those
/// wrapper files aren't part of any dependency chain so it's hard to check their up to date state.
/// </summary>
public static void CleanUp (BuildApk task)
public static void CleanUp (Config config)
{
Config config = task.EnsureConfig();

foreach (var kvp in config.DSOStubPaths) {
string outputDir = GetArchOutputPath (kvp.Key, config);
if (!Directory.Exists (outputDir)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,19 @@ public static string RidToAbi (string rid)
return abi;
}

public static AndroidTargetArch RidToArch (string rid)
public static AndroidTargetArch RidToArchMaybe (string rid)
{
if (!RidToArchMap.TryGetValue (rid, out AndroidTargetArch arch)) {
return AndroidTargetArch.None;
};

return arch;
}

public static AndroidTargetArch RidToArch (string rid)
{
AndroidTargetArch arch = RidToArchMaybe (rid);
if (arch == AndroidTargetArch.None) {
throw new NotSupportedException ($"Internal error: unsupported Runtime Identifier '{rid}'");
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,12 @@ public static string GetLibstubsRootDirectoryPath (string androidBinUtilsDirecto
return Path.GetFullPath (Path.Combine (androidBinUtilsDirectory, relPath, "libstubs"));
}

public static string GetDSOStubsRootDirectoryPath (string androidBinUtilsDirectory)
{
string relPath = GetToolsRootDirectoryRelativePath (androidBinUtilsDirectory);
return Path.GetFullPath (Path.Combine (androidBinUtilsDirectory, relPath, "dsostubs"));
}

public static string GetNativeLibsRootDirectoryPath (string androidBinUtilsDirectory)
{
string relPath = GetToolsRootDirectoryRelativePath (androidBinUtilsDirectory);
Expand Down
Loading

0 comments on commit 1857f28

Please sign in to comment.