Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Embed some data files in libxamarin-app.so #9367

Open
wants to merge 49 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
4681cbc
Embed some data files in `libxamarin-app.so`
grendello Oct 3, 2024
3f53146
Use llvm-mc to embed the binary, this way we have full control
grendello Oct 7, 2024
a117cac
Runtime config binary blob is now fully embedded
grendello Oct 8, 2024
eabdee5
Remove debug spam
grendello Oct 8, 2024
daa654c
New target to build embedded assembly store
grendello Oct 9, 2024
70c7b74
New task to build embedded assembly store
grendello Oct 9, 2024
070e60b
Move some of assembly packaging code to a helper class
grendello Oct 9, 2024
d34cffc
Move more assembly packaging code to the helper class
grendello Oct 9, 2024
623d7bd
Almost there, TBC
grendello Oct 9, 2024
87a1297
Assembly store embedding works
grendello Oct 10, 2024
c3dcb75
Actually use the embedded assembly store
grendello Oct 10, 2024
a6a0e2b
A handful of bugfixes and performance tweaks
grendello Oct 10, 2024
17e8d3e
Fix DSO count when building
grendello Oct 11, 2024
fad0c8b
Don't allocate memory for ZIP Central Directory data
grendello Oct 11, 2024
2e2fde6
Experimenting with zip scanning performance
grendello Oct 11, 2024
88dc31e
Post rebase fixups
grendello Oct 15, 2024
492fb28
More post rebase fixups
grendello Oct 15, 2024
4848efb
seek and ye shall read
grendello Oct 15, 2024
b1a3863
Details, details...
grendello Oct 16, 2024
2941030
Cleanup
grendello Oct 16, 2024
d69c5d6
Teach assembly store explorer about embedded stores
grendello Oct 16, 2024
bde06ac
Teach store v2 reader to look in `libxamarin-app.so`, too
grendello Oct 16, 2024
94a09e3
Nicer
grendello Oct 16, 2024
4607b6f
The runtime configuration blob is now part of `libxamarin-app.so`
grendello Oct 17, 2024
872f662
Optionally produce "empty" embed.*.s files
grendello Oct 17, 2024
26bf693
Don't embed assembly store when fastdev is used
grendello Oct 17, 2024
ea9d5b2
Let's see if designer tests pass now
grendello Oct 17, 2024
6aebe30
Expect the unexpected, DTB doesn't specify any ABIs
grendello Oct 18, 2024
56f030d
[WIP] Builds but doesn't work
grendello Oct 18, 2024
fb0e6e2
Let's see how it works now
grendello Oct 21, 2024
9e4b3fe
Always generate embedded store sources in CreateEmbeddedAssemblyStore
grendello Oct 22, 2024
581991e
Let's see...
grendello Oct 22, 2024
3a46110
Don't assume assemblies exist
grendello Oct 23, 2024
81726be
better
grendello Oct 23, 2024
a84b663
Update apkdesc files
grendello Oct 23, 2024
1035cdd
Load embedded assembly store also in filesystem mode
grendello Oct 23, 2024
d46e20f
Log it when an assembly is missing in ManifestDocument
grendello Oct 23, 2024
d14811c
Add some debug logging
grendello Oct 23, 2024
3648ab1
Don't throw
grendello Oct 23, 2024
4acc5a2
Always create the destination directory
grendello Oct 24, 2024
a7095d2
Post-rebase fixups
grendello Oct 28, 2024
4031e9b
Let's see what's in the override dir listing
grendello Oct 28, 2024
836c4b4
Let's see
grendello Oct 28, 2024
f4f1502
Cleanup
grendello Oct 29, 2024
8e41bdd
Address feedback
grendello Oct 31, 2024
bc47870
Let's see what breaks
grendello Nov 4, 2024
8dfab2e
Revert "Let's see what breaks"
grendello Nov 5, 2024
66179bc
Restore [Require]
grendello Nov 6, 2024
c584dbd
Remove comment, no longer valid
grendello Nov 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ This file is imported *after* the Microsoft.NET.Sdk/Sdk.targets.
<Import Project="Microsoft.Android.Sdk.Publish.targets" />
<Import Project="Microsoft.Android.Sdk.RuntimeConfig.targets" />
<Import Project="Microsoft.Android.Sdk.Tooling.targets" />
<Import Project="Microsoft.Android.Sdk.AssemblyStores.targets" />
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="Xamarin.Android.Tasks.CreateEmbeddedAssemblyStore" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" />
<UsingTask TaskName="Xamarin.Android.Tasks.PrepareAbiItems" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" />

