Skip to content

Commit ae6da92

Browse files
[One .NET] rework .NET 6 AOT support (#6562)
Fixes: #6520 Context: dotnet/runtime#56163 Context: dotnet/runtime#62725 This fixes AOT builds for project names like `foo Ümläüts`, and LLVM-related options are no longer space delimited. Previously, the `<MonoAOTCompiler/>` task managed its own `WorkingDirectory` to the location that `.dll` input files were located. The problem with this is that *other* paths like `temp-path` would then need to become full paths. Full paths containing characters like `Ümläüts` would break! Additionally, `%(AotArguments)` metadata is split on `;` and joined on `,`. As `ld-flags` contained `;`, we would lose it. To work around that, we delimited by the space character, which breaks if directories contain a space! [System.Runtime.dll] Exec (with response file contents expanded) in …\android\obj\Release\net6.0-android\android-arm64\linked: MONO_PATH=…\android\obj\Release\net6.0-android\android-arm64\linked; MONO_ENV_OPTIONS= C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Runtime.AOT.win-x64.Cross.android-arm64\6.0.0-rc.2.21480.5\Sdk\..\tools\mono-aot-cross.exe --debug --llvm "--aot=asmwriter,mtriple=aarch64-linux-android,tool-prefix=C:\Program Files (x86)\Android\android-sdk\ndk-bundle\toolchains\llvm\prebuilt\windows-x86_64\bin\aarch64-linux-android-,ld-name=ld.EXE,ld-flags=-LC:\Program\ Files\ (x86)\Android\android-sdk\ndk-bundle\toolchains\llvm\prebuilt\windows-x86_64\lib\gcc\aarch64-linux-android\4.9.x -LC:\Program\ Files\ (x86)\Android\android-sdk\ndk-bundle\platforms\android-21\arch-arm64\usr\lib C:\Program\ Files\ (x86)\Android\android-sdk\ndk-bundle\toolchains\llvm\prebuilt\windows-x86_64\lib\gcc\aarch64-linux-android\4.9.x\libgcc.a C:\Program\ Files\ (x86)\Android\android-sdk\ndk-bundle\platforms\android-21\arch-arm64\usr\lib\libc.so C:\Program\ Files\ (x86)\Android\android-sdk\ndk-bundle\platforms\android-21\arch-arm64\usr\lib\libm.so,temp-path=…\android\obj\Release\net6.0-android\android-arm64\aot\arm64-v8a\System.Runtime,nodebug,llvm-path=C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Runtime.AOT.win-x64.Cross.android-arm64\6.0.0-rc.2.21480.5\Sdk\..\tools,outfile=…\android\obj\Release\net6.0-android\android-arm64\aot\System.Runtime.dll.so,llvm-outfile=…\android\obj\Release\net6.0-android\android-arm64\aot\System.Runtime.dll-llvm.o" "System.Runtime.dll" … C:\Program Files (x86)\Android\android-sdk\ndk-bundle\toolchains\llvm\prebuilt\windows-x86_64\bin\aarch64-linux-android-ld.EXE: cannot find Files\: No such file or directory To solve these issues: * Added strongly-typed properties to the `<MonoAOTCompiler/>` MSBuild task. We no longer have to put as many things into `%(AotArguments)` on each assembly. Only assembly-specific settings go there now, such as the path to AOT profiles. Right now the "main assembly" has no profile at all, and the rest get built-in profiles. * Added a `MonoAOTCompiler.WorkingDirectory` property, for which we pass in `$(MSBuildProjectDirectory)` for the current project directory. After these changes, I could remove many temporary comments around dotnet/runtime#56163, as the tests work properly now.
1 parent db8b88a commit ae6da92

File tree

7 files changed

+97
-84
lines changed

7 files changed

+97
-84
lines changed

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,17 @@ They run in a context of an inner build with a single $(RuntimeIdentifier).
5353
AndroidApiLevel="$(_AndroidApiLevel)"
5454
MinimumSupportedApiLevel="$(AndroidMinimumSupportedApiLevel)"
5555
AndroidSequencePointsMode="$(_SequencePointsMode)"
56-
AotAdditionalArguments="$(AndroidAotAdditionalArguments)"
5756
TargetName="$(TargetName)"
5857
ResolvedAssemblies="@(_AndroidAotInputs)"
5958
AotOutputDirectory="$(_AndroidAotBinDirectory)"
6059
RuntimeIdentifier="$(RuntimeIdentifier)"
6160
EnableLLVM="$(EnableLLVM)"
62-
UsingAndroidNETSdk="true"
6361
Profiles="@(AndroidAotProfile)">
62+
<Output PropertyName="_Triple" TaskParameter="Triple" />
63+
<Output PropertyName="_ToolPrefix" TaskParameter="ToolPrefix" />
64+
<Output PropertyName="_MsymPath" TaskParameter="MsymPath" />
65+
<Output PropertyName="_LdName" TaskParameter="LdName" />
66+
<Output PropertyName="_LdFlags" TaskParameter="LdFlags" />
6467
<Output ItemName="_MonoAOTAssemblies" TaskParameter="ResolvedAssemblies" />
6568
</GetAotAssemblies>
6669
<PropertyGroup>
@@ -69,7 +72,10 @@ They run in a context of an inner build with a single $(RuntimeIdentifier).
6972
</PropertyGroup>
7073
<MakeDir Directories="$(IntermediateOutputPath)aot\" />
7174
<MonoAOTCompiler
72-
Assemblies="@(_MonoAOTAssemblies->'%(FullPath)')"
75+
Triple="$(_Triple)"
76+
ToolPrefix="$(_ToolPrefix)"
77+
MsymPath="$(_MsymPath)"
78+
Assemblies="@(_MonoAOTAssemblies)"
7379
CompilerBinaryPath="$(_MonoAOTCompilerPath)"
7480
DisableParallelAot="$(_DisableParallelAot)"
7581
IntermediateOutputPath="$(IntermediateOutputPath)"
@@ -79,7 +85,11 @@ They run in a context of an inner build with a single $(RuntimeIdentifier).
7985
OutputType="Library"
8086
UseAotDataFile="false"
8187
UseLLVM="$(EnableLLVM)"
82-
LLVMPath="$(_LLVMPath)">
88+
LLVMPath="$(_LLVMPath)"
89+
LdName="$(_LdName)"
90+
LdFlags="$(_LdFlags)"
91+
WorkingDirectory="$(MSBuildProjectDirectory)"
92+
AotArguments="$(AndroidAotAdditionalArguments)">
8393
<Output TaskParameter="CompiledAssemblies" ItemName="_AotCompiledAssemblies" />
8494
<Output TaskParameter="FileWrites" ItemName="FileWrites" />
8595
</MonoAOTCompiler>

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

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ public class Aot : GetAotArguments
4545

4646
public string ExtraAotOptions { get; set; }
4747

48+
public string AotAdditionalArguments { get; set; }
49+
4850
[Output]
4951
public string[] NativeLibrariesReferences { get; set; }
5052

@@ -137,11 +139,27 @@ IEnumerable<Config> GetAotConfigs (NdkTools ndk)
137139
string tempDir = Path.Combine (outdir, Path.GetFileName (assembly.ItemSpec));
138140
Directory.CreateDirectory (tempDir);
139141

140-
var aotOptions = GetAotOptions (ndk, arch, level, outdir, mtriple, toolPrefix);
142+
GetAotOptions (ndk, arch, level, outdir, toolPrefix);
141143
// NOTE: ordering seems to matter on Windows
142-
aotOptions.Insert (0, $"outfile={outputFile}");
143-
aotOptions.Insert (0, $"llvm-path={SdkBinDirectory}");
144-
aotOptions.Insert (0, $"temp-path={tempDir}");
144+
var aotOptions = new List<string> ();
145+
aotOptions.Add ("asmwriter");
146+
aotOptions.Add ($"mtriple={mtriple}");
147+
aotOptions.Add ($"tool-prefix={toolPrefix}");
148+
aotOptions.Add ($"outfile={outputFile}");
149+
aotOptions.Add ($"llvm-path={SdkBinDirectory}");
150+
aotOptions.Add ($"temp-path={tempDir}");
151+
if (!string.IsNullOrEmpty (AotAdditionalArguments)) {
152+
aotOptions.Add (AotAdditionalArguments);
153+
}
154+
if (!string.IsNullOrEmpty (MsymPath)) {
155+
aotOptions.Add ($"msym-dir={MsymPath}");
156+
}
157+
if (!string.IsNullOrEmpty (LdName)) {
158+
aotOptions.Add ($"ld-name={LdName}");
159+
}
160+
if (!string.IsNullOrEmpty (LdFlags)) {
161+
aotOptions.Add ($"ld-flags={LdFlags}");
162+
}
145163
if (Profiles != null && Profiles.Length > 0) {
146164
if (Path.GetFileNameWithoutExtension (assembly.ItemSpec) == TargetName) {
147165
LogDebugMessage ($"Not using profile(s) for main assembly: {assembly.ItemSpec}");

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

Lines changed: 21 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,24 @@ public abstract class GetAotArguments : AndroidAsyncTask
5151

5252
public ITaskItem [] Profiles { get; set; } = Array.Empty<ITaskItem> ();
5353

54-
public bool UsingAndroidNETSdk { get; set; }
55-
56-
public string AotAdditionalArguments { get; set; } = "";
57-
5854
[Required, Output]
5955
public ITaskItem [] ResolvedAssemblies { get; set; } = Array.Empty<ITaskItem> ();
6056

57+
[Output]
58+
public string? Triple { get; set; }
59+
60+
[Output]
61+
public string? ToolPrefix { get; set; }
62+
63+
[Output]
64+
public string? MsymPath { get; set; }
65+
66+
[Output]
67+
public string? LdName { get; set; }
68+
69+
[Output]
70+
public string? LdFlags { get; set; }
71+
6172
protected AotMode AotMode;
6273
protected SequencePointsMode SequencePointsMode;
6374
protected string SdkBinDirectory = "";
@@ -208,22 +219,12 @@ int GetNdkApiLevel (NdkTools ndk, AndroidTargetArch arch)
208219
}
209220

210221
/// <summary>
211-
/// Returns a list of parameters to pass to the --aot switch
222+
/// Fills [Output] parameters to pass to the --aot switch
212223
/// </summary>
213-
protected List<string> GetAotOptions (NdkTools ndk, AndroidTargetArch arch, int level, string outdir, string mtriple, string toolPrefix)
224+
protected void GetAotOptions (NdkTools ndk, AndroidTargetArch arch, int level, string outdir, string toolPrefix)
214225
{
215-
List<string> aotOptions = new List<string> ();
216-
217-
if (!string.IsNullOrEmpty (AotAdditionalArguments))
218-
aotOptions.Add (AotAdditionalArguments);
219226
if (SequencePointsMode == SequencePointsMode.Offline)
220-
aotOptions.Add ($"msym-dir={outdir}");
221-
if (AotMode != AotMode.Normal)
222-
aotOptions.Add (AotMode.ToString ().ToLowerInvariant ());
223-
224-
aotOptions.Add ("asmwriter");
225-
aotOptions.Add ($"mtriple={mtriple}");
226-
aotOptions.Add ($"tool-prefix={toolPrefix}");
227+
MsymPath = outdir;
227228

228229
string ldName;
229230
if (EnableLLVM) {
@@ -238,16 +239,12 @@ protected List<string> GetAotOptions (NdkTools ndk, AndroidTargetArch arch, int
238239
ldName = "ld";
239240
}
240241
string ldFlags = GetLdFlags (ndk, arch, level, toolPrefix);
241-
242-
// MUST be before `ld-flags`, otherwise Mono fails to parse it on Windows
243242
if (!string.IsNullOrEmpty (ldName)) {
244-
aotOptions.Add ($"ld-name={ldName}");
243+
LdName = ldName;
245244
}
246245
if (!string.IsNullOrEmpty (ldFlags)) {
247-
aotOptions.Add ($"ld-flags={ldFlags}");
246+
LdFlags = ldFlags;
248247
}
249-
250-
return aotOptions;
251248
}
252249

253250
string GetLdFlags(NdkTools ndk, AndroidTargetArch arch, int level, string toolPrefix)
@@ -292,13 +289,7 @@ string GetLdFlags(NdkTools ndk, AndroidTargetArch arch, int level, string toolPr
292289
libs.Add (Path.Combine (androidLibPath, "libc.so"));
293290
libs.Add (Path.Combine (androidLibPath, "libm.so"));
294291

295-
if (UsingAndroidNETSdk) {
296-
// NOTE: in .NET 6+ use space for the delimiter and escape spaces in paths
297-
var escaped = libs.Select (l => l.Replace (" ", "\\ "));
298-
ldFlags = string.Join (" ", escaped);
299-
} else {
300-
ldFlags = $"\\\"{string.Join ("\\\";\\\"", libs)}\\\"";
301-
}
292+
ldFlags = $"\\\"{string.Join ("\\\";\\\"", libs)}\\\"";
302293
}
303294
return ldFlags;
304295
}

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,10 @@ public override Task RunTaskAsync ()
4242
}
4343

