Skip to content

Commit db8f9eb

Browse files
[runtime] Infrastructure to build runtime components using NativeAOT (#98565)
# Native runtime component libraries using NativeAOT This PR allows managed libraries to be compiled using NativeAOT and to be used in the runtime using normal CMake idioms. ## Adding a new managed library Add a new subdirectory to `src/native/managed` for your library with a `src` and `inc` subdirectories: ``` console $ mkdir -p libMyNewLibrary/src libMyNewLibrary/inc $ dotnet new classlib -n libMyNewLibrary -o libMyNewLibrary/src ``` In `src/native/managed/compile-native.proj`, add `src/native/managed/libMyNewLibrary/src/libMyNewLibrary.csproj` to the `NativeLibsProjectsToBuild` item group. In `src/native/managed/libMyNewLibrary/src/libMyNewLibrary.csproj`: 1. Near the top, add `<Import Project="..\..\native-library.props" />` 2. Near the bottom, add `<Import Project="..\..\native-library.targets" />` 3. Define an item `@(InstallRuntimeComponentDest)` that has directory names relative to `artifacts/bin/<runtimeFlavor>/<os.arch.config>/` where the shared library should be installed. It's a good idea to have at least `.`: ```xml <ItemGroup> <InstallRuntimeComponentDest Include="." /> <InstallRuntimeComponentDest Include="sharedFramework" Condition="'$(RuntimeFlavor)' == 'coreclr'"/> </ItemGroup> ``` Limitations: * The project should be called `libXXXX` - currently the infrastructure expects a `lib` prefix on all platforms. * Currently only shared library output is supported. In principle static linking is possible, but the infrastructure is not finished yet. Additionally, mixing Debug/Release configurations with static linking will not be supported on Windows. --- Ideally we'd like to be able to produce shared or static libraries. Currently only shared libraries really work robustly (in that only the exported native symbols of the C# library are exposed and the NativeAOT implementation details are completely hidden). There was some work to have cmake integration: the runtime could do `find_nativeaot_library(libWhatever [REQUIRED])` and get two cmake targets: `libWhatever::libs` and `libWhatever::headers` that could be used for linking with other targets. The current usecase doesn't need this level of integration however, so the cmake stuff was moved to this branch: https://github.com/lambdageek/runtime/tree/naot-runtime-lib-with-cmake --- * add compile-native.proj to runtime-prereqs.proj * support for importing NativeAOT compiled shared libs * put AdditionalProperties on the ProjectReference same as msbuild publish make sure the same runtime identifier is passed down * no x86 or riscv * disable armel * pass SysRoot and --gcc-toolchain to native build * set linker flavor if doing cross builds * rename toplevel dir to src/native/managed * Add README * remove static linking support it wasn't a good fit for CoreCLR because the EventPipe and GC symbols clashed between CoreCLR and NativeAOT during static linking. A possible way forward is to use incremental linking (`clang -Wl,-r`) on Linux/Mac in combination with the `--exclude-libs` or `-hidden-lx` linker options. However on Windows since NativeAOT static artifacts are always built in Release mode, we would only be able to do static linking in limited circumstances (and we would need to find the right linker option to do the apropriate symbol hiding / incremental linking) * use _IsPublishing for the publish target Co-authored-by: Jeremy Koritzinsky <jkoritzinsky@gmail.com> * copy runtime components to final location in native-library.targets do not do any copying in cmake * revert cmake changes for imported libs remove the special install_clr and strip_symbols handling for imported libraries * strip nativeoat-built componentlike coreclr does on mac/linux * copy windows PDBs to final install destinations * set library name on linux and mac SharedLibraryInstallName only works on mobile apple platforms * native-library.targets: make SetupOSSpecificProps probe for objcopy * Remove cmake integration * rename InstallRuntimeComponentDestination * automatically include native-library.{props,targets} for IsSourceProject C# projects under src/native/managed * switch compile-native.proj to the traversal sdk --------- Co-authored-by: Jeremy Koritzinsky <jekoritz@microsoft.com>
1 parent da9528d commit db8f9eb

File tree

7 files changed

+247
-0
lines changed

7 files changed

+247
-0
lines changed

src/coreclr/runtime-prereqs.proj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
<Import Project="$(RepositoryEngineeringDir)versioning.targets" />
1414
<Import Project="$(RepositoryEngineeringDir)nativepgo.targets" />
1515

16+
<ItemGroup>
17+
<ProjectReference Include="$(RepoRoot)\src\native\managed\compile-native.proj" ReferenceOutputAssembly="false"/>
18+
</ItemGroup>
19+
1620
<Target Name="BuildPrereqs" BeforeTargets="Build" DependsOnTargets="GenerateRuntimeVersionFile;GenerateNativeSourcelinkFile;OutputPgoPathForCI" />
1721
<Import Project="Sdk.targets" Sdk="Microsoft.Build.NoTargets" />
1822
<!--
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<Project>
2+
<Import Project="..\..\..\Directory.Build.props" />
3+
<Import Project=".\native-library.props" Condition="'$(IsSourceProject)' == 'true' and '$(Language)' == 'C#'" />
4+
</Project>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<Project>
2+
<Import Project=".\native-library.targets" Condition="'$(IsSourceProject)' == 'true' and '$(Language)' == 'C#'" />
3+
<Import Project="..\..\..\Directory.Build.targets" />
4+
</Project>

src/native/managed/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Native runtime component libraries using NativeAOT
2+
3+
This directory contains managed libraries that will be compiled using NativeAOT and can be used in runtime components.
4+
5+
## Adding a new managed library
6+
7+
Add a new subdirectory to `src/native/managed` for your library with a `src`, `inc` and `test` subdirectories:
8+
9+
``` console
10+
$ mkdir -p libMyNewLibrary/src libMyNewLibrary/inc libMyNewLibrary/test
11+
$ dotnet new classlib -n libMyNewLibrary -o libMyNewLibrary/src
12+
```
13+
14+
In `src/native/managed/compile-native.proj`, add
15+
`src/native/managed/libMyNewLibrary/src/libMyNewLibrary.csproj` to the `NativeLibsProjectsToBuild`
16+
item group.
17+
18+
In `src/native/managed/libMyNewLibrary/src/libMyNewLibrary.csproj`:
19+
1. Define an item `@(InstallRuntimeComponentDestination)` that has directory names relative to `artifacts/bin/<runtimeFlavor>/<os.arch.config>/` where the shared library should be installed. It's a good idea to have at least `.`:
20+
```xml
21+
<ItemGroup>
22+
<InstallRuntimeComponentDestination Include="." />
23+
<InstallRuntimeComponentDestination Include="sharedFramework" Condition="'$(RuntimeFlavor)' == 'coreclr'"/>
24+
</ItemGroup>
25+
```
26+
27+
Limitations:
28+
29+
* The project should be called `libXXXX` - currently the infrastructure expects a `lib` prefix on all platforms.
30+
31+
* Currently only shared library output is supported. In principle static linking is possible, but the
32+
infrastructure is not finished yet. Additionally, mixing Debug/Release configurations with static
33+
linking will not be supported on Windows.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<Project Sdk="Microsoft.Build.Traversal" DefaultTargets="Publish">
2+
<PropertyGroup>
3+
<!-- We always want to use release for publishing using NativeAOT -->
4+
<NativeLibsPublishConfiguration>Release</NativeLibsPublishConfiguration>
5+
<!-- we always want to make shared libs -->
6+
<NativeLibKind Condition="'$(NativeLibKind)' == ''">shared</NativeLibKind>
7+
8+
<!-- When we publish, we want to ensure the SDK does the same thing as though we ran 'dotnet publish' -->
9+
<TraversalPublishGlobalProperties>$(TraversalPublishGlobalProperties);_IsPublishing=true</TraversalPublishGlobalProperties>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<!-- add new projects here -->
14+
<!-- NativeLibsProjectsToBuild Include="$(MSBuildThisFileDirectory)libhellomanaged/src/libhellomanaged.csproj" -->
15+
</ItemGroup>
16+
17+
<!-- Decide if we're going to do the NativeAOT builds -->
18+
<PropertyGroup>
19+
<!-- disable on Mono, for now -->
20+
<SupportsNativeAotComponents Condition="'$(SupportsNativeAotComponents)' == '' and '$(RuntimeFlavor)' == 'Mono'">false</SupportsNativeAotComponents>
21+
<!-- NativeAOT doesn't support cross-OS compilation. disable for crossdac-->
22+
<SupportsNativeAotComponents Condition="'$(SupportsNativeAotComponents)' == '' and '$(HostOS)' != '$(TargetOS)'">false</SupportsNativeAotComponents>
23+
<!-- unsupported targets -->
24+
<SupportsNativeAotComponents Condition="'$(SupportsNativeAotComponents)' == '' and ('$(TargetArchitecture)' == 'arm' or '$(TargetArchitecture)' == 'armel' or '$(TargetArchitecture)' == 'x86' or '$(TargetArchitecture)' == 'riscv64')">false</SupportsNativeAotComponents>
25+
<SupportsNativeAotComponents Condition="'$(SupportsNativeAotComponents)' == '' and ('$(TargetsWindows)' == 'true' or '$(TargetsOSX)' == 'true' or ('$(TargetsLinux)' == 'true' and '$(TargetsAndroid)' != 'true' and '$(TargetsLinuxMusl)' != 'true'))">true</SupportsNativeAotComponents>
26+
<SupportsNativeAotComponents Condition="'$(SupportsNativeAotComponents)' == ''">false</SupportsNativeAotComponents>
27+
</PropertyGroup>
28+
29+
<!-- some special kinds of runtime builds need extra NativeAOT flags -->
30+
<PropertyGroup>
31+
<SysRoot Condition="'$(CrossBuild)' == 'true' and '$(HostOS)' != 'windows'">$(ROOTFS_DIR)</SysRoot>
32+
<LinkerFlavor Condition="'$(CrossBuild)' == 'true' and '$(TargetsLinux)' == 'true'">lld</LinkerFlavor>
33+
<CustomLinkerArgToolchainArg Condition="'$(CrossBuild)' == 'true' and '$(_hostArchitecture)' == '$(_targetArchitecture)' and '$(_hostOS)' != 'windows'">--gcc-toolchain=$(ROOTFS_DIR)/usr</CustomLinkerArgToolchainArg>
34+
</PropertyGroup>
35+
36+
<!-- properties to pass down to the subproject builds -->
37+
<ItemGroup>
38+
<SubprojectProps Include="Configuration" Value="$(NativeLibsPublishConfiguration)" />
39+
<SubprojectProps Include="RuntimeConfiguration" Value="$(RuntimeConfiguration)" />
40+
<SubprojectProps Include="LibrariesConfiguration" Value="$(LibrariesConfiguration)" />
41+
<SubprojectProps Include="RuntimeIdentifier" Value="$(OutputRID)" />
42+
43+
<SubprojectProps Include="NativeLib" Value="$(NativeLibKind)" />
44+
45+
<SubprojectProps Condition="'$(SysRoot)' != ''" Include="SysRoot" Value="$(SysRoot)" />
46+
<SubprojectProps Condition="'$(LinkerFlavor)' != ''" Include="LinkerFlavor" Value="$(LinkerFlavor)" />
47+
<SubprojectProps Condition="'$(CustomLinkerArgToolchainArg)' != ''" Include="CustomLinkerArgToolchainArg" Value="$(CustomLinkerArgToolchainArg)" />
48+
</ItemGroup>
49+
50+
<PropertyGroup>
51+
<SplitSubprojectProps>@(SubprojectProps->'%(Identity)=%(Value)', ';')</SplitSubprojectProps>
52+
</PropertyGroup>
53+
54+
<ItemGroup>
55+
<ProjectReference Include="@(NativeLibsProjectsToBuild)"
56+
ReferenceOutputAssembly="false"
57+
AdditionalProperties="%(AdditionalProperties);$(SplitSubprojectProps)"
58+
Condition="$(SupportsNativeAotComponents)"/>
59+
</ItemGroup>
60+
</Project>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<Project>
2+
<PropertyGroup>
3+
<PublishAot>true</PublishAot>
4+
<SelfContained>true</SelfContained>
5+
<!-- Don't strip symbols NativeAOT's default way. We will strip and save the symbols ourselves,
6+
the same way as eng/native/functions.cmake strip_symbols
7+
-->
8+
<StripSymbols>false</StripSymbols>
9+
</PropertyGroup>
10+
11+
<!-- set the shared library name. this helps the native linker correctly reference this shared
12+
library in dependents -->
13+
<PropertyGroup>
14+
<!-- in net9.0 we can do this, but only on mobile apple platforms, not OSX -->
15+
<SharedLibraryInstallName>@rpath/$(MSBuildProjectName).dylib</SharedLibraryInstallName>
16+
</PropertyGroup>
17+
<ItemGroup Condition="'$(TargetsOSX)' == 'true'">
18+
<LinkerArg Include="-Wl,-install_name,@rpath/$(MSBuildProjectName).dylib" />
19+
</ItemGroup>
20+
<ItemGroup Condition="'$(TargetsUnix)' == 'true' and ! ('$(TargetsAppleMobile)' == 'true' or '$(TargetsOSX)' == 'true')">
21+
<!-- If there is no soname, ld on Linux and some other Unixes will embed the full (build-time!)
22+
path to this shared library into any binary that links against it. We don't want that. -->
23+
<LinkerArg Include="-Wl,-soname=$(MSBuildProjectName).so" />
24+
</ItemGroup>
25+
26+
<PropertyGroup>
27+
<!-- if IsRuntimeComponent is true, we will put the native library into the specified locations under `artifacts/bin/$(RuntimeFlavor)/os.arch.config/` -->
28+
<IsRuntimeComponent Condition="'$(IsRuntimeComponent)' == ''">true</IsRuntimeComponent>
29+
</PropertyGroup>
30+
31+
<ItemGroup>
32+
<!-- passed by compile-native.proj to set - -gcc-toolchain=$(ROOTFS_DIR)/usr -->
33+
<CustomLinkerArg Condition="'$(CustomLinkerArgToolchainArg)' != ''" Include="$(CustomLinkerArgToolchainArg)" />
34+
</ItemGroup>
35+
</Project>
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
<Project>
2+
3+
<!-- strip the library the same way as the cmake build for coreclr does it:
4+
- on mac, leave a .dylib.dwarf file next to the library.
5+
- on linux leave a .so.dbg file next to to the library
6+
-->
7+
<Target Name="StripLibraryLikeCoreCLRSetupPaths"
8+
DependsOnTargets="CopyNativeBinary"
9+
Condition="'$(IsRuntimeComponent)' == 'true' and '$(TargetsWindows)' != 'true'">
10+
<PropertyGroup>
11+
<StrippedOutputPath>$(OutputPath)stripped\</StrippedOutputPath>
12+
<StripSourceFile>$(StrippedOutputPath)$(TargetName)$(NativeBinaryExt)</StripSourceFile>
13+
<StrippedExt Condition="'$(StrippedExt)' == '' and ('$(TargetsOSX)' == 'true' or '$(TargetsAppleMobile)' == 'true')">.dylib.dwarf</StrippedExt>
14+
<StrippedExt Condition="'$(TargetsUnix)' == 'true' and '$(StrippedExt)' == ''">.so.dbg</StrippedExt>
15+
<StripDestinationFile>$(StrippedOutputPath)$(TargetName)$(StrippedExt)</StripDestinationFile>
16+
</PropertyGroup>
17+
</Target>
18+
19+
<!--
20+
Hack: temporarily turn on StripSymbols whlie SetupOSSpecificProps runs, then turn it off
21+
again before LinkNative runs. The problem is that SetupOSSpecificProps only probes for
22+
$(ObjCopyName) and $(ObjCopyNameAlternative) if symbol stripping is turned on. But we don't
23+
want LinkNative to actually do any stripping since we have our own way that we'd like it to
24+
work.
25+
-->
26+
<Target Name="TempStripSymbolsOn"
27+
BeforeTargets="SetupOSSpecificProps">
28+
<PropertyGroup>
29+
<StripSymbols>true</StripSymbols>
30+
</PropertyGroup>
31+
</Target>
32+
33+
<Target Name="TempStripSymbolsOff"
34+
AfterTargets="SetupOSSpecificProps"
35+
BeforeTargets="LinkNative">
36+
<PropertyGroup>
37+
<StripSymbols>false</StripSymbols>
38+
</PropertyGroup>
39+
</Target>
40+
41+
<Target Name="StripLibraryLikeCoreCLRBuild"
42+
DependsOnTargets="SetupOSSpecificProps;CopyNativeBinary;StripLibraryLikeCoreCLRSetupPaths"
43+
Condition="'$(IsRuntimeComponent)' == 'true' and '$(TargetsWindows)' != 'true'"
44+
Inputs="$(PublishDir)$(TargetName)$(NativeBinaryExt)"
45+
Outputs="$(StripSourceFile);$(StripDestinationFile)">
46+
<Error Text="Do not set StripSymbols to true - runtime components stripping is controlled by native-library.targets" Condition="'$(StripSymbols)' == 'true'" />
47+
48+
<Message Importance="Normal" Text="Stripping $(PublishDir)$(TargetName)$(NativeBinaryExt) into $(StripSourceFile) and $(StripDestinationFile)" />
49+
50+
<!-- copy from the published/ subfolder to the stripped/ subfolder -->
51+
<Copy SourceFiles="$(PublishDir)$(TargetName)$(NativeBinaryExt)"
52+
DestinationFolder="$(StrippedOutputPath)"
53+
SkipUnchangedFiles="true" />
54+
55+
<PropertyGroup>
56+
<_StripLike Condition="'$(TargetsOSX)' == 'true' or '$(TargetsAppleMobile)' == 'true'">apple</_StripLike>
57+
<_StripLike Condition="'$(_StripLike)' == ''">gnu</_StripLike>
58+
</PropertyGroup>
59+
60+
<Exec Command="dsymutil --flat $(LikeCoreCLRDSymUtilMinimizeOpt) $(StripSourceFile)" Condition="'$(_StripLike)' == 'apple'"/> <!-- produces the .dylib.dwarf file -->
61+
<Exec Command="strip -no_code_signature_warning -S $(StripSourceFile)" Condition="'$(_StripLike)' == 'apple'"/>
62+
<!-- runtime build runs "codesign -f -s - libWhatever.dylib" in release configurations -->
63+
<Exec Command="codesign -f -s - $(StripSourceFile)" Condition="'$(RuntimeConfiguration)' == 'Release' and '$(_StripLike)' == 'apple'" />
64+
65+
<Exec Command="$(ObjCopyName) --only-keep-debug $(StripSourceFile) $(StripDestinationFile)" Condition="'$(_StripLike)' == 'gnu'"/>
66+
<Exec Command="$(ObjCopyName) --strip-debug --strip-unneeded $(StripSourceFile)" Condition="'$(_StripLike)' == 'gnu'"/>
67+
<Exec Command="$(ObjCopyName) --add-gnu-debuglink=$(StripDestinationFile) $(StripSourceFile)" Condition="'$(_StripLike)' == 'gnu'"/>
68+
</Target>
69+
70+
<Target Name="InstallRuntimeComponentToFinalDestination"
71+
AfterTargets="CopyNativeBinary"
72+
DependsOnTargets="CopyNativeBinary;StripLibraryLikeCoreCLRBuild" Condition="'$(IsRuntimeComponent)' == 'true'">
73+
<Error Text="Set at least one @InstallRuntimeComponentDestination item" Condition="@(InstallRuntimeComponentDestination->Count()) == 0" />
74+
75+
<PropertyGroup>
76+
<!-- FIXME: this is the same as CoreCLRToolPath - but that doesn't seem like a good name -->
77+
<FinalRuntimeComponentDestinationBase>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', '$(RuntimeFlavor.ToLower())', '$(TargetOS).$(TargetArchitecture).$(RuntimeConfiguration)'))</FinalRuntimeComponentDestinationBase>
78+
</PropertyGroup>
79+
80+
<ItemGroup>
81+
<_NormalizedInstallRuntimeComponentDest Include="$([MSBuild]::NormalizeDirectory('$(FinalRuntimeComponentDestinationBase)', '%(InstallRuntimeComponentDestination.Identity)'))" />
82+
</ItemGroup>
83+
84+
<ItemGroup Condition="'$(TargetsWindows)' != 'true'">
85+
<CopyFinalFiles Include="$(StripSourceFile)" />
86+
<CopyFinalFiles Include="$(StripDestinationFile)" />
87+
</ItemGroup>
88+
89+
<ItemGroup Condition="'$(TargetsWindows)' == 'true'">
90+
<CopyFinalFiles Include="$(PublishDir)$(TargetName)$(NativeBinaryExt)" />
91+
<CopyFinalFilesPDB Include="$(PublishDir)$(TargetName).pdb" />
92+
</ItemGroup>
93+
94+
<Message Importance="Normal" Text="Installing @(CopyFinalFiles) into %(_NormalizedInstallRuntimeComponentDest.Identity)"/>
95+
<Message Importance="Normal" Text="Installing @(CopyFinalFilesPDB) into %(_NormalizedInstallRuntimeComponentDest.Identity)PDB\" Condition="'$(TargetsWindows)' == 'true'"/>
96+
97+
<Copy SourceFiles="@(CopyFinalFiles)"
98+
DestinationFolder="%(_NormalizedInstallRuntimeComponentDest.Identity)"
99+
SkipUnchangedFiles="true"/>
100+
<Copy SourceFiles="@(CopyFinalFilesPDB)"
101+
DestinationFolder="%(_NormalizedInstallRuntimeComponentDest.Identity)PDB\"
102+
SkipUnchangedFiles="true"
103+
Condition="'$(TargetsWindows)' == 'true'"/>
104+
</Target>
105+
106+
107+
</Project>

0 commit comments

Comments
 (0)