Skip to content

Commit c929289

Browse files
[One .NET] AOT support (#5539)
Fixes: #6052 Helpful reading: * https://github.com/dotnet/runtime/blob/15dec9a2aa5a4236d6ba70de2e9c146867b9d2e0/src/tasks/AotCompilerTask/MonoAOTCompiler.cs * https://github.com/dotnet/runtime/blob/15dec9a2aa5a4236d6ba70de2e9c146867b9d2e0/src/mono/netcore/nuget/Microsoft.NET.Runtime.MonoAOTCompiler.Task/README.md To make this work, I moved the existing `<Aot/>` MSBuild task calls to a new `_AndroidAot` MSBuild target in `Xamarin.Android.Legacy.targets`. In the .NET 6 targets, there is a *different* `_AndroidAot` MSBuild target that runs the new `<MonoAOTCompiler/>` MSBuild task. The `_AndroidAot` target runs once per `$(RuntimeIdentifier)`, after the linker completes. Native libraries are added to the `@(ResolvedFileToPublish)` item group to be included in the final `.apk`. To follow [convention with Blazor WASM][0], the `$(RunAOTCompilation)` MSBuild property enables AOT. To help with backwards compatibility with "legacy" Xamarin.Android, I kept the `$(AotAssemblies)` MSBuild property intact. Unfortunately, we have to manually import things to support `$(AotAssemblies)`: <ImportGroup Condition=" '$(MonoAOTCompilerTasksAssemblyPath)' == '' and '$(AotAssemblies)' == 'true' "> <Import Project="Sdk.props" Sdk="Microsoft.NET.Runtime.MonoAOTCompiler.Task" /> <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-x86" /> <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-x64" /> <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-arm" /> <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-arm64" /> </ImportGroup> as the [default Mono workload does not support `$(AotAssemblies)`][1]. I think this is reasonable for now. ~~ Limitations ~~ Profiled AOT is not implemented yet: * #6053 `$(EnableLLVM)` fails when locating `opt`: * dotnet/runtime#56386 `$(AndroidClientHandler)` usage causes runtime crash: * dotnet/runtime#56315 Use of folder names like `Build AndÜmläüts` fails: * dotnet/runtime#56163 ~~ Results ~~ All tests: 1. Were running on a [Google Pixel 5][2], and 2. Enabled two architectures, arm64 and x86, and 3. **JIT time** was average of 10 runs with `-c Release`, with the `Activity: Displayed` time, and 4. **AOT time** was average of 10 runs with `-c Release -p:RunAOTCompilation=true` with the `Activity: Displayed` time. 5. Δ values are (AOT / JIT)*100. | Test | JIT time | AOT time | Δ time | JIT apk size | AOT apk size | Δ size | | ------------------- | ------------: | ------------: | ------: | ------------: | ------------: | -----: | | [HelloAndroid][3] | 00:00:00.308 | 00:00:00.209 | 68% | 8,367,969 | 12,082,123 | 144.4% | | [HelloMaui][4] | 00:00:01.117 | 00:00:00.568 | 51% | 16,272,964 | 42,869,016 | 263.4% | [0]: https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-6-preview-4/#blazor-webassembly-ahead-of-time-aot-compilation [1]: https://github.com/dotnet/runtime/blob/69711860262e44458bbe276393ea3eb9f7a2192a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets.in#L20-L25 [2]: https://store.google.com/us/product/pixel_5_specs?hl=en-US [3]: https://github.com/dotnet/maui-samples/tree/714460431541f40570e91225e8ba4bc1fe08025f/HelloAndroid [4]: https://github.com/dotnet/maui-samples/tree/714460431541f40570e91225e8ba4bc1fe08025f/HelloMaui
1 parent 7e1558c commit c929289

File tree

19 files changed

+543
-263
lines changed

19 files changed

+543
-263
lines changed

Documentation/guides/OneDotNet.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,18 @@ It is recommended to migrate to the new linker settings, as
164164
[linker]: https://docs.microsoft.com/dotnet/core/deploying/trimming-options
165165
[linker-full]: https://docs.microsoft.com/dotnet/core/deploying/trimming-options#trimmed-assemblies
166166

167+
## AOT
168+
169+
`$(RunAOTCompilation)` will be the new MSBuild property for enabling
170+
AOT. This is the same property used for [Blazor WASM][blazor].
171+
`$(AotAssemblies)` will also enable AOT, in order to help with
172+
migration from "legacy" Xamarin.Android to .NET 6.
173+
174+
It is recommended to migrate to the new `$(RunAOTCompilation)`
175+
property, as `$(AotAssemblies)` will eventually be deprecated.
176+
177+
[blazor]: https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-6-preview-4/#blazor-webassembly-ahead-of-time-aot-compilation
178+
167179
## dotnet cli
168180

169181
There are currently a few "verbs" we are aiming to get working in

build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateSupportedPlatforms.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ public override bool Execute ()
6464
writer.WriteAttributeString ("Condition", " '$(TargetPlatformVersion)' == '' ");
6565
writer.WriteString (versions.MaxStableVersion.ApiLevel.ToString ("0.0", CultureInfo.InvariantCulture));
6666
writer.WriteEndElement (); // </TargetPlatformVersion>
67+
writer.WriteStartElement ("AndroidMinimumSupportedApiLevel");
68+
writer.WriteAttributeString ("Condition", " '$(AndroidMinimumSupportedApiLevel)' == '' ");
69+
writer.WriteString (MinimumApiLevel.ToString ());
70+
writer.WriteEndElement (); // </AndroidMinimumSupportedApiLevel>
6771
writer.WriteEndElement (); // </PropertyGroup>
6872

6973
writer.WriteStartElement ("ItemGroup");

build-tools/automation/azure-pipelines.yaml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ variables:
4949
# - This is a non-fork branch with name containing "mono-" (for Mono bumps)
5050
IsMonoBranch: $[and(ne(variables['System.PullRequest.IsFork'], 'True'), or(contains(variables['Build.SourceBranchName'], 'mono-'), contains(variables['System.PullRequest.SourceBranch'], 'mono-')))]
5151
RunAllTests: $[or(eq(variables['XA.RunAllTests'], true), eq(variables['IsMonoBranch'], true))]
52-
DotNetNUnitCategories: '& TestCategory != DotNetIgnore & TestCategory != AOT & TestCategory != MkBundle & TestCategory != MonoSymbolicate & TestCategory != PackagesConfig & TestCategory != StaticProject & TestCategory != Debugger & TestCategory != SystemApplication'
52+
DotNetNUnitCategories: '& TestCategory != DotNetIgnore & TestCategory != HybridAOT & TestCategory != ProfiledAOT & TestCategory != LLVM & TestCategory != MkBundle & TestCategory != MonoSymbolicate & TestCategory != PackagesConfig & TestCategory != StaticProject & TestCategory != Debugger & TestCategory != SystemApplication'
5353
NUnit.NumberOfTestWorkers: 4
5454
GitHub.Token: $(github--pat--vs-mobiletools-engineering-service2)
5555
CONVERT_JAVADOC_TO_XMLDOC: $[ne(variables['Build.DefinitionName'], 'Xamarin.Android-PR')]
@@ -798,6 +798,17 @@ stages:
798798
artifactFolder: net6-Interpreter
799799
useDotNet: true
800800

801+
- template: yaml-templates/apk-instrumentation.yaml
802+
parameters:
803+
configuration: $(XA.Build.Configuration)
804+
testName: Mono.Android.NET_Tests-Aot
805+
project: tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj
806+
testResultsFiles: TestResult-Mono.Android.NET_Tests-$(XA.Build.Configuration)Aot.xml
807+
extraBuildArgs: /p:TestsFlavor=Aot /p:RunAOTCompilation=true
808+
artifactSource: bin/Test$(XA.Build.Configuration)/net6.0-android/Mono.Android.NET_Tests-Signed.apk
809+
artifactFolder: net6-aot
810+
useDotNet: true
811+
801812
- task: MSBuild@1
802813
displayName: shut down emulator
803814
inputs:

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ This file is imported *after* the Microsoft.NET.Sdk/Sdk.targets.
1818
<Import Project="..\tools\Xamarin.Android.Bindings.Core.targets" />
1919
<Import Project="..\tools\Xamarin.Android.Bindings.ClassParse.targets" />
2020
<Import Project="Microsoft.Android.Sdk.AndroidLibraries.targets" />
21+
<Import Project="Microsoft.Android.Sdk.Aot.targets" Condition=" '$(AndroidApplication)' == 'true' " />
2122
<Import Project="Microsoft.Android.Sdk.Application.targets" Condition=" '$(AndroidApplication)' == 'true' " />
2223
<Import Project="Microsoft.Android.Sdk.AssemblyResolution.targets" />
2324
<Import Project="Microsoft.Android.Sdk.ILLink.targets" />
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<!--
2+
***********************************************************************************************
3+
Microsoft.Android.Sdk.Aot.targets
4+
5+
.NET 6 AOT support. You can find "legacy" Xamarin.Android AOT support
6+
in Xamarin.Android.Legacy.targets.
7+
8+
For <MonoAOTCompiler/> usage, see:
9+
* https://github.com/dotnet/runtime/blob/15dec9a2aa5a4236d6ba70de2e9c146867b9d2e0/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
10+
* https://github.com/dotnet/runtime/blob/15dec9a2aa5a4236d6ba70de2e9c146867b9d2e0/src/mono/netcore/nuget/Microsoft.NET.Runtime.MonoAOTCompiler.Task/README.md
11+
12+
These targets are running within the _ComputeFilesToPublishForRuntimeIdentifiers target.
13+
They run in a context of an inner build with a single $(RuntimeIdentifier).
14+
15+
***********************************************************************************************
16+
-->
17+
<Project>
18+
19+
<!--
20+
NOTE: currently, the only way to allow $(AotAssemblies) in
21+
.csproj files is to import these in the Android workload
22+
when $(MonoAOTCompilerTasksAssemblyPath) is blank:
23+
https://github.com/dotnet/runtime/blob/69711860262e44458bbe276393ea3eb9f7a2192a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets.in#L20-L25
24+
-->
25+
<ImportGroup Condition=" '$(MonoAOTCompilerTasksAssemblyPath)' == '' and '$(AotAssemblies)' == 'true' ">
26+
<Import Project="Sdk.props" Sdk="Microsoft.NET.Runtime.MonoAOTCompiler.Task" />
27+
<Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-x86" />
28+
<Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-x64" />
29+
<Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-arm" />
30+
<Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-arm64" />
31+
</ImportGroup>
32+
33+
<UsingTask TaskName="Xamarin.Android.Tasks.GetAotArguments" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" />
34+
35+
<Target Name="_AndroidAotInputs">
36+
<ItemGroup>
37+
<_AndroidAotInputs Include="@(ResolvedFileToPublish)" Condition=" '%(Extension)' == '.dll' " />
38+
</ItemGroup>
39+
</Target>
40+
41+
<Target Name="_AndroidAot"
42+
Condition=" '$(AotAssemblies)' == 'true' and '$(RuntimeIdentifier)' != '' "
43+
DependsOnTargets="_AndroidAotInputs"
44+
Inputs="@(_AndroidAotInputs)"
45+
Outputs="$(_AndroidStampDirectory)_AndroidAot.stamp">
46+
<GetAotArguments
47+
AndroidAotMode="$(AndroidAotMode)"
48+
AndroidNdkDirectory="$(_AndroidNdkDirectory)"
49+
AndroidBinUtilsDirectory="$(AndroidBinUtilsDirectory)"
50+
AndroidApiLevel="$(_AndroidApiLevel)"
51+
MinimumSupportedApiLevel="$(AndroidMinimumSupportedApiLevel)"
52+
AndroidSequencePointsMode="$(_SequencePointsMode)"
53+
AotAdditionalArguments="$(AndroidAotAdditionalArguments)"
54+
AotOutputDirectory="$(_AndroidAotBinDirectory)"
55+
RuntimeIdentifier="$(RuntimeIdentifier)"
56+
EnableLLVM="$(EnableLLVM)"
57+
Profiles="@(_AotProfiles)">
58+
<Output PropertyName="_AotArguments" TaskParameter="Arguments" />
59+
<Output PropertyName="_LLVMPath" TaskParameter="LLVMPath" />
60+
</GetAotArguments>
61+
<ItemGroup>
62+
<_MonoAOTAssemblies Include="@(_AndroidAotInputs->'%(FullPath)')" AotArguments="$(_AotArguments)" />
63+
</ItemGroup>
64+
<MakeDir Directories="$(IntermediateOutputPath)aot\" />
65+
<MonoAOTCompiler
66+
Assemblies="@(_MonoAOTAssemblies)"
67+
CompilerBinaryPath="@(MonoAotCrossCompiler->WithMetadataValue('RuntimeIdentifier', '$(RuntimeIdentifier)'))"
68+
DisableParallelAot="$(_DisableParallelAot)"
69+
LibraryFormat="So"
70+
Mode="$(AndroidAotMode)"
71+
OutputDir="$(IntermediateOutputPath)aot\"
72+
OutputType="Library"
73+
UseAotDataFile="false"
74+
UseLLVM="$(EnableLLVM)"
75+
LLVMPath="$(_LLVMPath)">
76+
<Output TaskParameter="CompiledAssemblies" ItemName="_AotCompiledAssemblies" />
77+
<Output TaskParameter="FileWrites" ItemName="FileWrites" />
78+
</MonoAOTCompiler>
79+
<Touch Files="$(_AndroidStampDirectory)_AndroidAot.stamp" AlwaysCreate="true" />
80+
<ItemGroup>
81+
<ResolvedFileToPublish
82+
Include="@(_AotCompiledAssemblies->'%(LibraryFile)')"
83+
ArchiveFileName="libaot-$([System.IO.Path]::GetFileNameWithoutExtension('%(_AotCompiledAssemblies.LibraryFile)')).so"
84+
/>
85+
</ItemGroup>
86+
</Target>
87+
</Project>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ _ResolveAssemblies MSBuild target.
3131
</PropertyGroup>
3232

3333
<Target Name="_ComputeFilesToPublishForRuntimeIdentifiers"
34-
DependsOnTargets="_FixupIntermediateAssembly;ResolveReferences;ComputeFilesToPublish"
34+
DependsOnTargets="_FixupIntermediateAssembly;ResolveReferences;ComputeFilesToPublish;_AndroidAot"
3535
Returns="@(ResolvedFileToPublish)">
3636
<ItemGroup>
3737
<ResolvedFileToPublish

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@
6868
<AndroidManifest Condition=" '$(AndroidManifest)' == '' and Exists ('Properties\AndroidManifest.xml') and !Exists ('AndroidManifest.xml') ">Properties\AndroidManifest.xml</AndroidManifest>
6969
<AndroidManifest Condition=" '$(AndroidManifest)' == '' ">AndroidManifest.xml</AndroidManifest>
7070
<GenerateApplicationManifest Condition=" '$(GenerateApplicationManifest)' == '' ">true</GenerateApplicationManifest>
71+
<RunAOTCompilation Condition=" '$(RunAOTCompilation)' == '' and '$(AotAssemblies)' == 'true' ">true</RunAOTCompilation>
72+
<RunAOTCompilation Condition=" '$(RunAOTCompilation)' == '' ">false</RunAOTCompilation>
73+
<AotAssemblies>$(RunAOTCompilation)</AotAssemblies>
7174

7275
<!--
7376
Runtime libraries feature switches defaults

0 commit comments

Comments
 (0)