Skip to content

Commit 5f4d223

Browse files
[NativeAOT] improve build logic, part 2 (#9631)
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/
1 parent df9ab24 commit 5f4d223

File tree

5 files changed

+28
-5
lines changed

5 files changed

+28
-5
lines changed

src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
<AndroidLinkMode Condition=" '$(AndroidLinkMode)' == '' and '$(PublishTrimmed)' == 'true' ">SdkOnly</AndroidLinkMode>
8989
<AndroidLinkMode Condition=" '$(AndroidLinkMode)' == '' ">None</AndroidLinkMode>
9090
<!-- For compat with user code not marked trimmable, only trim opt-in by default. -->
91-
<TrimMode Condition=" '$(TrimMode)' == '' and '$(AndroidLinkMode)' == 'Full' ">full</TrimMode>
91+
<TrimMode Condition=" '$(TrimMode)' == '' and ('$(AndroidLinkMode)' == 'Full' or '$(_AndroidNativeAot)' == 'true') ">full</TrimMode>
9292
<TrimMode Condition="'$(TrimMode)' == ''">partial</TrimMode>
9393
<SuppressTrimAnalysisWarnings Condition=" '$(SuppressTrimAnalysisWarnings)' == '' and ('$(TrimMode)' == 'full' or '$(IsAotCompatible)' == 'true') ">false</SuppressTrimAnalysisWarnings>
9494
<SuppressTrimAnalysisWarnings Condition=" '$(SuppressTrimAnalysisWarnings)' == '' ">true</SuppressTrimAnalysisWarnings>

src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,17 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
2525
</IlcCompileDependsOn>
2626
</PropertyGroup>
2727

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

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

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,9 @@ IList<string> MergeManifest (NativeCodeGenState codeGenState, Dictionary<string,
387387
var tdCache = new TypeDefinitionCache ();
388388
(List<TypeDefinition> allJavaTypes, List<TypeDefinition> javaTypesForJCW) = ScanForJavaTypes (resolver, tdCache, assemblies, userAssemblies, useMarshalMethods);
389389
var jcwContext = new JCWGeneratorContext (arch, resolver, assemblies.Values, javaTypesForJCW, tdCache, useMarshalMethods);
390-
var jcwGenerator = new JCWGenerator (Log, jcwContext);
390+
var jcwGenerator = new JCWGenerator (Log, jcwContext) {
391+
NativeAot = NativeAot,
392+
};
391393
bool success;
392394

393395
if (generateJavaCode) {

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ public void NativeAOT ()
129129

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

133134
string [] mono_classes = [
134135
"Lmono/MonoRuntimeProvider;",
@@ -138,11 +139,23 @@ public void NativeAOT ()
138139
];
139140
string [] nativeaot_files = [
140141
$"lib/arm64-v8a/lib{proj.ProjectName}.so",
142+
"lib/arm64-v8a/libc++_shared.so",
141143
];
142144

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

148+
var linkedMonoAndroidAssembly = Path.Combine (intermediate, "linked", "Mono.Android.dll");
149+
FileAssert.Exists (linkedMonoAndroidAssembly);
150+
using (var assembly = AssemblyDefinition.ReadAssembly (linkedMonoAndroidAssembly)) {
151+
var typeName = "Android.App.Activity";
152+
var methodName = "GetOnCreate_Landroid_os_Bundle_Handler";
153+
var type = assembly.MainModule.GetType (typeName);
154+
Assert.IsNotNull (type, $"{linkedMonoAndroidAssembly} should contain {typeName}");
155+
var method = type.Methods.FirstOrDefault (m => m.Name == methodName);
156+
Assert.IsNotNull (method, $"{linkedMonoAndroidAssembly} should contain {typeName}.{methodName}");
157+
}
158+
146159
var dexFile = Path.Combine (intermediate, "android", "bin", "classes.dex");
147160
FileAssert.Exists (dexFile);
148161
foreach (var className in mono_classes) {

src/Xamarin.Android.Build.Tasks/Utilities/JCWGenerator.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ class JCWGenerator
4444
readonly TaskLoggingHelper log;
4545
readonly JCWGeneratorContext context;
4646

47+
public bool NativeAot { get; set; }
48+
4749
public MarshalMethodsClassifier? Classifier { get; private set; }
4850

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

131133
try {

0 commit comments

Comments
 (0)