<Target Name="_PrepareCreateEmbeddedAssemblyStoreOutputItems">
<PrepareAbiItems
BuildTargetAbis="@(_BuildTargetAbis)"
NativeSourcesDir="$(_NativeAssemblySourceDir)"
Mode="EmbeddedAssemblyStore">
<Output TaskParameter="AssemblySources" ItemName="_EmbeddedAssemblyStoreSourceFiles" />
</PrepareAbiItems>

<ItemGroup>
<_CreateEmbeddedAssemblyStoreAssembly Include="@(_ShrunkUserAssemblies);@(_AndroidResolvedSatellitePaths);@(_ShrunkFrameworkAssemblies)"/>
<_CreateEmbeddedAssemblyStoreAssembly Condition=" '@(_CreateEmbeddedAssemblyStoreAssembly->Count())' == '0' " Include="@(_ResolvedUserAssemblies);@(_ResolvedFrameworkAssemblies);@(_AndroidResolvedSatellitePaths)" />
</ItemGroup>
</Target>

<Target Name="_CreateEmbeddedAssemblyStore"
DependsOnTargets="_PrepareCreateEmbeddedAssemblyStoreOutputItems"
Inputs="@(_CreateEmbeddedAssemblyStoreAssembly)"
Outputs="@(_EmbeddedAssemblyStoreSourceFiles)">
<CreateEmbeddedAssemblyStore
AndroidBinUtilsDirectory="$(AndroidBinUtilsDirectory)"
AppSharedLibrariesDir="$(_AndroidApplicationSharedLibraryPath)"
AssemblySourcesDir="$(IntermediateOutputPath)android"
AssemblyStoreEmbeddedInRuntime="$(_AndroidEmbedAssemblyStoreInRuntime)"
CompressedAssembliesDir="$(_AndroidCompressedAssembliesDir)\test\"
Debug="$(AndroidIncludeDebugSymbols)"
EnableCompression="$(AndroidEnableAssemblyCompression)"
ProjectFullPath="$(MSBuildProjectFullPath)"
ResolvedUserAssemblies="@(_ShrunkUserAssemblies);@(_AndroidResolvedSatellitePaths)"
ResolvedFrameworkAssemblies="@(_ShrunkFrameworkAssemblies)"
SupportedAbis="@(_BuildTargetAbis)" />

<ItemGroup>
<FileWrites Include="@(_EmbeddedAssemblyStoreSourceFiles)" />
</ItemGroup>
</Target>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ properties that determine build ordering.
<_PrepareBuildApkDependsOnTargets>
_SetLatestTargetFrameworkVersion;
_GetLibraryImports;
_RemoveRegisterAttribute;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change needed? I didn't see anything in here that is trying to reorder MSBuild targets?

Maybe it's from before, trying to workaround the bug in #9452?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible, I don't remember exactly which test was affected by this change. I'll revert this bit and we'll see what (if anything) breaks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did this revert work?

_ResolveAssemblies;
_ResolveSatellitePaths;
_CreatePackageWorkspace;
Expand Down
45 changes: 13 additions & 32 deletions src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,6 @@ public class BuildApk : AndroidTask

public string CheckedBuild { get; set; }

public string RuntimeConfigBinFilePath { get; set; }

public bool UseAssemblyStore { get; set; }

public string ZipFlushFilesLimit { get; set; }
Expand All @@ -118,6 +116,12 @@ public class BuildApk : AndroidTask
[Required]
public string ProjectFullPath { get; set; }

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

[Required]
public bool AssemblyStoreEmbeddedInRuntime { get; set; }

[Output]
public ITaskItem[] OutputFiles { get; set; }

Expand Down Expand Up @@ -218,7 +222,6 @@ void ExecuteWithAbi (DSOWrapperGenerator.Config dsoWrapperConfig, string [] supp
apk.Flush ();
}

