Skip to content

Commit

Permalink
Fix Windows Build
Browse files Browse the repository at this point in the history
* Create BionicNativeAot.target
* Remove sed dependency.
* Use batch files only on Windows.
  • Loading branch information
josephmoresena committed Jan 19, 2025
1 parent d2be14b commit 4233b2b
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 84 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -336,3 +336,6 @@ ASALocalRun/
*.dll
*.dwo
.DS_Store
/android_clang.cmd
/android_llvm-objcopy.cmd
android_clang.sh
63 changes: 8 additions & 55 deletions AndroidHelloJniLib.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,10 @@
<TrimmerSingleWarn>false</TrimmerSingleWarn>
</PropertyGroup>

<PropertyGroup>
<!--This is needed in order to build successfully Android binaries-->
<IsAndroid>$(RuntimeIdentifier.ToLower().StartsWith('android'))</IsAndroid>
<IsAndroid Condition="'$(IsAndroid)'=='false'">$(RuntimeIdentifier.ToLower().StartsWith('linux-bionic'))</IsAndroid>
<IsWindowsHost>$([MSBuild]::IsOSPlatform('Windows'))</IsWindowsHost>
<IsLinuxHost>$([MSBuild]::IsOSPlatform('Linux'))</IsLinuxHost>
<IsOSXHost>$([MSBuild]::IsOSPlatform('OSX'))</IsOSXHost>
</PropertyGroup>

<ItemGroup>
<Compile Remove="BFlatSupport\*.cs"/>
<None Remove="BFlatSupport\*"/>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Rxmxnx.PInvoke.Extensions" Version="2.0.3" />
<!--JNI libs needs to be compiled with -z noexecstack-->
<LinkerArg Condition="$(LinuxHost) == 'true' Or $(IsAndroid) == 'true'" Include="-z noexecstack"/>
</ItemGroup>
<!-- Import NDK pressets -->
<Import Project="BionicNativeAot.targets"/>

<!-- NativeAot Properties -->
<PropertyGroup>
<TrimUnusedDependencies>true</TrimUnusedDependencies>
<RootAllApplicationAssemblies>false</RootAllApplicationAssemblies>
Expand All @@ -45,44 +29,13 @@
<IlcFoldIdenticalMethodBodies>true</IlcFoldIdenticalMethodBodies>
</PropertyGroup>

