Skip to content

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

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

Draft
wants to merge 55 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
466e052
Embed some data files in `libxamarin-app.so`
grendello Oct 3, 2024
e510709
Use llvm-mc to embed the binary, this way we have full control
grendello Oct 7, 2024
0e15b9d
Runtime config binary blob is now fully embedded
grendello Oct 8, 2024
6340183
Remove debug spam
grendello Oct 8, 2024
19df4bb
New target to build embedded assembly store
grendello Oct 9, 2024
7acf5f7
New task to build embedded assembly store
grendello Oct 9, 2024
dc63ef0
Move some of assembly packaging code to a helper class
grendello Oct 9, 2024
e439290
Move more assembly packaging code to the helper class
grendello Oct 9, 2024
9878608
Almost there, TBC
grendello Oct 9, 2024
ef290af
Assembly store embedding works
grendello Oct 10, 2024
f8461e6
Actually use the embedded assembly store
grendello Oct 10, 2024
955170e
A handful of bugfixes and performance tweaks
grendello Oct 10, 2024
8d62e3a
Fix DSO count when building
grendello Oct 11, 2024
aa86a9d
Don't allocate memory for ZIP Central Directory data
grendello Oct 11, 2024
447cbb2
Experimenting with zip scanning performance
grendello Oct 11, 2024
f697a01
Post rebase fixups
grendello Oct 15, 2024
e542c9a
More post rebase fixups
grendello Oct 15, 2024
907bd19
seek and ye shall read
grendello Oct 15, 2024
100b71d
Details, details...
grendello Oct 16, 2024
f40e9c8
Cleanup
grendello Oct 16, 2024
3c05a34
Teach assembly store explorer about embedded stores
grendello Oct 16, 2024
2ea11d3
Teach store v2 reader to look in `libxamarin-app.so`, too
grendello Oct 16, 2024
07d13ba
Nicer
grendello Oct 16, 2024
ffd9151
The runtime configuration blob is now part of `libxamarin-app.so`
grendello Oct 17, 2024
9a9104f
Optionally produce "empty" embed.*.s files
grendello Oct 17, 2024
5fe3d4c
Don't embed assembly store when fastdev is used
grendello Oct 17, 2024
e992196
Let's see if designer tests pass now
grendello Oct 17, 2024
e955f19
Expect the unexpected, DTB doesn't specify any ABIs
grendello Oct 18, 2024
2c8b676
[WIP] Builds but doesn't work
grendello Oct 18, 2024
184636f
Let's see how it works now
grendello Oct 21, 2024
e5799df
Always generate embedded store sources in CreateEmbeddedAssemblyStore
grendello Oct 22, 2024
eaf589c
Let's see...
grendello Oct 22, 2024
ff832d1
Don't assume assemblies exist
grendello Oct 23, 2024
3ea3e3b
better
grendello Oct 23, 2024
a67f936
Update apkdesc files
grendello Oct 23, 2024
2d196a3
Load embedded assembly store also in filesystem mode
grendello Oct 23, 2024
39324dd
Log it when an assembly is missing in ManifestDocument
grendello Oct 23, 2024
48144f5
Don't throw
grendello Oct 23, 2024
c9102a2
Always create the destination directory
grendello Oct 24, 2024
1a609bc
Post-rebase fixups
grendello Oct 28, 2024
f0060bd
Let's see what's in the override dir listing
grendello Oct 28, 2024
5822876
Let's see
grendello Oct 28, 2024
9aec552
Cleanup
grendello Oct 29, 2024
eb2c2d7
Address feedback
grendello Oct 31, 2024
8459cd0
Let's see what breaks
grendello Nov 4, 2024
be38e90
Revert "Let's see what breaks"
grendello Nov 5, 2024
9c70658
Restore [Require]
grendello Nov 6, 2024
1fc7f4b
Remove comment, no longer valid
grendello Nov 6, 2024
e20f262
Fix after rebase
grendello May 27, 2025
a1665db
Post-rebase fixes
grendello May 27, 2025
db53272
A handful of NRT fixes + reuse shared code
grendello May 28, 2025
275241b
MSBuild updates + cleanup
grendello May 28, 2025
71bc545
Embed runtime config for MonoVM
grendello May 28, 2025
d6e9013
Fix DSO counts
grendello May 28, 2025
661aa5c
Fix MonoVM diagnostic messages
grendello May 28, 2025
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,38 @@
<?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="$(_EmbedAssemblyStoreInRuntime)"
IncludeDebugSymbols="$(AndroidIncludeDebugSymbols)"
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

This file was deleted.

117 changes: 19 additions & 98 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 Down Expand Up @@ -37,109 +33,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 ();
}
}
}
39 changes: 3 additions & 36 deletions src/Xamarin.Android.Build.Tasks/Tasks/CreateAssemblyStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,49 +36,16 @@ public class CreateAssemblyStore : AndroidTask
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 ();
var assemblies = ResolvedFrameworkAssemblies.Concat (ResolvedUserAssemblies).Where (asm => !(AssemblyPackagingHelper.ShouldSkipAssembly (Log, asm)));

if (!UseAssemblyStore) {
AssembliesToAddToArchive = assemblies;
AssembliesToAddToArchive = assemblies.ToArray ();
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");
}

var assembly_store_paths = AssemblyPackagingHelper.CreateAssemblyStore (Log, assemblies, AppSharedLibrariesDir, SupportedAbis, IncludeDebugSymbols);
AssembliesToAddToArchive = assembly_store_paths.Select (kvp => new TaskItem (kvp.Value, new Dictionary<string, string> { { "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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
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;

public class CreateEmbeddedAssemblyStore : AndroidTask
{
public override string TaskPrefix => "CEAS";

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

[Required]
public string AppSharedLibrariesDir { get; set; } = "";

[Required]
public string AssemblySourcesDir { get; set; } = "";

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

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

[Required]
public ITaskItem[] ResolvedUserAssemblies { get; set; } = [];

[Required]
public ITaskItem[] ResolvedFrameworkAssemblies { get; set; } = [];

[Required]
public string [] SupportedAbis { get; set; } = [];

public override bool RunTask ()
{
if (AssemblyStoreEmbeddedInRuntime) {
return EmbedAssemblyStore ();
}

// Generate sources to satisfy libmonodroid's ABI requirements
foreach (string abi in SupportedAbis) {
ELFEmbeddingHelper.EmbedBinary (
Log,
abi,
AndroidBinUtilsDirectory,
inputFile: null,
ELFEmbeddingHelper.KnownEmbedItems.AssemblyStore,
AssemblySourcesDir,
missingContentOK: true
);
}

return !Log.HasLoggedErrors;
}

bool EmbedAssemblyStore ()
{
var assemblies = ResolvedFrameworkAssemblies.Concat (ResolvedUserAssemblies).Where (asm => !(AssemblyPackagingHelper.ShouldSkipAssembly (Log, asm)));
var assemblyStorePaths = AssemblyPackagingHelper.CreateAssemblyStore (
Log, assemblies,
Path.Combine (AppSharedLibrariesDir, "embedded"),
SupportedAbis,
IncludeDebugSymbols
);

foreach (var kvp in assemblyStorePaths) {
string abi = MonoAndroidHelper.ArchToAbi (kvp.Key);
string inputFile = kvp.Value;

ELFEmbeddingHelper.EmbedBinary (
Log,
abi,
AndroidBinUtilsDirectory,
inputFile,
ELFEmbeddingHelper.KnownEmbedItems.AssemblyStore,
AssemblySourcesDir,
missingContentOK: false
);
}

return !Log.HasLoggedErrors;
}
}
Loading
Loading