Skip to content

Commit 482210f

Browse files
[One .NET] AOT support
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 TODO / limitations: * We only have the osx-x64 host packages * I have not updated the installers yet. The AOT packs are placed in `~/android-toolchain/dotnet` for local testing. * Many other `TODO` comments 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. In order to acquire the AOT packages, I created a new `mono-aot-compiler.proj` that is invoked such as: <Exec Command="$(DotNetPreviewTool) build @(_GlobalProperties, ' ') &quot;$(MSBuildThisFileDirectory)..\mono-aot-compiler\mono-aot-compiler.proj&quot;" EnvironmentVariables="NUGET_PACKAGES=$(DotNetPreviewPath)packs" /> Setting `$NUGET_PACKAGES` allows us to extract the AOT NuGet packages directly to `dotnet/packs` when this project is restored. Then we conditionally import the Sdk: <Import Project="..\build\Microsoft.NET.Runtime.MonoAOTCompiler.Task.props" Sdk="Microsoft.NET.Runtime.MonoAOTCompiler.Task" Condition=" '$(AotAssemblies)' == 'true' " /> Which allows us to use `$(MonoAOTCompilerTaskAssemblyPath)`: <UsingTask Condition=" '$(AotAssemblies)' == 'true' " TaskName="MonoAOTCompiler" AssemblyFile="$(MonoAOTCompilerTaskAssemblyPath)" /> I still need to add the new packs to our installers.
1 parent c677a16 commit 482210f

File tree

7 files changed

+403
-271
lines changed

7 files changed

+403
-271
lines changed

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: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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+
***********************************************************************************************
13+
-->
14+
<Project>
15+
16+
<UsingTask TaskName="Xamarin.Android.Tasks.GetAotArguments" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" />
17+
18+
<Target Name="_AndroidAot"
19+
Condition=" '$(RunAOTCompilation)' == 'true' "
20+
Inputs="$(_BuildApkEmbedInputs)"
21+
Outputs="$(_BuildApkEmbedOutputs)">
22+
23+
<!--TODO: $(MonoAotCrossCompilerPath) is only set to the arm64 path -->
24+
<Error
25+
Condition=" '$(RuntimeIdentifier)' != 'android-arm64' "
26+
Text=" AOT is currently only supported when %24(RuntimeIdentifier) is set to 'android-arm64'."
27+
/>
28+
29+
<GetAotArguments
30+
AndroidAotMode="$(AndroidAotMode)"
31+
AndroidNdkDirectory="$(_AndroidNdkDirectory)"
32+
AndroidBinUtilsDirectory="$(AndroidBinUtilsDirectory)"
33+
AndroidApiLevel="$(_AndroidApiLevel)"
34+
ManifestFile="$(IntermediateOutputPath)android\AndroidManifest.xml"
35+
AndroidSequencePointsMode="$(_SequencePointsMode)"
36+
AotAdditionalArguments="$(AndroidAotAdditionalArguments)"
37+
AotOutputDirectory="$(_AndroidAotBinDirectory)"
38+
RuntimeIdentifier="$(RuntimeIdentifier)"
39+
EnableLLVM="$(EnableLLVM)"
40+
Profiles="@(_AotProfiles)">
41+
<Output PropertyName="_AotArguments" TaskParameter="Arguments" />
42+
</GetAotArguments>
43+
<ItemGroup>
44+
<_MonoAOTAssemblies Include="@(_ShrunkAssemblies->'%(FullPath)')" AotArguments="$(_AotArguments)" />
45+
</ItemGroup>
46+
<MakeDir Directories="$(IntermediateOutputPath)aot\$(RuntimeIdentifier)\" />
47+
<!--
48+
TODO:
49+
* I set every [Input] with a non-existent property.
50+
* AotProfilePath is a single string, not sure how to pass @(_AotProfiles)?
51+
-->
52+
<MonoAOTCompiler
53+
AotProfilePath="$(_AotProfilePath)"
54+
Assemblies="@(_MonoAOTAssemblies)"
55+
CompilerBinaryPath="$(MonoAotCrossCompilerPath)"
56+
DisableParallelAot="$(_DisableParallelAot)"
57+
LLVMPath="$(_LLVMPath)"
58+
Mode="$(AndroidAotMode)"
59+
OutputDir="$(IntermediateOutputPath)aot\$(RuntimeIdentifier)\"
60+
Profilers="@(_AotProfiles)"
61+
UseAotDataFile="$(_UseAotDataFile)"
62+
UseLLVM="$(EnableLLVM)">
63+
<Output TaskParameter="CompiledAssemblies" ItemName="_AotCompiledAssemblies" />
64+
<Output TaskParameter="FileWrites" ItemName="FileWrites" />
65+
</MonoAOTCompiler>
66+
</Target>
67+
</Project>

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