4444
(_, string outdir, string mtriple, AndroidTargetArch arch) = GetAbiSettings (abi);
45-
string toolPrefix = GetToolPrefix (ndk, arch, out int level);
45+
Triple = mtriple;
46+
ToolPrefix = GetToolPrefix (ndk, arch, out int level);
4647

47-
var aotOptions = GetAotOptions (ndk, arch, level, outdir, mtriple, toolPrefix);
48+
GetAotOptions (ndk, arch, level, outdir, ToolPrefix);
4849

4950
var aotProfiles = new StringBuilder ();
5051
if (Profiles != null && Profiles.Length > 0) {
@@ -56,15 +57,16 @@ public override Task RunTaskAsync ()
5657
}
5758
}
5859

59-
var arguments = string.Join (",", aotOptions);
6060
foreach (var assembly in ResolvedAssemblies) {
61-
var temp = Path.GetFullPath (Path.Combine (outdir, Path.GetFileNameWithoutExtension (assembly.ItemSpec)));
61+
var temp = Path.Combine (outdir, Path.GetFileNameWithoutExtension (assembly.ItemSpec));
6262
Directory.CreateDirectory (temp);
6363
if (Path.GetFileNameWithoutExtension (assembly.ItemSpec) == TargetName) {
64-
LogDebugMessage ($"Not using profile(s) for main assembly: {assembly.ItemSpec}");
65-
assembly.SetMetadata ("AotArguments", $"{arguments},temp-path={temp}");
64+
if (Profiles != null && Profiles.Length > 0) {
65+
LogDebugMessage ($"Not using profile(s) for main assembly: {assembly.ItemSpec}");
66+
}
67+
assembly.SetMetadata ("AotArguments", $"asmwriter,temp-path={temp}");
6668
} else {
67-
assembly.SetMetadata ("AotArguments", $"{arguments},temp-path={temp}{aotProfiles}");
69+
assembly.SetMetadata ("AotArguments", $"asmwriter,temp-path={temp}{aotProfiles}");
6870
}
6971
}
7072

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

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ public void Setup ()
2626
{
2727
if (!IsWindows)
2828
return;
29-
// Use standard NDK directory for now
30-
// See: https://github.com/dotnet/runtime/issues/56163
31-
if (Builder.UseDotNet)
32-
return;
3329

3430
var sdkPath = AndroidSdkPath;
3531
var ndkPath = AndroidNdkPath;
@@ -49,10 +45,6 @@ public void TearDown ()
4945
{
5046
if (!IsWindows)
5147
return;
52-
// Use standard NDK directory for now
53-
// See: https://github.com/dotnet/runtime/issues/56163
54-
if (Builder.UseDotNet)
55-
return;
5648
Environment.SetEnvironmentVariable ("TEST_ANDROID_SDK_PATH", "");
5749
Environment.SetEnvironmentVariable ("TEST_ANDROID_NDK_PATH", "");
5850
Directory.Delete (SdkWithSpacesPath, recursive: true);
@@ -169,7 +161,6 @@ public void BuildBasicApplicationReleaseProfiledAotWithoutDefaultProfile ()
169161

170162
[Test]
171163
[TestCaseSource (nameof (AotChecks))]
172-
[Category ("DotNetIgnore")] // Not currently working, see: https://github.com/dotnet/runtime/issues/56163
173164
public void BuildAotApplicationAndÜmläüts (string supportedAbis, bool enableLLVM, bool expectedResult, bool usesAssemblyBlobs)
174165
{
175166
var path = Path.Combine ("temp", string.Format ("BuildAotApplication AndÜmläüts_{0}_{1}_{2}_{3}", supportedAbis, enableLLVM, expectedResult, usesAssemblyBlobs));
@@ -203,7 +194,7 @@ public void BuildAotApplicationAndÜmläüts (string supportedAbis, bool enableL
203194
if (!expectedResult)
204195
return;
205196
//NOTE: Windows has shortened paths such as: C:\Users\myuser\ANDROI~3\ndk\PLATFO~1\AN3971~1\arch-x86\usr\lib\libc.so
206-
if (checkMinLlvmPath && !IsWindows) {
197+
if (checkMinLlvmPath && !IsWindows && !Builder.UseDotNet) {
207198
Xamarin.Android.Tasks.NdkTools ndk = Xamarin.Android.Tasks.NdkTools.Create (AndroidNdkPath);
208199
bool ndk22OrNewer = ndk.Version.Main.Major >= 22;
209200

@@ -218,12 +209,13 @@ public void BuildAotApplicationAndÜmläüts (string supportedAbis, bool enableL
218209
}
219210
}
220211
foreach (var abi in supportedAbis.Split (new char [] { ';' })) {
221-
var libapp = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath,
222-
"bundles", abi, "libmonodroid_bundle_app.so");
223-
Assert.IsFalse (File.Exists (libapp), abi + " libmonodroid_bundle_app.so should not exist");
224-
var assemblies = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath,
225-
"aot", abi, "libaot-UnnamedProject.dll.so");
226-
Assert.IsTrue (File.Exists (assemblies), "{0} libaot-UnnamedProject.dll.so does not exist", abi);
212+
var intermediate = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath);
213+
var libapp = Path.Combine (intermediate, "bundles", abi, "libmonodroid_bundle_app.so");
214+
FileAssert.DoesNotExist (libapp);
215+
var aotNativeLibrary = Builder.UseDotNet ?
216+
Path.Combine (intermediate, AbiUtils.AbiToRuntimeIdentifier (abi), "aot", "UnnamedProject.dll.so") :
217+
Path.Combine (intermediate, "aot", abi, "libaot-UnnamedProject.dll.so");
218+
FileAssert.Exists (aotNativeLibrary);
227219
var apk = Path.Combine (Root, b.ProjectDirectory,
228220
proj.OutputPath, $"{proj.PackageName}-Signed.apk");
229221

@@ -248,7 +240,6 @@ public void BuildAotApplicationAndÜmläüts (string supportedAbis, bool enableL
248240
[Test]
249241
[TestCaseSource (nameof (AotChecks))]
250242
[Category ("Minor"), Category ("MkBundle")]
251-
[Category ("DotNetIgnore")] // Not currently working, see: https://github.com/dotnet/runtime/issues/56163
252243
public void BuildAotApplicationAndBundleAndÜmläüts (string supportedAbis, bool enableLLVM, bool expectedResult, bool usesAssemblyBlobs)
253244
{
254245
var path = Path.Combine ("temp", string.Format ("BuildAotApplicationAndBundle AndÜmläüts_{0}_{1}_{2}_{3}", supportedAbis, enableLLVM, expectedResult, usesAssemblyBlobs));
@@ -449,10 +440,6 @@ public static void Foo () {
449440
[Category ("LLVM")]
450441
public void NoSymbolsArgShouldReduceAppSize ([Values ("", "Hybrid")] string androidAotMode)
451442
{
452-
if (Builder.UseDotNet) {
453-
Assert.Ignore ("https://github.com/dotnet/runtime/issues/57800");
454-
}
455-
456443
AssertAotModeSupported (androidAotMode);
457444

458445
var proj = new XamarinAndroidApplicationProject () {
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
3+
namespace Xamarin.ProjectTools
4+
{
5+
public static class AbiUtils
6+
{
7+
public static string AbiToRuntimeIdentifier (string androidAbi)
8+
{
9+
if (androidAbi == "armeabi-v7a") {
10+
return "android-arm";
11+
} else if (androidAbi == "arm64-v8a") {
12+
return "android-arm64";
13+
} else if (androidAbi == "x86") {
14+
return "android-x86";
15+
} else if (androidAbi == "x86_64") {
16+
return "android-x64";
17+
}
18+
throw new InvalidOperationException ($"Unknown abi: {androidAbi}");
19+
}
20+
}
21+
}

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Utilities/ProjectExtensions.cs

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,30 +31,14 @@ public static void SetAndroidSupportedAbis (this IShortFormProject project, stri
3131

3232
public static void SetRuntimeIdentifier (this IShortFormProject project, string androidAbi)
3333
{
34-
if (androidAbi == "armeabi-v7a") {
35-
project.SetProperty (KnownProperties.RuntimeIdentifier, "android-arm");
36-
} else if (androidAbi == "arm64-v8a") {
37-
project.SetProperty (KnownProperties.RuntimeIdentifier, "android-arm64");
38-
} else if (androidAbi == "x86") {
39-
project.SetProperty (KnownProperties.RuntimeIdentifier, "android-x86");
40-
} else if (androidAbi == "x86_64") {
41-
project.SetProperty (KnownProperties.RuntimeIdentifier, "android-x64");
42-
}
34+
project.SetProperty (KnownProperties.RuntimeIdentifier, AbiUtils.AbiToRuntimeIdentifier (androidAbi));
4335
}
4436

4537
public static void SetRuntimeIdentifiers (this IShortFormProject project, string [] androidAbis)
4638
{
4739
var abis = new List<string> ();
4840
foreach (var androidAbi in androidAbis) {
49-
if (androidAbi == "armeabi-v7a") {
50-
abis.Add ("android-arm");
51-
} else if (androidAbi == "arm64-v8a") {
52-
abis.Add ("android-arm64");
53-
} else if (androidAbi == "x86") {
54-
abis.Add ("android-x86");
55-
} else if (androidAbi == "x86_64") {
56-
abis.Add ("android-x64");
57-
}
41+
abis.Add (AbiUtils.AbiToRuntimeIdentifier (androidAbi));
5842
}
5943
project.SetProperty (KnownProperties.RuntimeIdentifiers, string.Join (";", abis));
6044
}

0 commit comments

Comments
 (0)