Skip to content

Commit

Permalink
[NativeAOT] improve build logic, part 2 (#9631)
Browse files Browse the repository at this point in the history
Context: 8d77130
Context: #9630

We're exploring how to get .NET for Android apps to build and run
using [Native AOT][0].

Default to `$(TrimMode)=Full` for NativeAOT, this enables trimmer
warnings and should be the default mode for NativeAOT.

Ensure the `_PrepareLinking` MSBuild target runs at the appropriate
time.  Failure to do so was causing
`Android.App.Activity.GetOnCreate_Landroid_os_Bundle_Handler()` to be
trimmed away.

Various fixes to avoid `java.lang.UnsatisfiedLinkError` errors:

  * Set `$(LinkerFlavor)=lld` by default to avoid:

        E AndroidRuntime: java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "__start___modules" referenced by
          "/data/app/~~_ggpMC4foLk_jUUycm0CfA==/net.dot.hellonativeaot-fvszIWroqgweLHYgULxVoQ==/split_config.arm64_v8a.apk!/lib/arm64-v8a/libNativeAOT.so"...

  * Include `libc++_shared.so` within the app, as Native AOT output
    requires C++:

        E AndroidRuntime: java.lang.UnsatisfiedLinkError: dlopen failed: library "libc++_shared.so" not found: needed by
          /data/app/~~_W0B9EE3hhajnFvCHyUKSg==/net.dot.hellonativeaot-zlXemqHdkbHaLu60oYPVQQ==/lib/arm64/libNativeAOT.so in namespace clns-6

Emit `JavaPeerStyle.JavaInterop1` java stubs for NativeAOT, as it is
easier to get working in place of `JavaPeerStyle.XAJavaInterop1`.
Specifically, we need to be able to avoid P/Invokes related to
typemaps/etc.  XAJavaInterop1 hits `JNIEnvInit.RegisterJniNatives()`,
which is full of P/Invokes such as `TypeManager.GetClassName()`,
while `JavaInterop1` hits `ManagedPeer.RegisterNativeMembers()` which
goes through `JniRuntime` abstractions, allowing for a P/Invoke-free
code path.

I updated `BuildTest2.NativeAOT()` to assert for these changes where
possible.

[0]: https://learn.microsoft.com/dotnet/core/deploying/native-aot/
  • Loading branch information
jonathanpeppers authored Dec 19, 2024
1 parent df9ab24 commit 5f4d223
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
<AndroidLinkMode Condition=" '$(AndroidLinkMode)' == '' and '$(PublishTrimmed)' == 'true' ">SdkOnly</AndroidLinkMode>
<AndroidLinkMode Condition=" '$(AndroidLinkMode)' == '' ">None</AndroidLinkMode>
<!-- For compat with user code not marked trimmable, only trim opt-in by default. -->
<TrimMode Condition=" '$(TrimMode)' == '' and '$(AndroidLinkMode)' == 'Full' ">full</TrimMode>
<TrimMode Condition=" '$(TrimMode)' == '' and ('$(AndroidLinkMode)' == 'Full' or '$(_AndroidNativeAot)' == 'true') ">full</TrimMode>
<TrimMode Condition="'$(TrimMode)' == ''">partial</TrimMode>
<SuppressTrimAnalysisWarnings Condition=" '$(SuppressTrimAnalysisWarnings)' == '' and ('$(TrimMode)' == 'full' or '$(IsAotCompatible)' == 'true') ">false</SuppressTrimAnalysisWarnings>
<SuppressTrimAnalysisWarnings Condition=" '$(SuppressTrimAnalysisWarnings)' == '' ">true</SuppressTrimAnalysisWarnings>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,17 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
</IlcCompileDependsOn>
</PropertyGroup>

<Target Name="_AndroidBeforeIlcCompile" BeforeTargets="SetupProperties">
<Target Name="_AndroidBeforeIlcCompile"
DependsOnTargets="_PrepareLinking"
BeforeTargets="SetupProperties">
<!-- Example settings from: https://github.com/jonathanpeppers/Android-NativeAOT/blob/ea69d122cdc7de67aa6a5db14b7e560763c63cdd/DotNet/libdotnet.targets -->
<PropertyGroup>
<_NdkSysrootAbi>aarch64-linux-android</_NdkSysrootAbi>
<_NdkClangPrefix>aarch64-linux-android21-</_NdkClangPrefix>
<_NdkPrebuiltAbi Condition=" $([MSBuild]::IsOSPlatform('windows')) ">windows-x86_64</_NdkPrebuiltAbi>
<_NdkPrebuiltAbi Condition=" $([MSBuild]::IsOSPlatform('osx')) ">darwin-x86_64</_NdkPrebuiltAbi>
<_NdkPrebuiltAbi Condition=" $([MSBuild]::IsOSPlatform('linux')) ">linux-x86_64</_NdkPrebuiltAbi>
<_NdkSysrootDir>$(_AndroidNdkDirectory)toolchains/llvm/prebuilt/$(_NdkPrebuiltAbi)/sysroot/usr/lib/$(_NdkSysrootAbi)</_NdkSysrootDir>
<_NdkSysrootDir>$(_AndroidNdkDirectory)toolchains/llvm/prebuilt/$(_NdkPrebuiltAbi)/sysroot/usr/lib/$(_NdkSysrootAbi)/</_NdkSysrootDir>
<_NdkBinDir>$(_AndroidNdkDirectory)toolchains/llvm/prebuilt/$(_NdkPrebuiltAbi)/bin/</_NdkBinDir>
<CppCompilerAndLinker>$(_NdkBinDir)$(_NdkClangPrefix)clang++</CppCompilerAndLinker>
<CppLinker>$(CppCompilerAndLinker)</CppLinker>
Expand All @@ -56,6 +58,8 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
<_targetOS>linux</_targetOS>
<!-- HACK: prevents libSystem.Net.Security.Native.a from being added -->
<_linuxLibcFlavor>bionic</_linuxLibcFlavor>
<!-- HACK: prevents: java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "__start___modules" -->
<LinkerFlavor Condition=" '$(LinkerFlavor)' == '' ">lld</LinkerFlavor>
</PropertyGroup>
</Target>

Expand All @@ -76,6 +80,8 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
<IlcReference Include="@(_AndroidILLinkAssemblies)" />
<!-- Passes linked assemblies to outer MSBuild tasks/targets -->
<ResolvedFileToPublish Include="@(IlcCompileInput);@(_AndroidILLinkAssemblies)" RuntimeIdentifier="$(_OriginalRuntimeIdentifier)" />
<!-- Include libc++ -->
<ResolvedFileToPublish Include="$(_NdkSysrootDir)libc++_shared.so" RuntimeIdentifier="$(_OriginalRuntimeIdentifier)" />
</ItemGroup>
</Target>

Expand Down
4 changes: 3 additions & 1 deletion src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,9 @@ IList<string> MergeManifest (NativeCodeGenState codeGenState, Dictionary<string,
var tdCache = new TypeDefinitionCache ();
(List<TypeDefinition> allJavaTypes, List<TypeDefinition> javaTypesForJCW) = ScanForJavaTypes (resolver, tdCache, assemblies, userAssemblies, useMarshalMethods);
var jcwContext = new JCWGeneratorContext (arch, resolver, assemblies.Values, javaTypesForJCW, tdCache, useMarshalMethods);
var jcwGenerator = new JCWGenerator (Log, jcwContext);
var jcwGenerator = new JCWGenerator (Log, jcwContext) {
NativeAot = NativeAot,
};
bool success;

if (generateJavaCode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ public void NativeAOT ()

using var b = CreateApkBuilder ();
Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
b.Output.AssertTargetIsNotSkipped ("_PrepareLinking");

string [] mono_classes = [
"Lmono/MonoRuntimeProvider;",
Expand All @@ -138,11 +139,23 @@ public void NativeAOT ()
];
string [] nativeaot_files = [
$"lib/arm64-v8a/lib{proj.ProjectName}.so",
"lib/arm64-v8a/libc++_shared.so",
];

var intermediate = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, proj.RuntimeIdentifier);
var output = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, proj.RuntimeIdentifier);

var linkedMonoAndroidAssembly = Path.Combine (intermediate, "linked", "Mono.Android.dll");
FileAssert.Exists (linkedMonoAndroidAssembly);
using (var assembly = AssemblyDefinition.ReadAssembly (linkedMonoAndroidAssembly)) {
var typeName = "Android.App.Activity";
var methodName = "GetOnCreate_Landroid_os_Bundle_Handler";
var type = assembly.MainModule.GetType (typeName);
Assert.IsNotNull (type, $"{linkedMonoAndroidAssembly} should contain {typeName}");
var method = type.Methods.FirstOrDefault (m => m.Name == methodName);
Assert.IsNotNull (method, $"{linkedMonoAndroidAssembly} should contain {typeName}.{methodName}");
}

var dexFile = Path.Combine (intermediate, "android", "bin", "classes.dex");
FileAssert.Exists (dexFile);
foreach (var className in mono_classes) {
Expand Down
4 changes: 3 additions & 1 deletion src/Xamarin.Android.Build.Tasks/Utilities/JCWGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class JCWGenerator
readonly TaskLoggingHelper log;
readonly JCWGeneratorContext context;

public bool NativeAot { get; set; }

public MarshalMethodsClassifier? Classifier { get; private set; }

public JCWGenerator (TaskLoggingHelper log, JCWGeneratorContext context)
Expand Down Expand Up @@ -125,7 +127,7 @@ bool GenerateCode (CallableWrapperType generator, TypeDefinition type, string ou
bool ok = true;
using var writer = MemoryStreamPool.Shared.CreateStreamWriter ();
var writer_options = new CallableWrapperWriterOptions {
CodeGenerationTarget = JavaPeerStyle.XAJavaInterop1
CodeGenerationTarget = NativeAot ? JavaPeerStyle.JavaInterop1 : JavaPeerStyle.XAJavaInterop1
};

try {
Expand Down

0 comments on commit 5f4d223

Please sign in to comment.