AddRuntimeConfigBlob (dsoWrapperConfig, apk);
AddRuntimeLibraries (apk, supportedAbis);
apk.Flush();
AddNativeLibraries (files, supportedAbis);
Expand Down Expand Up @@ -354,7 +357,7 @@ public override bool RunTask ()

if (compress) {
string key = CompressedAssemblyInfo.GetKey (ProjectFullPath);
Log.LogDebugMessage ($"Retrieving assembly compression info with key '{key}'");
Log.LogDebugMessage ($"[{TaskPrefix}] Retrieving assembly compression info with key '{key}'");
compressedAssembliesInfo = BuildEngine4.UnregisterTaskObjectAssemblyLocal<IDictionary<AndroidTargetArch, Dictionary<string, CompressedAssemblyInfo>>> (key, RegisteredTaskObjectLifetime.Build);
if (compressedAssembliesInfo == null)
throw new InvalidOperationException ($"Assembly compression info not found for key '{key}'. Compression will not be performed.");
Expand Down Expand Up @@ -403,31 +406,17 @@ static Regex FileGlobToRegEx (string fileGlob, RegexOptions options)
return new Regex (sb.ToString (), options);
}

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%
// of cases these days, since AAB enforces that split). `base.apk` contains only ABI-agnostic file, while one of the split config files contains only
// ABI-specific data+code.
if (!String.IsNullOrEmpty (RuntimeConfigBinFilePath) && File.Exists (RuntimeConfigBinFilePath)) {
foreach (string abi in SupportedAbis) {
// 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 (Log, dsoWrapperConfig, MonoAndroidHelper.AbiToTargetArch (abi), RuntimeConfigBinFilePath, Path.GetFileName (inArchivePath));
AddFileToArchiveIfNewer (apk, wrappedSourcePath, inArchivePath, compressionMethod: GetCompressionMethod (inArchivePath));
}
}
}

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;
string compressedOutputDir = Path.GetFullPath (Path.Combine (Path.GetDirectoryName (ApkOutputPath), "..", "lz4"));
AssemblyStoreBuilder? storeBuilder = null;

if (UseAssemblyStore) {
if (AssemblyStoreEmbeddedInRuntime) {
// We don't need to do anything here, the store is in `libxamarin-app.so`
return;
}

storeBuilder = new AssemblyStoreBuilder (Log);
}

Expand Down Expand Up @@ -501,21 +490,13 @@ void DoAddAssembliesFromArchCollection (TaskLoggingHelper log, AndroidTargetArch
);
}

void EnsureCompressedAssemblyData (string sourcePath, uint descriptorIndex)
{
if (compressedAssembly == null)
compressedAssembly = new AssemblyCompression.AssemblyData (sourcePath, descriptorIndex);
else
compressedAssembly.SetData (sourcePath, descriptorIndex);
}

string CompressAssembly (ITaskItem assembly)
{
if (!compress) {
return assembly.ItemSpec;
}

return AssemblyCompression.Compress (Log, assembly, compressedAssembliesInfo, compressedOutputDir);
return AssemblyCompression.Compress (Log, assembly, compressedAssembliesInfo, CompressedAssembliesDir);
}
}

Expand Down
124 changes: 19 additions & 105 deletions src/Xamarin.Android.Build.Tasks/Tasks/CompileNativeAssembly.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;

using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

using Xamarin.Android.Tools;
using Microsoft.Android.Build.Tasks;