Lines changed: 4 additions & 208 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Collections.Specialized;
43
using System.Diagnostics;
54
using System.IO;
65
using System.Linq;
7-
using System.Reflection;
86
using System.Text;
97
using System.Threading;
108
using Microsoft.Build.Framework;
@@ -33,111 +31,29 @@ public enum SequencePointsMode {
3331
}
3432

3533
// can't be a single ToolTask, because it has to run mkbundle many times for each arch.
36-
public class Aot : AndroidAsyncTask
34+
public class Aot : GetAotArguments
3735
{
3836
public override string TaskPrefix => "AOT";
3937

40-
[Required]
41-
public string AndroidAotMode { get; set; }
42-
43-
public string AndroidNdkDirectory { get; set; }
44-
45-
[Required]
46-
public string AndroidApiLevel { get; set; }
47-
48-
[Required]
49-
public ITaskItem ManifestFile { get; set; }
50-
5138
[Required]
5239
public ITaskItem[] ResolvedAssemblies { get; set; }
5340

5441
// Which ABIs to include native libs for
5542
[Required]
5643
public string [] SupportedAbis { get; set; }
5744

58-
[Required]
59-
public string AotOutputDirectory { get; set; }
60-
6145
[Required]
6246
public string IntermediateAssemblyDir { get; set; }
6347

6448
public string LinkMode { get; set; }
6549

66-
public bool EnableLLVM { get; set; }
67-
68-
public string AndroidSequencePointsMode { get; set; }
69-
70-
public string AotAdditionalArguments { get; set; }
71-
7250
public ITaskItem[] AdditionalNativeLibraryReferences { get; set; }
7351

7452
public string ExtraAotOptions { get; set; }
7553

76-
public ITaskItem [] Profiles { get; set; }
77-
78-
[Required]
79-
public string AndroidBinUtilsDirectory { get; set; }
80-
8154
[Output]
8255
public string[] NativeLibrariesReferences { get; set; }
8356

84-
AotMode AotMode;
85-
SequencePointsMode sequencePointsMode;
86-
87-
public override bool RunTask ()
88-
{
89-
// NdkUtil must always be initialized - once per thread
90-
if (!NdkUtil.Init (LogCodedError, AndroidNdkDirectory))
91-
return false;
92-
93-
return base.RunTask ();
94-
}
95-
96-
public static bool GetAndroidAotMode(string androidAotMode, out AotMode aotMode)
97-
{
98-
aotMode = AotMode.Normal;
99-
100-
switch ((androidAotMode ?? string.Empty).ToLowerInvariant().Trim())
101-
{
102-
case "":
103-
case "none":
104-
aotMode = AotMode.None;
105-
return true;
106-
case "normal":
107-
aotMode = AotMode.Normal;
108-
return true;
109-
case "hybrid":
110-
aotMode = AotMode.Hybrid;
111-
return true;
112-
case "full":
113-
aotMode = AotMode.Full;
114-
return true;
115-
case "interpreter":
116-
aotMode = AotMode.Interp;
117-
return true; // We don't do anything here for this mode, this is just to set the flag for the XA
118-
// runtime to initialize Mono in the inrepreter "AOT" mode.
119-
}
120-
121-
return false;
122-
}
123-
124-
public static bool TryGetSequencePointsMode (string value, out SequencePointsMode mode)
125-
{
126-
mode = SequencePointsMode.None;
127-
switch ((value ?? string.Empty).ToLowerInvariant().Trim ()) {
128-
case "none":
129-
mode = SequencePointsMode.None;
130-
return true;
131-
case "normal":
132-
mode = SequencePointsMode.Normal;
133-
return true;
134-
case "offline":
135-
mode = SequencePointsMode.Offline;
136-
return true;
137-
}
138-
return false;
139-
}
140-
14157
static string GetNdkToolchainLibraryDir(string binDir, string archDir = null)
14258
{
14359
var baseDir = Path.GetFullPath(Path.Combine(binDir, ".."));
@@ -183,51 +99,6 @@ static string QuoteFileName(string fileName)
18399
return builder.ToString();
184100
}
185101

186-
int GetNdkApiLevel(string androidNdkPath, string androidApiLevel, AndroidTargetArch arch)
187-
{
188-
var manifest = AndroidAppManifest.Load (ManifestFile.ItemSpec, MonoAndroidHelper.SupportedVersions);
189-
190-
int level;
191-
if (manifest.MinSdkVersion.HasValue) {
192-
level = manifest.MinSdkVersion.Value;
193-
}
194-
else if (int.TryParse (androidApiLevel, out level)) {
195-
// level already set
196-
}
197-
else {
198-
// Probably not ideal!
199-
level = MonoAndroidHelper.SupportedVersions.MaxStableVersion.ApiLevel;
200-
}
201-
202-
// Some Android API levels do not exist on the NDK level. Workaround this my mapping them to the
203-
// most appropriate API level that does exist.
204-
if (level == 6 || level == 7) level = 5;
205-
else if (level == 10) level = 9;
206-
else if (level == 11) level = 12;
207-
else if (level == 20) level = 19;
208-
else if (level == 22) level = 21;
209-
else if (level == 23) level = 21;
210-
211-
// API levels below level 21 do not provide support for 64-bit architectures.
212-
if (NdkUtil.IsNdk64BitArch(arch) && level < 21) {
213-
level = 21;
214-
}
215-
216-
// We perform a downwards API level lookup search since we might not have hardcoded the correct API
217-
// mapping above and we do not want to crash needlessly.
218-
for (; level >= 5; level--) {
219-
try {
220-
NdkUtil.GetNdkPlatformLibPath (androidNdkPath, arch, level);
221-
break;
222-
} catch (InvalidOperationException ex) {
223-
// Path not found, continue searching...
224-
continue;
225-
}
226-
}
227-
228-
return level;
229-
}
230-
231102
public async override System.Threading.Tasks.Task RunTaskAsync ()
232103
{
233104
// NdkUtil must always be initialized - once per thread
@@ -236,19 +107,6 @@ public async override System.Threading.Tasks.Task RunTaskAsync ()
236107
return;
237108
}
238109

239-
bool hasValidAotMode = GetAndroidAotMode (AndroidAotMode, out AotMode);
240-
if (!hasValidAotMode) {
241-
LogCodedError ("XA3002", Properties.Resources.XA3002, AndroidAotMode);
242-
return;
243-
}
244-
245-
if (AotMode == AotMode.Interp) {
246-
LogDebugMessage ("Interpreter AOT mode enabled");
247-
return;
248-
}
249-
250-
TryGetSequencePointsMode (AndroidSequencePointsMode, out sequencePointsMode);
251-
252110
var nativeLibs = new List<string> ();
253111

254112
await this.WhenAllWithLock (GetAotConfigs (),
@@ -282,48 +140,8 @@ IEnumerable<Config> GetAotConfigs ()
282140
if (!Directory.Exists (AotOutputDirectory))
283141
Directory.CreateDirectory (AotOutputDirectory);
284142

285-
var sdkBinDirectory = MonoAndroidHelper.GetOSBinPath ();
286143
foreach (var abi in SupportedAbis) {
287-
string aotCompiler = "";
288-
string outdir = "";
289-
string mtriple = "";
290-
AndroidTargetArch arch;
291-
292-
switch (abi) {
293-
case "armeabi-v7a":
294-
aotCompiler = Path.Combine (sdkBinDirectory, "cross-arm");
295-
outdir = Path.Combine (AotOutputDirectory, "armeabi-v7a");
296-
mtriple = "armv7-linux-gnueabi";
297-
arch = AndroidTargetArch.Arm;
298-
break;
299-
300-
case "arm64":
301-
case "arm64-v8a":
302-
case "aarch64":
303-
aotCompiler = Path.Combine (sdkBinDirectory, "cross-arm64");
304-
outdir = Path.Combine (AotOutputDirectory, "arm64-v8a");
305-
mtriple = "aarch64-linux-android";
306-
arch = AndroidTargetArch.Arm64;
307-
break;
308-
309-
case "x86":
310-
aotCompiler = Path.Combine (sdkBinDirectory, "cross-x86");
311-
outdir = Path.Combine (AotOutputDirectory, "x86");
312-
mtriple = "i686-linux-android";
313-
arch = AndroidTargetArch.X86;
314-
break;
315-
316-
case "x86_64":
317-
aotCompiler = Path.Combine (sdkBinDirectory, "cross-x86_64");
318-
outdir = Path.Combine (AotOutputDirectory, "x86_64");
319-
mtriple = "x86_64-linux-android";
320-
arch = AndroidTargetArch.X86_64;
321-
break;
322-
323-
// case "mips":
324-
default:
325-
throw new Exception ("Unsupported Android target architecture ABI: " + abi);
326-
}
144+
(string aotCompiler, string outdir, string mtriple, AndroidTargetArch arch) = GetAbiSettings (abi);
327145

328146
if (EnableLLVM && !NdkUtil.ValidateNdkPlatform (LogMessage, LogCodedError, AndroidNdkDirectory, arch, enableLLVM:EnableLLVM)) {
329147
yield return Config.Invalid;
@@ -339,10 +157,7 @@ IEnumerable<Config> GetAotConfigs ()
339157
outdir = outdir.Replace (WorkingDirectory + Path.DirectorySeparatorChar, string.Empty);
340158
}
341159

342-
int level = 0;
343-
string toolPrefix = EnableLLVM
344-
? NdkUtil.GetNdkToolPrefix (AndroidNdkDirectory, arch, level = GetNdkApiLevel (AndroidNdkDirectory, AndroidApiLevel, arch))
345-
: Path.Combine (AndroidBinUtilsDirectory, $"{NdkUtil.GetArchDirName (arch)}-");
160+
string toolPrefix = GetToolPrefix (arch, out int level);
346161
var toolchainPath = toolPrefix.Substring(0, toolPrefix.LastIndexOf(Path.DirectorySeparatorChar));
347162
var ldFlags = string.Empty;
348163
if (EnableLLVM) {
@@ -407,27 +222,8 @@ IEnumerable<Config> GetAotConfigs ()
407222
if (!Directory.Exists (tempDir))
408223
Directory.CreateDirectory (tempDir);
409224

410-
List<string> aotOptions = new List<string> ();
411-
412-
if (Profiles != null && Profiles.Length > 0) {
413-
aotOptions.Add ("profile-only");
414-
foreach (var p in Profiles) {
415-
var fp = Path.GetFullPath (p.ItemSpec);
416-
aotOptions.Add ($"profile={fp}");
417-
}
418-
}
419-
if (!string.IsNullOrEmpty (AotAdditionalArguments))
420-
aotOptions.Add (AotAdditionalArguments);
421-
if (sequencePointsMode == SequencePointsMode.Offline)
422-
aotOptions.Add ($"msym-dir={outdir}");
423-
if (AotMode != AotMode.Normal)
424-
aotOptions.Add (AotMode.ToString ().ToLowerInvariant ());
425-
225+
var aotOptions = GetAotOptions (outdir, mtriple, toolPrefix);
426226
aotOptions.Add ($"outfile={outputFile}");
427-
aotOptions.Add ("asmwriter");
428-
aotOptions.Add ($"mtriple={mtriple}");
429-
aotOptions.Add ($"tool-prefix={toolPrefix}");
430-
aotOptions.Add ($"llvm-path={sdkBinDirectory}");
431227
aotOptions.Add ($"temp-path={tempDir}");
432228

433229
if (!String.IsNullOrEmpty (ldName)) {

0 commit comments

Comments
 (0)