Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions build-tools/create-packs/Microsoft.Android.Runtime.proj
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,22 @@ projects that use the Microsoft.Android.Runtimes framework in .NET 6+.
<NativeRuntimeAsset Condition=" Exists('$(NativeRuntimeOutputRootDir)$(_RuntimeFlavorDirName)\$(AndroidRID)\libnaot-android.debug-static-debug.a') " Include="$(NativeRuntimeOutputRootDir)$(_RuntimeFlavorDirName)\$(AndroidRID)\libnaot-android.debug-static-debug.a" />
<NativeRuntimeAsset Condition=" Exists('$(NativeRuntimeOutputRootDir)$(_RuntimeFlavorDirName)\$(AndroidRID)\libnaot-android.release-static-release.a') " Include="$(NativeRuntimeOutputRootDir)$(_RuntimeFlavorDirName)\$(AndroidRID)\libnaot-android.release-static-release.a" />
<_AndroidRuntimePackAssemblies Include="$(_MonoAndroidNETOutputRoot)$(AndroidLatestStableApiLevel)\Microsoft.Android.Runtime.NativeAOT.dll" />

<!-- NativeAOT uses a separate redist directory with system lib stubs and CRT objects
at the higher API level required by dotnet/runtime's native libraries (API 24). -->
<NativeRuntimeAsset Include="$(NativeRuntimeOutputRootDir)redist-nativeaot\$(AndroidRID)\crtbegin_so.o" />
<NativeRuntimeAsset Include="$(NativeRuntimeOutputRootDir)redist-nativeaot\$(AndroidRID)\crtend_so.o" />
<NativeRuntimeAsset Include="$(NativeRuntimeOutputRootDir)redist-nativeaot\$(AndroidRID)\libc.so" />
<NativeRuntimeAsset Include="$(NativeRuntimeOutputRootDir)redist-nativeaot\$(AndroidRID)\libdl.so" />
<NativeRuntimeAsset Include="$(NativeRuntimeOutputRootDir)redist-nativeaot\$(AndroidRID)\liblog.so" />
<NativeRuntimeAsset Include="$(NativeRuntimeOutputRootDir)redist-nativeaot\$(AndroidRID)\libm.so" />
<NativeRuntimeAsset Include="$(NativeRuntimeOutputRootDir)redist-nativeaot\$(AndroidRID)\libz.so" />

<!-- Toolchain libraries (API-level-agnostic, from common redist) -->
<NativeRuntimeAsset Include="$(NativeRuntimeOutputRootDir)$(_RuntimeRedistDirName)\$(AndroidRID)\libc++_static.a" />
<NativeRuntimeAsset Include="$(NativeRuntimeOutputRootDir)$(_RuntimeRedistDirName)\$(AndroidRID)\libc++abi.a" />
<NativeRuntimeAsset Include="$(NativeRuntimeOutputRootDir)$(_RuntimeRedistDirName)\$(AndroidRID)\libclang_rt.builtins-$(_ClangArch)-android.a" />
<NativeRuntimeAsset Include="$(NativeRuntimeOutputRootDir)$(_RuntimeRedistDirName)\$(AndroidRID)\libunwind.a" />
</ItemGroup>

<ItemGroup>
Expand Down
16 changes: 14 additions & 2 deletions build-tools/xaprepare/xaprepare/Steps/Step_Android_SDK_NDK.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
-->
<ProcessRuntimePackLibraryDirectories
Condition=" '$(_AndroidRuntime)' != 'NativeAOT' "
ResolvedFilesToPublish="@(ResolvedFileToPublish)">
<Output TaskParameter="RuntimePackLibraryDirectories" ItemName="_RuntimePackLibraryDirectory" />
<Output TaskParameter="NativeLibrariesToRemove" ItemName="_NativeLibraryToRemove" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ _ResolveAssemblies MSBuild target.
are never taken into consideration in any context.
-->
<ProcessRuntimePackLibraryDirectories
Condition=" '$(_AndroidRuntime)' != 'NativeAOT' "
ResolvedFilesToPublish="@(ResolvedFileToPublish)">
<Output TaskParameter="RuntimePackLibraryDirectories" ItemName="_RuntimePackLibraryDirectory" />
<Output TaskParameter="NativeLibrariesToRemove" ItemName="_NativeLibraryToRemove" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,49 +307,79 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
Outputs="$(NativeOutputPath)$(NativeBinaryPrefix)$(TargetName).so">
<PropertyGroup>
<_AndroidNativeAotSharedLibrary>$(NativeOutputPath)$(NativeBinaryPrefix)$(TargetName).so</_AndroidNativeAotSharedLibrary>
<!-- NDK paths for CRT objects and compiler-rt -->
<_AndroidUseWorkloadNativeLinker Condition=" '$(_AndroidUseWorkloadNativeLinker)' == '' ">false</_AndroidUseWorkloadNativeLinker>
</PropertyGroup>

