Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
<!-- ResolvedFileToPublish is not populated until _AndroidComputeIlcCompileInputs, so
proguard generation must run after that target instead of after ILLink. -->
<_GenerateProguardAfterTargets>_AndroidComputeIlcCompileInputs</_GenerateProguardAfterTargets>
<!-- Ask ILC to produce a static library (.a) instead of a shared library (.so).
This gives Android full control over the native linker invocation. -->
<NativeLib>static</NativeLib>
<!-- ILC only sets this for NativeLib=Shared, but we need [UnmanagedCallersOnly] methods
exported as native symbols so JNI can find them via dlsym. -->
<IlcExportUnmanagedEntrypoints>true</IlcExportUnmanagedEntrypoints>
</PropertyGroup>

<!-- Default property values for NativeAOT Debug configuration -->
Expand Down Expand Up @@ -107,10 +113,19 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
<CppCompilerAndLinker>$(_NdkAbi)-linux-android$(_NDKApiLevel)-clang$(_NdkWrapperScriptExt)</CppCompilerAndLinker>
<CppLinker>$(_NdkAbi)-linux-android$(_NDKApiLevel)-clang$(_NdkWrapperScriptExt)</CppLinker>
<ObjCopyName>llvm-objcopy</ObjCopyName>
<!-- Use the NDK's llvm-ar instead of the host 'ar' so that ELF objects are handled
correctly when cross-compiling. -->
<CppLibCreator>llvm-ar</CppLibCreator>

<!-- We must ensure this is `false`, as it would interfere with statically linking libc++ -->
<LinkStandardCPlusPlusLibrary>false</LinkStandardCPlusPlusLibrary>

<!-- Clear LinkerFlavor: the ILC targets set it to 'lld' for android/bionic, but with NativeLib=static
the _LinkerVersion detection is skipped, causing a numeric comparison error in LinkNative.
Since we handle native linking ourselves, LinkerFlavor is not needed.
Context: https://github.com/dotnet/runtime/issues/126978 -->
<LinkerFlavor />

<!-- Example settings from: https://github.com/xamarin/xamarin-macios/blob/c43d4ea40bc777969e3b158cf46446df292d8449/dotnet/targets/Xamarin.Shared.Sdk.targets#L541-L550 -->
<RunILLink>true</RunILLink>
<!--
Expand All @@ -133,7 +148,7 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
</PropertyGroup>
<ItemGroup>
<!-- Android needs a proper soname property or it will refuse to load the library -->
<LinkerArg Include="&quot;-Wl,-soname,lib$(TargetName)$(NativeBinaryExt)&quot;" />
<LinkerArg Include="&quot;-Wl,-soname,lib$(TargetName).so&quot;" />
<LinkerArg Include="-Wl,--error-unresolved-symbols" />
<LinkerArg Include="-Wl,--no-undefined" />

Expand Down Expand Up @@ -258,6 +273,61 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
DependsOnTargets="_GenerateNativeAotAndroidAppAssemblerSources">
</Target>

<!--
With NativeLib=static, ILC's LinkNative target produces a .a archive via `ar`.
We need to link the ILC .o output + all LinkerArg items into a .so ourselves.
This target runs after LinkNative and produces the shared library that Android needs.
-->
<Target Name="_AndroidLinkNativeAotSharedLibrary"
AfterTargets="LinkNative"
Inputs="$(NativeObject);@(NativeLibrary)"
Outputs="$(NativeOutputPath)$(NativeBinaryPrefix)$(TargetName).so">
Comment on lines +281 to +284
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do managed assemblies also need to be Inputs? Someone could add their own [UnmanagedCallersOnly] and it wouldn't get picked up in an incremental build.

Honestly, this is NativeAOT (Release-mode only), maybe we could just let this always run if LinkNative ran?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if someone adds [UCO] it would cause ILC to re-run, produce a new $(NativeObject) and that would make this target run.

These are the same inputs/outputs as the LinkNative target so it should already be running if LinkNative ran. If we remove the Inputs/Outputs, it would always run even if LinkNative was skipped.