namespace Xamarin.Android.Tasks
Expand All @@ -16,13 +12,6 @@ public class CompileNativeAssembly : AsyncTask
{
public override string TaskPrefix => "CNA";

sealed class Config
{
public string AssemblerPath;
public string AssemblerOptions;
public string InputSource;
}

[Required]
public ITaskItem[] Sources { get; set; }

Expand All @@ -37,109 +26,34 @@ sealed class Config

public override System.Threading.Tasks.Task RunTaskAsync ()
{
return this.WhenAll (GetAssemblerConfigs (), RunAssembler);
var context = new NativeAssemblerCompilation.AssemblerRunContext (
Log,
Path.GetFullPath (WorkingDirectory),
registerForCancellation: RegisterForCancellation,
cancel: Cancel
);

return this.WhenAll (
GetAssemblerConfigs (),
(NativeAssemblerCompilation.AssemblerConfig config) => NativeAssemblerCompilation.RunAssembler (context, config)
);
}

void RunAssembler (Config config)
void RegisterForCancellation (Process proc)
{
var stdout_completed = new ManualResetEvent (false);
var stderr_completed = new ManualResetEvent (false);
var psi = new ProcessStartInfo () {
FileName = config.AssemblerPath,
Arguments = config.AssemblerOptions,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
WorkingDirectory = WorkingDirectory,
};

string assemblerName = Path.GetFileName (config.AssemblerPath);
LogDebugMessage ($"[LLVM llc] {psi.FileName} {psi.Arguments}");

var stdoutLines = new List<string> ();
var stderrLines = new List<string> ();

using (var proc = new Process ()) {
proc.OutputDataReceived += (s, e) => {
if (e.Data != null) {
OnOutputData (assemblerName, s, e);
stdoutLines.Add (e.Data);
} else
stdout_completed.Set ();
};

proc.ErrorDataReceived += (s, e) => {
if (e.Data != null) {
OnErrorData (assemblerName, s, e);
stderrLines.Add (e.Data);
} else
stderr_completed.Set ();
};

proc.StartInfo = psi;
proc.Start ();
proc.BeginOutputReadLine ();
proc.BeginErrorReadLine ();
CancellationToken.Register (() => { try { proc.Kill (); } catch (Exception) { } });
proc.WaitForExit ();

if (psi.RedirectStandardError)
stderr_completed.WaitOne (TimeSpan.FromSeconds (30));

if (psi.RedirectStandardOutput)
stdout_completed.WaitOne (TimeSpan.FromSeconds (30));

if (proc.ExitCode != 0) {
var sb = MonoAndroidHelper.MergeStdoutAndStderrMessages (stdoutLines, stderrLines);
LogCodedError ("XA3006", Properties.Resources.XA3006, Path.GetFileName (config.InputSource), sb.ToString ());
Cancel ();
CancellationToken.Register (() => {
try {
proc.Kill ();
} catch (Exception) {
}
}
});
}

IEnumerable<Config> GetAssemblerConfigs ()
IEnumerable<NativeAssemblerCompilation.AssemblerConfig> GetAssemblerConfigs ()
{
const string assemblerOptions =
"-O2 " +
"--debugger-tune=lldb " + // NDK uses lldb now
"--debugify-level=location+variables " +
"--fatal-warnings " +
"--filetype=obj " +
"--relocation-model=pic";
string llcPath = Path.Combine (AndroidBinUtilsDirectory, "llc");

foreach (ITaskItem item in Sources) {
// We don't need the directory since our WorkingDirectory is where all the sources are
string sourceFile = Path.GetFileName (item.ItemSpec);
string outputFile = QuoteFileName (sourceFile.Replace (".ll", ".o"));
string executableDir = Path.GetDirectoryName (llcPath);
string executableName = MonoAndroidHelper.GetExecutablePath (executableDir, Path.GetFileName (llcPath));

yield return new Config {
InputSource = item.ItemSpec,
AssemblerPath = Path.Combine (executableDir, executableName),
AssemblerOptions = $"{assemblerOptions} -o={outputFile} {QuoteFileName (sourceFile)}",
};
yield return NativeAssemblerCompilation.GetAssemblerConfig (AndroidBinUtilsDirectory, item, stripFilePaths: true);
}
}

void OnOutputData (string assemblerName, object sender, DataReceivedEventArgs e)
{
LogDebugMessage ($"[{assemblerName} stdout] {e.Data}");
}

void OnErrorData (string assemblerName, object sender, DataReceivedEventArgs e)
{
LogMessage ($"[{assemblerName} stderr] {e.Data}", MessageImportance.High);
}

static string QuoteFileName (string fileName)
{
var builder = new CommandLineBuilder ();
builder.AppendFileNameIfNotNull (fileName);
return builder.ToString ();
}
}
}
Loading
Loading