<!-- Resolve the runtime pack native directory for workload-provided linker dependencies.
_RuntimePackLibraryDirectory is not populated for NativeAOT, so derive the path from
the libnaot-android RuntimePackAsset item. -->
<ItemGroup Condition=" '$(_AndroidUseWorkloadNativeLinker)' == 'true' ">
<_NativeAotRuntimePackAsset Include="@(RuntimePackAsset->WithMetadataValue('Filename', 'libnaot-android.$(Configuration.ToLower())-static-$(Configuration.ToLower())'))" />
</ItemGroup>
<PropertyGroup Condition=" '$(_AndroidUseWorkloadNativeLinker)' == 'true' ">
<_NativeAotRuntimePackNativeDir>@(_NativeAotRuntimePackAsset->'%(RootDir)%(Directory)')</_NativeAotRuntimePackNativeDir>
<_NativeAotLinkerBinDir>$(AndroidBinUtilsDirectory)</_NativeAotLinkerBinDir>
</PropertyGroup>

<!-- NDK paths (default, when not using workload linker) -->
<PropertyGroup Condition=" '$(_AndroidUseWorkloadNativeLinker)' != 'true' ">
<_NdkApiSysrootDir>$(_NdkSysrootDir)$(_NDKApiLevel)/</_NdkApiSysrootDir>
<_NdkClangResourceDir>$(_AndroidNdkDirectory)toolchains/llvm/prebuilt/$(_NdkPrebuiltAbi)/lib/clang</_NdkClangResourceDir>
<_NativeAotLinkerBinDir>$(_NdkBinDir)</_NativeAotLinkerBinDir>
</PropertyGroup>

<ItemGroup>
<!-- CRT start file -->
<_NativeAotCrtStartFiles Include="$(_NdkApiSysrootDir)crtbegin_so.o">
<!-- CRT and toolchain items from workload runtime pack -->
<ItemGroup Condition=" '$(_AndroidUseWorkloadNativeLinker)' == 'true' ">
<_NativeAotCrtStartFiles Include="$(_NativeAotRuntimePackNativeDir)/crtbegin_so.o">
<Abi>@(_PrivateBuildTargetAbi)</Abi>
</_NativeAotCrtStartFiles>
<_NativeAotCrtEndFiles Include="$(_NativeAotRuntimePackNativeDir)/crtend_so.o">
<Abi>@(_PrivateBuildTargetAbi)</Abi>
</_NativeAotCrtEndFiles>
<_NativeAotLibSearchPaths Include="$(_NativeAotRuntimePackNativeDir)" />
<_NativeAotCompilerRtLibs Include="$(_NativeAotRuntimePackNativeDir)/libclang_rt.builtins-$(_NdkAbi)-android.a" />
<_NativeAotCompilerRtLibs Include="$(_NativeAotRuntimePackNativeDir)/libunwind.a" />
</ItemGroup>

<!-- CRT end file -->
<!-- CRT and toolchain items from NDK -->
<ItemGroup Condition=" '$(_AndroidUseWorkloadNativeLinker)' != 'true' ">
<_NativeAotCrtStartFiles Include="$(_NdkApiSysrootDir)crtbegin_so.o">
<Abi>@(_PrivateBuildTargetAbi)</Abi>
</_NativeAotCrtStartFiles>
<_NativeAotCrtEndFiles Include="$(_NdkApiSysrootDir)crtend_so.o">
<Abi>@(_PrivateBuildTargetAbi)</Abi>
</_NativeAotCrtEndFiles>

<!-- Library search paths (matching what clang provides) -->
<_NativeAotLibSearchPaths Include="$(_NdkApiSysrootDir)" />
<_NativeAotLibSearchPaths Include="$(_NdkSysrootDir)" />
<_NativeAotCompilerRtLibs Include="$(_NdkClangResourceDir)/**/libclang_rt.builtins-$(_NdkAbi)-android.a" />
<_NativeAotCompilerRtLibs Include="$(_NdkClangResourceDir)/**/$(_NdkAbi)/libunwind.a" />
</ItemGroup>