<!--Following blocks are exclusive for Android building-->
<PropertyGroup Condition="$(IsAndroid) == 'true'">
<!--Use ndk-sample expected library name -->
<CppCompilerAndLinker Condition="$(IsWindowsHost) == 'true'">./android_fake_clang.cmd</CppCompilerAndLinker>
<CppCompilerAndLinker Condition="$(IsLinuxHost) == 'true'">./android_fake_clang.sh</CppCompilerAndLinker>
<CppCompilerAndLinker Condition="$(IsOSXHost) == 'true'">./android_fake_clang.command</CppCompilerAndLinker>
<NdkHost Condition="$(IsWindowsHost) == 'true'">windows-x86_64</NdkHost>
<NdkHost Condition="$(IsLinuxHost) == 'true'">linux-x86_64</NdkHost>
<NdkHost Condition="$(IsOSXHost) == 'true'">darwin-x86_64</NdkHost>
<ObjCopyName Condition="'$(ObjCopyName)' == ''">$(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/$(NdkHost)/bin/llvm-objcopy</ObjCopyName>
<SysRoot Condition="'$(SysRoot)' == ''">$(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/$(NdkHost)/sysroot</SysRoot>
<!--Hack for removing init and fini sections into the exports file-->
<ExportsPatch>'s/global: _init; _fini;/global: /g;'</ExportsPatch>
</PropertyGroup>

<ItemGroup Condition="$(IsAndroid) == 'true'">
<!--Hack for passing a valid target argument on NDK linker invocation-->
<LinkerArg Condition="$(RuntimeIdentifier.ToLower().EndsWith('-x64'))" Include="--target=x86_64-linux-android21"/>
<LinkerArg Condition="$(RuntimeIdentifier.ToLower().EndsWith('-arm64'))" Include="--target=aarch64-linux-android21"/>
<ItemGroup>
<Compile Remove="BFlatSupport\*.cs"/>
<None Remove="BFlatSupport\*"/>
</ItemGroup>

<ItemGroup Condition="$(IsAndroid) == 'true' And $(UseLibCSections) == 'true'">
<!--Hack for passing a valid target argument on NDK linker invocation-->
<LinkerArg Include="-Wl,--defsym,_init=__libc_init"/>
<LinkerArg Include="-Wl,--defsym,_fini=__libc_fini"/>
<ItemGroup>
<PackageReference Include="Rxmxnx.PInvoke.Extensions" Version="2.0.3"/>
</ItemGroup>

<Target Name="RemoveSections" Condition="$(IsAndroid) == 'true' And $(RemoveSections) == 'true'" AfterTargets="IlcCompile" BeforeTargets="LinkNative">
<!--Reads as lines the generated exports file-->
<ReadLinesFromFile File="$(ExportsFile)">
<Output TaskParameter="Lines" PropertyName="ExportsLines"/>
</ReadLinesFromFile>
<!--Gets a single text from the exports lines-->
<PropertyGroup>
<ExportsText Condition="$(ExportsLines) != ''">@(ExportsLines)</ExportsText>
</PropertyGroup>
<!--The sed tool is used as invalid lines remover-->
<Exec Command="sed -i -z $(ExportsPatch) $(ExportsFile)"/>
</Target>

</Project>
69 changes: 69 additions & 0 deletions BionicNativeAot.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<PropertyGroup>
<!--This is needed in order to build successfully bionic binaries-->
<IsAndroid>$(RuntimeIdentifier.ToLower().StartsWith('android'))</IsAndroid>
<IsAndroid Condition="'$(IsAndroid)'=='false'">$(RuntimeIdentifier.ToLower().StartsWith('linux-bionic'))</IsAndroid>
<IsWindowsHost>$([MSBuild]::IsOSPlatform('Windows'))</IsWindowsHost>
<IsLinuxHost>$([MSBuild]::IsOSPlatform('Linux'))</IsLinuxHost>
<IsOSXHost>$([MSBuild]::IsOSPlatform('OSX'))</IsOSXHost>
<RemoveSections Condition="'$(RemoveSections)' != '' And $([System.String]::ToLower('$(RemoveSections)')) != 'false'">true</RemoveSections>
</PropertyGroup>

<!--Following blocks are exclusive for NKD building-->
<PropertyGroup Condition="$(IsAndroid) == 'true'">
<NdkPropertiesFile>$(ANDROID_NDK_ROOT)\source.properties</NdkPropertiesFile>
<NdkHost Condition="$(IsWindowsHost) == 'true'">windows-x86_64</NdkHost>
<NdkHost Condition="$(IsLinuxHost) == 'true'">linux-x86_64</NdkHost>
<NdkHost Condition="$(IsOSXHost) == 'true'">darwin-x86_64</NdkHost>
<SysRoot Condition="'$(SysRoot)' == ''">$(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/$(NdkHost)/sysroot</SysRoot>
<!--Retrieves NDK version-->
<BaseRevision>$([System.IO.File]::ReadAllText('$(NdkPropertiesFile)'))</BaseRevision>
<NdkVersion>$([System.Text.RegularExpressions.Regex]::Match('$(BaseRevision)', 'Pkg.BaseRevision\s*=\s*(\S+)').Groups[1].Value.Split('.')[0])</NdkVersion>
<NdkVersion Condition="'$(NdkVersion)' == ''">0</NdkVersion>
</PropertyGroup>

<PropertyGroup Condition="$(IsAndroid) == 'true'">
<!--OSX and Linux can use NDK directly-->
<CppCompilerAndLinker Condition="$(IsWindowsHost) != 'true'">$(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/$(NdkHost)/bin/clang</CppCompilerAndLinker>
<ObjCopyName Condition="$(IsWindowsHost) != 'true'">$(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/$(NdkHost)/bin/llvm-objcopy</ObjCopyName>
<!--Windows should use batch files to work with NDK-->
<CppCompilerAndLinker Condition="$(IsWindowsHost) == 'true'">android_clang.cmd</CppCompilerAndLinker>
<ObjCopyName Condition="$(IsWindowsHost) == 'true'">android_llvm-objcopy.cmd</ObjCopyName>
</PropertyGroup>

<ItemGroup Condition="$(IsAndroid) == 'true'">
<!--JNI libs needs to be compiled with -z noexecstack-->
<LinkerArg Include="-z noexecstack"/>
<!--Hack for passing a valid target argument on NDK linker invocation-->
<LinkerArg Condition="$(RuntimeIdentifier.ToLower().EndsWith('-x64'))" Include="--target=x86_64-linux-android21"/>
<LinkerArg Condition="$(RuntimeIdentifier.ToLower().EndsWith('-arm64'))" Include="--target=aarch64-linux-android21"/>
</ItemGroup>

<!--Hack for using NDK executables on Windows-->
<Target Condition="$(IsAndroid) == 'true'" Name="PrepareNdkBatches" AfterTargets="Build" BeforeTargets="IlcCompile">
<PropertyGroup>
<NdkHostBin Condition="$(IsWindowsHost) == 'true'">$(ANDROID_NDK_ROOT)\toolchains\llvm\prebuilt\$(NdkHost)\bin\</NdkHostBin>
<NdkHostBin Condition="$(IsWindowsHost) != 'true'">$(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/$(NdkHost)/bin/</NdkHostBin>
</PropertyGroup>
<!--Create Windows .cmd files-->
<WriteLinesToFile Condition="$(IsWindowsHost) == 'true'" File="android_clang.cmd" Lines="&quot;$(NdkHostBin)clang.exe&quot; %*" Overwrite="true"/>
<WriteLinesToFile Condition="$(IsWindowsHost) == 'true'" File="android_llvm-objcopy.cmd" Lines="&quot;$(NdkHostBin)llvm-objcopy.exe&quot; %*" Overwrite="true"/>
</Target>
<Target Condition="$(IsAndroid) == 'true'" Name="RemoveBatchFiles" AfterTargets="LinkNative">
<!--Remove batch files-->
<ItemGroup>
<BatchFiles Condition="$(IsWindowsHost) == 'true'" Include="android_clang.cmd"/>
<BatchFiles Condition="$(IsWindowsHost) == 'true'" Include="android_llvm-objcopy.cmd"/>
</ItemGroup>
<Delete Files="@(BatchFiles)"/>
</Target>

<!--Hack for removing _init and _fini sections into the exports file-->
<Target Name="RemoveSections" Condition="$(IsAndroid) == 'true'" AfterTargets="IlcCompile" BeforeTargets="LinkNative">
<PropertyGroup Condition="$(RemoveSections) != 'true'">
<RemoveSections Condition="$([System.Int32]::Parse($(NdkVersion.Split('.')[0]))) >= 26">true</RemoveSections>
</PropertyGroup>
<WriteLinesToFile Condition="$(RemoveSections) == 'true'" File="$(ExportsFile)" Lines="$([System.IO.File]::ReadAllText($(ExportsFile)).Replace('global: _init; _fini;','global: '))" Overwrite="true"/>
</Target>
</Project>
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,29 +33,31 @@ With new .NET 8.0 SDK we are now able to compile NativeAOT android binaries usin

### How to build it

This process was tested for android-arm64 (`linux-bionic-arm64`) but may work for android-x64 (`linux-bionic-x64`) too. <br/>
This process was tested for android-arm64 (`linux-bionic-arm64`) but may work for android-x64 (`linux-bionic-x64`)
too. <br/>
The following commands assume:

* **ANDROID_NDK_ROOT** environment variable: Full path to NDK. Used to preconfigure **CppCompilerAndLinker**,
**ObjCopyName** and **SysRoot**.
* .NET project imports [BionicNativeAot.targets](BionicNativeAot.targets) file.
* **ANDROID_NDK_ROOT** environment variable: Full path to NDK. Used to preconfigure **CppCompilerAndLinker**,
**ObjCopyName** and **SysRoot**.
* Android NDK version is **r26b**.
* Target architecture is **arm64**.
* Host architecture is windows, linux or macOS x64.

dotnet publish -r linux-bionic-arm64 -p:DisableUnsupportedError=true -p:PublishAotUsingRuntimePack=true -p:AssemblyName=libhello-jni -p:RemoveSections=true
dotnet publish -r linux-bionic-arm64 -p:DisableUnsupportedError=true -p:PublishAotUsingRuntimePack=true -p:AssemblyName=libhello-jni

#### Environment Parameters

* **ObjCopyName**: Path of NDK **llvm-objcopy**. This is needed in order to use StripSymbols MSBuild parameter.

#### MSBuild Parameters

* **CppCompilerAndLinker**: Linker. The android_fake_clang is just a script that invokes the real NDK Clang executable.
* **CppCompilerAndLinker**: Linker. On Windows, NDK executables must be encapsulated in **cmd** files in order to be used.
On Linux and macOS, the executables are used directly.
* **SysRoot**: Sysroot path from NDK. Needed for NDK compilation.
* **RemoveSections**: Hack to remove **__init** and **__fini** symbols from .exports file.
* **RemoveSections**: Removes **__init** and **__fini** symbols from .exports file. When it detects that the NDK version
is greater than r26 it is automatically set to true.
* **AssemblyName**: In order to produce a .so file with given name.
* **UseLibCSections**: In order to use **__libc_init** and **__libc_fini** as exported **__init** and **__fini**
symbols.

## Considerations

Expand Down
9 changes: 0 additions & 9 deletions android_fake_clang.cmd

This file was deleted.

6 changes: 0 additions & 6 deletions android_fake_clang.command

This file was deleted.

6 changes: 0 additions & 6 deletions android_fake_clang.sh

This file was deleted.

0 comments on commit 4233b2b

Please sign in to comment.