diff --git a/build-tools/create-packs/Microsoft.Android.Runtime.proj b/build-tools/create-packs/Microsoft.Android.Runtime.proj
index 93452ea06b6..7722a8ba636 100644
--- a/build-tools/create-packs/Microsoft.Android.Runtime.proj
+++ b/build-tools/create-packs/Microsoft.Android.Runtime.proj
@@ -108,6 +108,22 @@ projects that use the Microsoft.Android.Runtimes framework in .NET 6+.
<_AndroidRuntimePackAssemblies Include="$(_MonoAndroidNETOutputRoot)$(AndroidLatestStableApiLevel)\Microsoft.Android.Runtime.NativeAOT.dll" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_Android_SDK_NDK.cs b/build-tools/xaprepare/xaprepare/Steps/Step_Android_SDK_NDK.cs
index d987b23c878..0743a1e0dec 100644
--- a/build-tools/xaprepare/xaprepare/Steps/Step_Android_SDK_NDK.cs
+++ b/build-tools/xaprepare/xaprepare/Steps/Step_Android_SDK_NDK.cs
@@ -229,17 +229,29 @@ bool CopyRedistributableFiles (Context context)
foreach (string file in ClangArchFiles) {
CopyFile (abi, clangArchLibPath, file);
}
+
+ // NativeAOT only supports 64-bit ABIs and links against dotnet/runtime's
+ // libSystem.Native.a which was compiled targeting a higher API level. Copy a
+ // second set of CRT/system lib stubs from the higher API level sysroot for the
+ // NativeAOT runtime pack.
+ bool is64BitAbi = abi == "arm64-v8a" || abi == "x86_64";
+ if (is64BitAbi) {
+ string nativeAotCrtFilesPath = Path.Combine (abiDir, BuildAndroidPlatforms.NdkMinimumNonMonoAPI);
+ foreach (string file in CRTFiles) {
+ CopyFile (abi, nativeAotCrtFilesPath, file, redistDirName: "redist-nativeaot");
+ }
+ }
}
return true;
- void CopyFile (string abi, string sourceDir, string fileName)
+ void CopyFile (string abi, string sourceDir, string fileName, string? redistDirName = null)
{
Log.StatusLine ($" {context.Characters.Bullet} Copying NDK redistributable: ", $"{fileName} ({abi})", tailColor: ConsoleColor.White);
string rid = Configurables.Defaults.AbiToRID [abi];
string outputDir = Path.Combine (
context.Properties.GetRequiredValue (KnownProperties.NativeRuntimeOutputRootDir),
- context.Properties.GetRequiredValue (KnownProperties.RuntimeRedistDirName),
+ redistDirName ?? context.Properties.GetRequiredValue (KnownProperties.RuntimeRedistDirName),
rid
);
diff --git a/external/xamarin-android-tools b/external/xamarin-android-tools
index 981df21b947..7d0e083ed8e 160000
--- a/external/xamarin-android-tools
+++ b/external/xamarin-android-tools
@@ -1 +1 @@
-Subproject commit 981df21b947eb16fd0d1910c2cf888f2030323f6
+Subproject commit 7d0e083ed8e4dad3686973cc359e9c4aad57e3aa
diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Aot.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Aot.targets
index 3537c75fb4b..2a214899948 100644
--- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Aot.targets
+++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Aot.targets
@@ -83,7 +83,6 @@ They run in a context of an inner build with a single $(RuntimeIdentifier).
runs in an inner build and `_RuntimePackLibraryDirectory` items aren't carried over to it.
-->
diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets
index 60133cb930a..efb8c209f7b 100644
--- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets
+++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets
@@ -144,7 +144,6 @@ _ResolveAssemblies MSBuild target.
are never taken into consideration in any context.
-->
diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets
index 6138e7e2e8e..092f9a601f0 100644
--- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets
+++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets
@@ -307,49 +307,79 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
Outputs="$(NativeOutputPath)$(NativeBinaryPrefix)$(TargetName).so">
<_AndroidNativeAotSharedLibrary>$(NativeOutputPath)$(NativeBinaryPrefix)$(TargetName).so
-
+ <_AndroidUseWorkloadNativeLinker Condition=" '$(_AndroidUseWorkloadNativeLinker)' == '' ">false
+
+
+
+
+ <_NativeAotRuntimePackAsset Include="@(RuntimePackAsset->WithMetadataValue('Filename', 'libnaot-android.$(Configuration.ToLower())-static-$(Configuration.ToLower())'))" />
+
+
+ <_NativeAotRuntimePackNativeDir>@(_NativeAotRuntimePackAsset->'%(RootDir)%(Directory)')
+ <_NativeAotLinkerBinDir>$(AndroidBinUtilsDirectory)
+
+
+
+
<_NdkApiSysrootDir>$(_NdkSysrootDir)$(_NDKApiLevel)/
<_NdkClangResourceDir>$(_AndroidNdkDirectory)toolchains/llvm/prebuilt/$(_NdkPrebuiltAbi)/lib/clang
+ <_NativeAotLinkerBinDir>$(_NdkBinDir)
-
-
- <_NativeAotCrtStartFiles Include="$(_NdkApiSysrootDir)crtbegin_so.o">
+
+
+ <_NativeAotCrtStartFiles Include="$(_NativeAotRuntimePackNativeDir)/crtbegin_so.o">
@(_PrivateBuildTargetAbi)
+ <_NativeAotCrtEndFiles Include="$(_NativeAotRuntimePackNativeDir)/crtend_so.o">
+ @(_PrivateBuildTargetAbi)
+
+ <_NativeAotLibSearchPaths Include="$(_NativeAotRuntimePackNativeDir)" />
+ <_NativeAotCompilerRtLibs Include="$(_NativeAotRuntimePackNativeDir)/libclang_rt.builtins-$(_NdkAbi)-android.a" />
+ <_NativeAotCompilerRtLibs Include="$(_NativeAotRuntimePackNativeDir)/libunwind.a" />
+
-
+
+
+ <_NativeAotCrtStartFiles Include="$(_NdkApiSysrootDir)crtbegin_so.o">
+ @(_PrivateBuildTargetAbi)
+
<_NativeAotCrtEndFiles Include="$(_NdkApiSysrootDir)crtend_so.o">
@(_PrivateBuildTargetAbi)
-
-
<_NativeAotLibSearchPaths Include="$(_NdkApiSysrootDir)" />
<_NativeAotLibSearchPaths Include="$(_NdkSysrootDir)" />
+ <_NativeAotCompilerRtLibs Include="$(_NdkClangResourceDir)/**/libclang_rt.builtins-$(_NdkAbi)-android.a" />
+ <_NativeAotCompilerRtLibs Include="$(_NdkClangResourceDir)/**/$(_NdkAbi)/libunwind.a" />
+
-
-
+
+
<_NativeAotLinkLibraries Include="@(NativeLibrary)" />
- <_NativeAotLinkLibraries Include="@(_NdkLibs)" />
-
-
<_NativeAotAdditionalObjects Include="@(_PrivateJniInitFuncsNativeObjectFile)" />
<_NativeAotAdditionalObjects Include="@(_PrivateEnvironmentNativeObjectFile)" />
-
-
<_NativeAotSystemLibraries Include="dl" />
<_NativeAotSystemLibraries Include="z" />
<_NativeAotSystemLibraries Include="log" />
<_NativeAotSystemLibraries Include="m" />
<_NativeAotSystemLibraries Include="c" />
+
-
- <_NativeAotCompilerRtLibs Include="$(_NdkClangResourceDir)/**/libclang_rt.builtins-$(_NdkAbi)-android.a" />
- <_NativeAotCompilerRtLibs Include="$(_NdkClangResourceDir)/**/$(_NdkAbi)/libunwind.a" />
+
+
+ <_NativeAotLinkLibraries Include="@(RuntimePackAsset->WithMetadataValue('Filename', 'libnaot-android.$(Configuration.ToLower())-static-$(Configuration.ToLower())'))" />
+ <_NativeAotLinkLibraries Include="$(_NativeAotRuntimePackNativeDir)libc++_static.a" />
+ <_NativeAotLinkLibraries Include="$(_NativeAotRuntimePackNativeDir)libc++abi.a" />
+
+
+ <_NativeAotLinkLibraries Include="@(_NdkLibs)" />
$(NativeBinaryPrefix)$(TargetName).so
PreserveNewest
+
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/LinkNativeAotSharedLibrary.cs b/src/Xamarin.Android.Build.Tasks/Tasks/LinkNativeAotSharedLibrary.cs
index 491ece9dd1b..b1ade49afd5 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/LinkNativeAotSharedLibrary.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/LinkNativeAotSharedLibrary.cs
@@ -131,7 +131,6 @@ bool LinkForAbi (string abi)
HashStyleBoth = true,
LittleEndian = true,
EntryPoint = "0x0",
- CompressDebugSections = "zlib",
};
if (!ExportsFile.IsNullOrEmpty ()) {
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/ProcessRuntimePackLibraryDirectories.cs b/src/Xamarin.Android.Build.Tasks/Tasks/ProcessRuntimePackLibraryDirectories.cs
index 142a0dccde6..c4d10e2f3f9 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/ProcessRuntimePackLibraryDirectories.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/ProcessRuntimePackLibraryDirectories.cs
@@ -95,6 +95,7 @@ bool IsInSupportedRuntimePack (ITaskItem item)
}
return NuGetPackageId.StartsWith ("Microsoft.Android.Runtime.CoreCLR.", StringComparison.OrdinalIgnoreCase) ||
- NuGetPackageId.StartsWith ("Microsoft.Android.Runtime.Mono.", StringComparison.OrdinalIgnoreCase);
+ NuGetPackageId.StartsWith ("Microsoft.Android.Runtime.Mono.", StringComparison.OrdinalIgnoreCase) ||
+ NuGetPackageId.StartsWith ("Microsoft.Android.Runtime.NativeAOT.", StringComparison.OrdinalIgnoreCase);
}
}
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/NativeAotBuildTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/NativeAotBuildTests.cs
new file mode 100644
index 00000000000..d830c8cb77a
--- /dev/null
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/NativeAotBuildTests.cs
@@ -0,0 +1,54 @@
+using NUnit.Framework;
+using Xamarin.Android.Tasks;
+using Xamarin.ProjectTools;
+
+namespace Xamarin.Android.Build.Tests
+{
+ ///
+ /// Build tests specific to the NativeAOT runtime.
+ ///
+ [TestFixture]
+ [Category ("Node-2")]
+ public class NativeAotBuildTests : BaseTest
+ {
+ [Test]
+ public void BuildNativeAot_WithoutNdk_Fails ()
+ {
+ var proj = new XamarinAndroidApplicationProject {
+ IsRelease = true,
+ };
+ proj.SetRuntime (AndroidRuntime.NativeAOT);
+
+ using var builder = CreateApkBuilder ();
+ builder.ThrowOnBuildFailure = false;
+ // Override _AndroidNdkDirectory (the internal resolved path) to simulate no NDK.
+ // Setting AndroidNdkDirectory alone is not sufficient because ResolveSdks has a
+ // fallback chain that discovers NDK from the SDK directory, environment variables,
+ // and other standard locations. /p: has highest MSBuild precedence and cannot be
+ // overridden by task outputs.
+ Assert.IsFalse (
+ builder.Build (proj, parameters: new [] { "_AndroidNdkDirectory=" }),
+ "Build should have failed without NDK."
+ );
+ }
+
+ [Test]
+ public void BuildNativeAot_WithWorkloadLinker_WithoutNdk ()
+ {
+ var proj = new XamarinAndroidApplicationProject {
+ IsRelease = true,
+ };
+ proj.SetRuntime (AndroidRuntime.NativeAOT);
+
+ using var builder = CreateApkBuilder ();
+ // Use workload-provided linker and sysroot instead of NDK
+ Assert.IsTrue (
+ builder.Build (proj, parameters: new [] {
+ "_AndroidNdkDirectory=",
+ "_AndroidUseWorkloadNativeLinker=true",
+ }),
+ "Build should succeed with workload linker and no NDK."
+ );
+ }
+ }
+}
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/Builder.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/Builder.cs
index 753f6d7a9fa..24151e8e3da 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/Builder.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/Builder.cs
@@ -104,6 +104,11 @@ public IEnumerable LastBuildOutput {
///
public bool AutomaticNuGetRestore { get; set; } = true;
+ ///
+ /// When true, skip emitting AndroidNdkDirectory into the test response file.
+ ///
+ public bool SkipNdkDirectory { get; set; } = false;
+
///
/// Checks whether cross-compilers are available for the specified Android ABIs.
///
@@ -276,7 +281,7 @@ protected bool BuildInternal (string projectOrSolution, string target, string []
sw.WriteLine ("/p:AndroidSdkDirectory=\"{0}\"", sdkPath);
}
string ndkPath = AndroidSdkResolver.GetAndroidNdkPath ();
- if (Directory.Exists (ndkPath)) {
+ if (!SkipNdkDirectory && Directory.Exists (ndkPath)) {
sw.WriteLine ("/p:AndroidNdkDirectory=\"{0}\"", ndkPath);
}
string jdkPath = AndroidSdkResolver.GetJavaSdkPath ();
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs
index ff584246f61..b0d450822fa 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs
@@ -68,7 +68,8 @@ public static Config GetConfig (TaskLoggingHelper log, string androidBinUtilsDir
string stubPath = Path.Combine (packLibDir.ItemSpec, StubFileName);
if (!File.Exists (stubPath)) {
- throw new InvalidOperationException ($"Internal error: archive DSO stub file '{stubPath}' does not exist in runtime pack at {packLibDir}");
+ log.LogDebugMessage ($"Archive DSO stub file '{stubPath}' not found in runtime pack directory, skipping");
+ continue;
}
AndroidTargetArch arch = MonoAndroidHelper.RidToArch (packRID);