<!-- ILC runtime .a files are already in @(NativeLibrary) from SetupOSSpecificProps -->
<!-- Android-added libraries from _AndroidComputeIlcCompileInputs are in @(_NdkLibs) -->
<!-- Common items (same regardless of linker source) -->
<ItemGroup>
<_NativeAotLinkLibraries Include="@(NativeLibrary)" />
<_NativeAotLinkLibraries Include="@(_NdkLibs)" />

<!-- Additional .o files (jni_init, environment) -->
<_NativeAotAdditionalObjects Include="@(_PrivateJniInitFuncsNativeObjectFile)" />
<_NativeAotAdditionalObjects Include="@(_PrivateEnvironmentNativeObjectFile)" />

<!-- System libraries (resolved via -L search paths) -->
<_NativeAotSystemLibraries Include="dl" />
<_NativeAotSystemLibraries Include="z" />
<_NativeAotSystemLibraries Include="log" />
<_NativeAotSystemLibraries Include="m" />
<_NativeAotSystemLibraries Include="c" />
</ItemGroup>

<!-- Compiler-rt builtins and unwinder (explicit paths from NDK) -->
<_NativeAotCompilerRtLibs Include="$(_NdkClangResourceDir)/**/libclang_rt.builtins-$(_NdkAbi)-android.a" />
<_NativeAotCompilerRtLibs Include="$(_NdkClangResourceDir)/**/$(_NdkAbi)/libunwind.a" />
<!-- When using workload linker, resolve libc++/libnaot from runtime pack dir.
When using NDK, @(_NdkLibs) already has the right NDK sysroot paths. -->
<ItemGroup Condition=" '$(_AndroidUseWorkloadNativeLinker)' == 'true' ">
<_NativeAotLinkLibraries Include="@(RuntimePackAsset->WithMetadataValue('Filename', 'libnaot-android.$(Configuration.ToLower())-static-$(Configuration.ToLower())'))" />
<_NativeAotLinkLibraries Include="$(_NativeAotRuntimePackNativeDir)libc++_static.a" />
<_NativeAotLinkLibraries Include="$(_NativeAotRuntimePackNativeDir)libc++abi.a" />
</ItemGroup>
<ItemGroup Condition=" '$(_AndroidUseWorkloadNativeLinker)' != 'true' ">
<_NativeAotLinkLibraries Include="@(_NdkLibs)" />
</ItemGroup>

<LinkNativeAotSharedLibrary
AndroidBinUtilsDirectory="$(_NdkBinDir)"
AndroidBinUtilsDirectory="$(_NativeAotLinkerBinDir)"
IntermediateOutputPath="$(NativeIntermediateOutputPath)"
RuntimePackLibraryDirectories="@(RuntimePackLibraryDirectories)"
NativeObject="$(NativeObject)"
Expand All @@ -376,6 +406,7 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
<RelativePath>$(NativeBinaryPrefix)$(TargetName).so</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>

<FileWrites Include="$(_AndroidNativeAotSharedLibrary)" />
<FileWrites Include="$(_AndroidNativeAotSharedLibrary)$(NativeSymbolExt)" Condition=" '$(AndroidIncludeDebugSymbols)' != 'true' " />
<FileWrites Include="$(NativeIntermediateOutputPath)sections.ld" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ bool LinkForAbi (string abi)
HashStyleBoth = true,
LittleEndian = true,
EntryPoint = "0x0",
CompressDebugSections = "zlib",
};

if (!ExportsFile.IsNullOrEmpty ()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using NUnit.Framework;
using Xamarin.Android.Tasks;
using Xamarin.ProjectTools;

namespace Xamarin.Android.Build.Tests
{
/// <summary>
/// Build tests specific to the NativeAOT runtime.
/// </summary>
[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."
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ public IEnumerable<string> LastBuildOutput {
/// </summary>
public bool AutomaticNuGetRestore { get; set; } = true;
Comment thread
sbomer marked this conversation as resolved.

/// <summary>
/// When true, skip emitting AndroidNdkDirectory into the test response file.
/// </summary>
public bool SkipNdkDirectory { get; set; } = false;

/// <summary>
/// Checks whether cross-compilers are available for the specified Android ABIs.
/// </summary>
Expand Down Expand Up @@ -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 ();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down