<PropertyGroup>
<_AndroidNativeAotSharedLibrary>$(NativeOutputPath)$(NativeBinaryPrefix)$(TargetName).so</_AndroidNativeAotSharedLibrary>
</PropertyGroup>
Comment thread
sbomer marked this conversation as resolved.
<ItemGroup>
<_AndroidNativeAotLinkerArgs Include="&quot;$(NativeObject)&quot;" />
<_AndroidNativeAotLinkerArgs Include="-shared" />
<_AndroidNativeAotLinkerArgs Include="-fuse-ld=lld" />
<_AndroidNativeAotLinkerArgs Include="-o &quot;$(_AndroidNativeAotSharedLibrary)&quot;" />
<!-- These flags are in the ILC targets' LinkNative/LinkerArg but conditioned on NativeLib=Shared
or LinkerFlavor=lld, so they're missing when NativeLib=static. Add them back. -->
<_AndroidNativeAotLinkerArgs Include="-Wl,-e,0x0" />
<_AndroidNativeAotLinkerArgs Include="-Wl,-z,max-page-size=16384" />
<_AndroidNativeAotLinkerArgs Include="-Wl,--version-script=&quot;$(ExportsFile)&quot;" Condition="'$(ExportsFile)' != ''" />
<_AndroidNativeAotLinkerArgs Include="-Wl,--export-dynamic" Condition="'$(ExportsFile)' != ''" />
<_AndroidNativeAotLinkerArgs Include="-Wl,--discard-all" />
<_AndroidNativeAotLinkerArgs Include="-Wl,--gc-sections" />
<_AndroidNativeAotLinkerArgs Include="-Wl,-T,&quot;$(NativeIntermediateOutputPath)sections.ld&quot;" />
Comment thread
jonathanpeppers marked this conversation as resolved.
<_AndroidNativeAotLinkerArgs Include="@(LinkerArg)" />
Comment thread
jonathanpeppers marked this conversation as resolved.
</ItemGroup>

<!-- Linker script to retain the __modules section (required by NativeAOT runtime) -->
<WriteLinesToFile File="$(NativeIntermediateOutputPath)sections.ld" Lines="OVERWRITE_SECTIONS { __modules : { KEEP(*(__modules)) } }" Overwrite="true" />

<MakeDir Directories="$([System.IO.Path]::GetDirectoryName($(_AndroidNativeAotSharedLibrary)))" />
<!-- On Windows, write args to a response file to avoid cmd.exe quoting issues with spaces in paths.
This matches what ILC's LinkNative does. -->
<Exec Command="&quot;$(CppLinker)&quot; @(_AndroidNativeAotLinkerArgs, ' ')"
Condition=" !$([MSBuild]::IsOSPlatform('windows')) " />
<WriteLinesToFile File="$(NativeIntermediateOutputPath)android-link.rsp" Lines="@(_AndroidNativeAotLinkerArgs)" Overwrite="true" Encoding="utf-8"
Condition=" $([MSBuild]::IsOSPlatform('windows')) " />
<Exec Command="&quot;$(CppLinker)&quot; @&quot;$(NativeIntermediateOutputPath)android-link.rsp&quot;"
Condition=" $([MSBuild]::IsOSPlatform('windows')) " />

<ItemGroup>
<!-- Replace the .a that NativeCompile/ComputeLinkedFilesToPublish would publish with our .so -->
<ResolvedFileToPublish Remove="@(ResolvedFileToPublish)" Condition=" '%(Filename)%(Extension)' == '$(TargetName).a' " />
<ResolvedFileToPublish Include="$(_AndroidNativeAotSharedLibrary)">
<RelativePath>$(NativeBinaryPrefix)$(TargetName).so</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
<FileWrites Include="$(_AndroidNativeAotSharedLibrary)" />
<FileWrites Include="$(NativeIntermediateOutputPath)sections.ld" />
<FileWrites Include="$(NativeIntermediateOutputPath)android-link.rsp" Condition=" $([MSBuild]::IsOSPlatform('windows')) " />
</ItemGroup>
</Target>

<!--
dotnet/runtime#125629 moved the NativeAOT entry point from ComputeLinkedFilesToPublish
(AfterTargets="ComputeResolvedFilesToPublishList") to NativeCompile (an SDK Publish hook).
Expand All @@ -272,7 +342,7 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
<Target Name="_AndroidFixNativeLibraryFileName" AfterTargets="ComputeFilesToPublish">
<ItemGroup>
<!-- Fix paths to contain lib-prefix -->
<ResolvedFileToPublish Update="@(ResolvedFileToPublish)" ArchiveFileName="lib%(FileName)%(Extension)" Condition=" '%(Filename)%(Extension)' == '$(TargetName)$(NativeBinaryExt)' " />
<ResolvedFileToPublish Update="@(ResolvedFileToPublish)" ArchiveFileName="lib%(FileName)%(Extension)" Condition=" '%(Filename)%(Extension)' == '$(TargetName).so' " />
</ItemGroup>
</Target>

Expand Down