Skip to content

Commit

Permalink
[NativeAOT] improve build logic, part 1 (#9614)
Browse files Browse the repository at this point in the history
Introduce `$(_AndroidNativeAot)` to be used throughout a build to
know if it is a NativeAOT build.  The logic for detecting a NativeAOT
build will likely change for Debug vs Release in the future.

Skip generation of `mono.MonoRuntimeProvider` for NativeAOT and
don't emit the value in `AndroidManifest.xml`.

Update `Microsoft.Android.Sdk.NativeAOT.targets` to use
`@(LinkerArg)` and `%(ResolvedFileToPublish.ArchiveFileName)` to
ensure that the NativeAOT-generated native library has a `lib` prefix,
as `System.loadLibrary("@name@")` only looks for `lib@NAME@.so`.

Update `BuildTest2.NativeAOT()` to ensure that the native library
`libHello.so` is created, and that `classes.dex` does NOT contain
`mono.MonoRuntimeProvider`.
  • Loading branch information
jonathanpeppers authored Dec 18, 2024
1 parent 0d4c1cb commit 8d77130
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
<_AndroidTargetingPackId Condition=" '$(_AndroidTargetingPackId)' != '$(_AndroidLatestStableApiLevel)' and '$(_AndroidTargetingPackId)' != '$(_AndroidLatestUnstableApiLevel)' ">$(_AndroidLatestStableApiLevel)</_AndroidTargetingPackId>
<_AndroidRuntimePackId Condition=" '$(_AndroidRuntimePackId)' == '' ">$(_AndroidTargetingPackId)</_AndroidRuntimePackId>
<_AndroidRuntimePackId Condition=" '$(_AndroidRuntimePackId)' != '$(_AndroidLatestStableApiLevel)' and '$(_AndroidRuntimePackId)' != '$(_AndroidLatestUnstableApiLevel)' ">$(_AndroidLatestStableApiLevel)</_AndroidRuntimePackId>
<_AndroidRuntimePackRuntime Condition=" '$(PublishAot)' == 'true' ">NativeAOT</_AndroidRuntimePackRuntime>
<_AndroidRuntimePackRuntime Condition=" '$(_AndroidRuntimePackRuntime)' == '' ">Mono</_AndroidRuntimePackRuntime>
</PropertyGroup>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@
See: https://github.com/dotnet/sdk/blob/955c0fc7b06e2fa34bacd076ed39f61e4fb61716/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets#L16
-->
<_GetChildProjectCopyToPublishDirectoryItems>false</_GetChildProjectCopyToPublishDirectoryItems>
<UseMonoRuntime Condition=" '$(PublishAot)' == 'true' and '$(UseMonoRuntime)' == '' ">false</UseMonoRuntime>
<!-- Define a $(_AndroidNativeAot) property, as the logic detecting NativeAOT may change in the future -->
<_AndroidNativeAot Condition=" '$(PublishAot)' == 'true' ">true</_AndroidNativeAot>
<_AndroidNativeAot Condition=" '$(_AndroidNativeAot)' == '' ">false</_AndroidNativeAot>
<UseMonoRuntime Condition=" '$(_AndroidNativeAot)' == 'true' and '$(UseMonoRuntime)' == '' ">false</UseMonoRuntime>
<UseMonoRuntime Condition=" '$(UseMonoRuntime)' == '' ">true</UseMonoRuntime>
<!-- HACK: make dotnet restore include Microsoft.NETCore.App.Runtime.NativeAOT.linux-bionic-arm64 -->
<_IsPublishing Condition=" '$(_IsPublishing)' == '' and '$(PublishAot)' == 'true' ">true</_IsPublishing>
<_IsPublishing Condition=" '$(_IsPublishing)' == '' and '$(_AndroidNativeAot)' == 'true' ">true</_IsPublishing>

<!-- Use $(AndroidMinimumSupportedApiLevel) for $(SupportedOSPlatformVersion) if unset -->
<SupportedOSPlatformVersion Condition=" '$(SupportedOSPlatformVersion)' == '' ">$(AndroidMinimumSupportedApiLevel)</SupportedOSPlatformVersion>
Expand Down Expand Up @@ -94,7 +97,7 @@
<RuntimeIdentifier Condition=" '$(RuntimeIdentifiers)' != '' And '$(RuntimeIdentifier)' != '' " />
<GenerateApplicationManifest Condition=" '$(GenerateApplicationManifest)' == '' ">true</GenerateApplicationManifest>
<!-- Default to Mono's AOT in Release mode -->
<RunAOTCompilation Condition=" '$(RunAOTCompilation)' == '' and '$(AotAssemblies)' == '' and '$(Configuration)' == 'Release' and '$(PublishAot)' != 'true' ">true</RunAOTCompilation>
<RunAOTCompilation Condition=" '$(RunAOTCompilation)' == '' and '$(AotAssemblies)' == '' and '$(Configuration)' == 'Release' and '$(_AndroidNativeAot)' != 'true' ">true</RunAOTCompilation>
<RunAOTCompilation Condition=" '$(RunAOTCompilation)' == '' and '$(AotAssemblies)' == 'true' ">true</RunAOTCompilation>
<RunAOTCompilation Condition=" '$(RunAOTCompilation)' == '' ">false</RunAOTCompilation>
<_AndroidXA1029 Condition=" '$(AotAssemblies)' != '' ">true</_AndroidXA1029>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
-->
<Project>

<!-- Default property values for NativeAOT -->
<PropertyGroup>
<_AndroidRuntimePackRuntime>NativeAOT</_AndroidRuntimePackRuntime>
</PropertyGroup>

<!-- Make IlcCompile depend on the trimmer -->
<PropertyGroup>
<IlcCompileDependsOn>
Expand Down Expand Up @@ -60,12 +65,17 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
<SuppressTrimAnalysisWarnings>$(_OriginalSuppressTrimAnalysisWarnings)</SuppressTrimAnalysisWarnings>
</PropertyGroup>
<ItemGroup>
<!-- Android needs a proper soname property or it will refuse to load the library -->
<LinkerArg Include="-Wl,-soname,lib$(TargetName)$(NativeBinaryExt)" />
<!-- Give ILLink's output to ILC -->
<IlcCompileInput Remove="@(IlcCompileInput)" />
<IlcCompileInput Include="$(IntermediateLinkDir)$(TargetName)$(TargetExt)" />
<_AndroidILLinkAssemblies Include="@(ManagedAssemblyToLink->'$(IntermediateLinkDir)%(Filename)%(Extension)')" Condition="Exists('$(IntermediateLinkDir)%(Filename)%(Extension)')" />
<IlcReference Remove="@(IlcReference)" />
<IlcReference Include="@(PrivateSdkAssemblies)" />
<IlcReference Include="@(ManagedAssemblyToLink->'$(IntermediateLinkDir)%(Filename)%(Extension)')" Condition="Exists('$(IntermediateLinkDir)%(Filename)%(Extension)')" Exclude="@(IlcCompileInput)" />
<IlcReference Include="@(_AndroidILLinkAssemblies)" />
<!-- Passes linked assemblies to outer MSBuild tasks/targets -->
<ResolvedFileToPublish Include="@(IlcCompileInput);@(_AndroidILLinkAssemblies)" RuntimeIdentifier="$(_OriginalRuntimeIdentifier)" />
</ItemGroup>
</Target>

Expand All @@ -76,4 +86,11 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
</PropertyGroup>
</Target>

<Target Name="_AndroidFixNativeLibraryFileName" AfterTargets="ComputeFilesToPublish">
<ItemGroup>
<!-- Fix paths to contain lib-prefix -->
<ResolvedFileToPublish Update="@(ResolvedFileToPublish)" ArchiveFileName="lib%(FileName)%(Extension)" Condition=" '%(Filename)%(Extension)' == '$(TargetName)$(NativeBinaryExt)' " />
</ItemGroup>
</Target>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@
<Import Project="Microsoft.Android.Sdk.DefaultProperties.targets" />
<Import Project="$(MSBuildThisFileDirectory)..\tools\Xamarin.Android.Common.Debugging.props"
Condition="Exists('$(MSBuildThisFileDirectory)..\tools\Xamarin.Android.Common.Debugging.props')"/>
<Import Project="Microsoft.Android.Sdk.NativeAOT.targets" Condition=" '$(PublishAot)' == 'true' " />
<Import Project="Microsoft.Android.Sdk.NativeAOT.targets" Condition=" '$(_AndroidNativeAot)' == 'true' " />

</Project>
8 changes: 8 additions & 0 deletions src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ public class GenerateJavaStubs : AndroidTask
[Output]
public ITaskItem[] GeneratedBinaryTypeMaps { get; set; }

public bool NativeAot { get; set; }

internal const string AndroidSkipJavaStubGeneration = "AndroidSkipJavaStubGeneration";

public override bool RunTask ()
Expand Down Expand Up @@ -294,6 +296,11 @@ Dictionary<string, ITaskItem> MaybeGetArchAssemblies (Dictionary<AndroidTargetAr

void GenerateAdditionalProviderSources (NativeCodeGenState codeGenState, IList<string> additionalProviders)
{
if (NativeAot) {
Log.LogDebugMessage ("Skipping MonoRuntimeProvider generation for NativeAot");
return;
}

// Create additional runtime provider java sources.
string providerTemplateFile = "MonoRuntimeProvider.Bundled.java";
string providerTemplate = GetResource (providerTemplateFile);
Expand Down Expand Up @@ -347,6 +354,7 @@ IList<string> MergeManifest (NativeCodeGenState codeGenState, Dictionary<string,
Debug = Debug,
MultiDex = MultiDex,
NeedsInternet = NeedsInternet,
NativeAot = NativeAot,
};
// Only set manifest.VersionCode if there is no existing value in AndroidManifest.xml.
if (manifest.HasVersionCode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,18 +115,49 @@ public void NativeAOT ()
}

var proj = new XamarinAndroidApplicationProject {
ProjectName = "Hello",
IsRelease = true,
RuntimeIdentifier = "android-arm64",
// Add locally downloaded NativeAOT packs
ExtraNuGetConfigSources = {
Path.Combine (XABuildPaths.BuildOutputDirectory, "nuget-unsigned"),
}
};
proj.SetRuntimeIdentifier ("arm64-v8a");
proj.SetProperty ("PublishAot", "true");
proj.SetProperty ("PublishAotUsingRuntimePack", "true");
proj.SetProperty ("AndroidNdkDirectory", AndroidNdkPath);

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

string [] mono_classes = [
"Lmono/MonoRuntimeProvider;",
];
string[] mono_files = [
"lib/arm64-v8a/libmonosgen-2.0.so",
];
string [] nativeaot_files = [
$"lib/arm64-v8a/lib{proj.ProjectName}.so",
];

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

var dexFile = Path.Combine (intermediate, "android", "bin", "classes.dex");
FileAssert.Exists (dexFile);
foreach (var className in mono_classes) {
Assert.IsFalse (DexUtils.ContainsClassWithMethod (className, "<init>", "()V", dexFile, AndroidSdkPath), $"`{dexFile}` should *not* include `{className}`!");
}

var apkFile = Path.Combine (output, $"{proj.PackageName}-Signed.apk");
FileAssert.Exists (apkFile);
using var zip = ZipHelper.OpenZip (apkFile);
foreach (var mono_file in mono_files) {
Assert.IsFalse (zip.ContainsEntry (mono_file, caseSensitive: true), $"APK must *not* contain `{mono_file}`.");
}
foreach (var nativeaot_file in nativeaot_files) {
Assert.IsTrue (zip.ContainsEntry (nativeaot_file, caseSensitive: true), $"APK must contain `{nativeaot_file}`.");
}
}

[Test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ public bool UseJackAndJill {
set { SetProperty (KnownProperties.UseJackAndJill, value.ToString ()); }
}

public string RuntimeIdentifier {
get { return GetProperty (KnownProperties.RuntimeIdentifier); }
set { SetProperty (KnownProperties.RuntimeIdentifier, value); }
}

public AndroidLinkMode AndroidLinkModeDebug {
get {
AndroidLinkMode m;
Expand Down
6 changes: 6 additions & 0 deletions src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ internal class ManifestDocument
public bool ForceDebuggable { get; set; }
public string VersionName { get; set; }
public IVersionResolver VersionResolver { get; set; } = new MonoAndroidHelperVersionResolver ();
public bool NativeAot { get; set; }

string versionCode;

Expand Down Expand Up @@ -672,6 +673,11 @@ XElement CreateApplicationElement (XElement manifest, string applicationClass, L

IList<string> AddMonoRuntimeProviders (XElement app)
{
if (NativeAot) {
//TODO: implement NativeAOT provider logic
return [];
}

app.Add (CreateMonoRuntimeProvider ("mono.MonoRuntimeProvider", null, --AppInitOrder));

var providerNames = new List<string> ();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,9 +329,9 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
<AndroidUseAssemblyStore Condition=" '$(AndroidUseAssemblyStore)' == '' ">true</AndroidUseAssemblyStore>
<AndroidAotEnableLazyLoad Condition=" '$(AndroidAotEnableLazyLoad)' == '' And '$(AotAssemblies)' == 'true' And '$(AndroidIncludeDebugSymbols)' != 'true' ">True</AndroidAotEnableLazyLoad>
<AndroidEnableMarshalMethods Condition=" '$(AndroidEnableMarshalMethods)' == '' and ('$(UsingMicrosoftNETSdkRazor)' == 'true') ">False</AndroidEnableMarshalMethods>
<AndroidEnableMarshalMethods Condition=" '$(AndroidEnableMarshalMethods)' == '' and '$(PublishAot)' != 'true' ">True</AndroidEnableMarshalMethods>
<AndroidEnableMarshalMethods Condition=" '$(AndroidEnableMarshalMethods)' == '' and '$(_AndroidNativeAot)' != 'true' ">True</AndroidEnableMarshalMethods>
<!-- NOTE: temporarily disable for NativeAOT for now, to get build passing -->
<AndroidEnableMarshalMethods Condition=" '$(AndroidEnableMarshalMethods)' == '' and '$(PublishAot)' == 'true' ">False</AndroidEnableMarshalMethods>
<AndroidEnableMarshalMethods Condition=" '$(AndroidEnableMarshalMethods)' == '' and '$(_AndroidNativeAot)' == 'true' ">False</AndroidEnableMarshalMethods>
<_AndroidUseMarshalMethods Condition=" '$(AndroidIncludeDebugSymbols)' == 'True' ">False</_AndroidUseMarshalMethods>
<_AndroidUseMarshalMethods Condition=" '$(AndroidIncludeDebugSymbols)' != 'True' ">$(AndroidEnableMarshalMethods)</_AndroidUseMarshalMethods>
</PropertyGroup>
Expand Down Expand Up @@ -1379,6 +1379,7 @@ because xbuild doesn't support framework reference assemblies.
DependsOnTargets="_CollectRuntimeJarFilenames;$(_BeforeAddStaticResources);_GetMonoPlatformJarPath">
<CopyResource ResourceName="machine.config" OutputPath="$(MonoAndroidIntermediateAssemblyDir)machine.config" />
<CopyResource
Condition=" '$(_AndroidNativeAot)' != 'true' "
ResourceName="MonoRuntimeProvider.Bundled.java"
OutputPath="$(_AndroidIntermediateJavaSourceDirectory)mono\MonoRuntimeProvider.java"
/>
Expand Down Expand Up @@ -1497,6 +1498,7 @@ because xbuild doesn't support framework reference assemblies.
</ItemGroup>

<GenerateJavaStubs
NativeAot="$(_AndroidNativeAot)"
ResolvedAssemblies="@(_ResolvedAssemblies)"
ResolvedUserAssemblies="@(_ResolvedUserMonoAndroidAssemblies)"
ErrorOnCustomJavaObject="$(AndroidErrorOnCustomJavaObject)"
Expand Down Expand Up @@ -1726,6 +1728,7 @@ because xbuild doesn't support framework reference assemblies.
</Target>

<Target Name="_GeneratePackageManagerJava"
Condition=" '$(_AndroidNativeAot)' != 'true' "
DependsOnTargets="$(_GeneratePackageManagerJavaDependsOn)"
Inputs="@(_GeneratePackageManagerJavaInputs)"
Outputs="$(_AndroidStampDirectory)_GeneratePackageManagerJava.stamp">
Expand Down Expand Up @@ -1944,7 +1947,7 @@ because xbuild doesn't support framework reference assemblies.

<!-- Shrink Mono.Android.dll by removing attribute only needed for GenerateJavaStubs -->
<RemoveRegisterAttribute
Condition="'$(AndroidLinkMode)' != 'None' and '$(AndroidIncludeDebugSymbols)' != 'true' and '$(AndroidStripILAfterAOT)' != 'true' and '$(PublishAot)' != 'true' "
Condition="'$(AndroidLinkMode)' != 'None' and '$(AndroidIncludeDebugSymbols)' != 'true' and '$(AndroidStripILAfterAOT)' != 'true' and '$(_AndroidNativeAot)' != 'true' "
ShrunkFrameworkAssemblies="@(_ShrunkAssemblies)" />

<MakeDir Directories="$(MonoAndroidIntermediateAssemblyDir)shrunk" />
Expand Down

0 comments on commit 8d77130

Please sign in to comment.