Add Microsoft.ML.OnnxRuntime nuget props for Linux and MacOS#27328
Add Microsoft.ML.OnnxRuntime nuget props for Linux and MacOS#27328
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the C# NuGet build props so native ONNX Runtime binaries are copied into the build output on Linux/macOS (similar to existing Windows behavior), enabling simplification of the custom DllImportResolver.
Changes:
- Added MSBuild
ItemGroupentries inprops.xmlto copy Linux/macOS native libraries (and some provider libs) fromruntimes/{rid}/nativeinto the output directory. - Simplified
NativeMethods.shared.csDllImportResolverby removing manual probing ofruntimes/{rid}/nativeand extra logging helpers.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| csharp/src/Microsoft.ML.OnnxRuntime/targets/netstandard/props.xml | Adds copy-to-output rules for Linux/macOS native assets to make runtime loading rely on standard probing. |
| csharp/src/Microsoft.ML.OnnxRuntime/NativeMethods.shared.cs | Removes custom RID probing logic and keeps only platform name-mapping + single TryLoad. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| <None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-x64\native\libonnxruntime.so" | ||
| Condition="('$(PlatformTarget)' == 'x64' OR ('$(PlatformTarget)' == 'AnyCPU' AND '$(Prefer32Bit)' != 'true')) AND | ||
| Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-x64\native\libonnxruntime.so')"> | ||
| <Link>libonnxruntime.so</Link> | ||
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
| <Visible>false</Visible> | ||
| </None> |
Copilot
AI
Feb 12, 2026
•
There was a problem hiding this comment.
The Condition for copying linux-x64 native assets is based only on PlatformTarget/Prefer32Bit. This will also evaluate true when building on non-Linux hosts (e.g., Windows x64) and can copy Linux .so files into the output unintentionally. Consider conditioning on the target/runtime RID (e.g.,
There was a problem hiding this comment.
We probably need to update all the copy commands to use the runtime identifier as the condition, but getting that can be non-trivial.
AI suggested set of steps:
<PropertyGroup>
<!-- Primary: Use explicit RID if set -->
<_ResolvedRuntimeIdentifier Condition="'$(RuntimeIdentifier)' != ''">
$(RuntimeIdentifier)
</_ResolvedRuntimeIdentifier>
<!-- SDK internal fallback (works in most modern SDK builds) -->
<_ResolvedRuntimeIdentifier Condition="'$(_ResolvedRuntimeIdentifier)' == '' AND '$(NETCoreSdkRuntimeIdentifier)' != ''">
$(NETCoreSdkRuntimeIdentifier)
</_ResolvedRuntimeIdentifier>
<!-- .NET Framework fallback -->
<_ResolvedRuntimeIdentifier Condition="'$(_ResolvedRuntimeIdentifier)' == '' AND '$(TargetFrameworkIdentifier)' == '.NETFramework'">
win
</_ResolvedRuntimeIdentifier>
<!-- netstandard has no runtime -->
<_ResolvedRuntimeIdentifier Condition="'$(_ResolvedRuntimeIdentifier)' == '' AND '$(TargetFrameworkIdentifier)' == '.NETStandard'">
any
</_ResolvedRuntimeIdentifier>
<!-- .NET (Core / 5+) fallback -->
<_ResolvedRuntimeIdentifier Condition="'$(_ResolvedRuntimeIdentifier)' == '' AND '$(TargetFrameworkIdentifier)' == '.NETCoreApp'">
portable
</_ResolvedRuntimeIdentifier>
<!-- Final exposed property -->
<ResolvedRuntimeIdentifier>$(_ResolvedRuntimeIdentifier)</ResolvedRuntimeIdentifier>
</PropertyGroup>
There was a problem hiding this comment.
And for added fun, if it's an AnyCPU build we probably need to copy both x64/arm64 if available to the output dir and put them under runtimes/<runtime id>/native/.... Sorry - I overlooked that use case.
There was a problem hiding this comment.
Taking a step back, if we have the probing looking at runtimes for development scenarios do we need manual copying at all? The 'publish' step might take care of copying the correct binaries for the publish target so we could be unnecessarily complicating things.
| Condition="'$(PlatformTarget)' == 'ARM64' AND | ||
| Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime.so')"> | ||
| <Link>libonnxruntime.so</Link> | ||
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
| <Visible>false</Visible> | ||
| </None> | ||
| <None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_shared.so" | ||
| Condition="'$(PlatformTarget)' == 'ARM64' AND | ||
| Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_shared.so')"> | ||
| <Link>libonnxruntime_providers_shared.so</Link> | ||
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
| <Visible>false</Visible> | ||
| </None> | ||
| <None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_cuda.so" | ||
| Condition="'$(PlatformTarget)' == 'ARM64' AND | ||
| Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_cuda.so')"> | ||
| <Link>libonnxruntime_providers_cuda.so</Link> | ||
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
| <Visible>false</Visible> | ||
| </None> | ||
| <None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_dnnl.so" | ||
| Condition="'$(PlatformTarget)' == 'ARM64' AND |
There was a problem hiding this comment.
linux-arm64 assets are copied only when
| Condition="'$(PlatformTarget)' == 'ARM64' AND | |
| Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime.so')"> | |
| <Link>libonnxruntime.so</Link> | |
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | |
| <Visible>false</Visible> | |
| </None> | |
| <None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_shared.so" | |
| Condition="'$(PlatformTarget)' == 'ARM64' AND | |
| Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_shared.so')"> | |
| <Link>libonnxruntime_providers_shared.so</Link> | |
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | |
| <Visible>false</Visible> | |
| </None> | |
| <None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_cuda.so" | |
| Condition="'$(PlatformTarget)' == 'ARM64' AND | |
| Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_cuda.so')"> | |
| <Link>libonnxruntime_providers_cuda.so</Link> | |
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | |
| <Visible>false</Visible> | |
| </None> | |
| <None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_dnnl.so" | |
| Condition="'$(PlatformTarget)' == 'ARM64' AND | |
| Condition="('$(PlatformTarget)' == 'ARM64' OR '$(NETCoreSdkRuntimeIdentifier)' == 'linux-arm64') AND | |
| Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime.so')"> | |
| <Link>libonnxruntime.so</Link> | |
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | |
| <Visible>false</Visible> | |
| </None> | |
| <None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_shared.so" | |
| Condition="('$(PlatformTarget)' == 'ARM64' OR '$(NETCoreSdkRuntimeIdentifier)' == 'linux-arm64') AND | |
| Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_shared.so')"> | |
| <Link>libonnxruntime_providers_shared.so</Link> | |
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | |
| <Visible>false</Visible> | |
| </None> | |
| <None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_cuda.so" | |
| Condition="('$(PlatformTarget)' == 'ARM64' OR '$(NETCoreSdkRuntimeIdentifier)' == 'linux-arm64') AND | |
| Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_cuda.so')"> | |
| <Link>libonnxruntime_providers_cuda.so</Link> | |
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | |
| <Visible>false</Visible> | |
| </None> | |
| <None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_dnnl.so" | |
| Condition="('$(PlatformTarget)' == 'ARM64' OR '$(NETCoreSdkRuntimeIdentifier)' == 'linux-arm64') AND |
| <None Include="$(MSBuildThisFileDirectory)..\..\runtimes\osx-x64\native\libonnxruntime.dylib" | ||
| Condition="('$(PlatformTarget)' == 'x64' OR ('$(PlatformTarget)' == 'AnyCPU' AND '$(Prefer32Bit)' != 'true')) AND | ||
| Exists('$(MSBuildThisFileDirectory)..\..\runtimes\osx-x64\native\libonnxruntime.dylib')"> | ||
| <Link>libonnxruntime.dylib</Link> | ||
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
| <Visible>false</Visible> | ||
| </None> | ||
| <None Include="$(MSBuildThisFileDirectory)..\..\runtimes\osx-x64\native\libonnxruntime_providers_shared.dylib" | ||
| Condition="('$(PlatformTarget)' == 'x64' OR ('$(PlatformTarget)' == 'AnyCPU' AND '$(Prefer32Bit)' != 'true')) AND | ||
| Exists('$(MSBuildThisFileDirectory)..\..\runtimes\osx-x64\native\libonnxruntime_providers_shared.dylib')"> | ||
| <Link>libonnxruntime_providers_shared.dylib</Link> | ||
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
| <Visible>false</Visible> | ||
| </None> | ||
|
|
||
| <!-- osx-arm64 --> | ||
| <None Include="$(MSBuildThisFileDirectory)..\..\runtimes\osx-arm64\native\libonnxruntime.dylib" | ||
| Condition="'$(PlatformTarget)' == 'ARM64' AND | ||
| Exists('$(MSBuildThisFileDirectory)..\..\runtimes\osx-arm64\native\libonnxruntime.dylib')"> | ||
| <Link>libonnxruntime.dylib</Link> | ||
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
| <Visible>false</Visible> | ||
| </None> |
There was a problem hiding this comment.
The osx-x64 block is gated by the same x64/AnyCPU PlatformTarget condition, and osx-arm64 is gated by PlatformTarget == 'ARM64'. On Apple Silicon, PlatformTarget is commonly AnyCPU/empty, which could result in no macOS native assets being copied (and osx-x64 assets likely don’t exist in official packages). Suggest using
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
| <Visible>false</Visible> | ||
| </None> | ||
| <None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-x64\native\libonnxruntime_providers_tensorrt.so" |
There was a problem hiding this comment.
Do we build this in a package?
|
Replaced by #27351 for a better solution. |
Description
This PR updates props.xml of ONNX Runtime NuGet package, specifically on macOS and Linux. It aligns the behavior of non-Windows platforms with the existing Windows logic by ensuring native libraries are copied to the build output directory. This change allows for a significant simplification of the
DllImportResolverinNativeMethods.shared.cs.Technical Details
1. props.xml Update
ItemGroupentries forlinux-x64,linux-arm64,osx-x64, andosx-arm64.libonnxruntime.so,libonnxruntime.dylib) and shared provider libraries (CUDA, DNNL, TensorRT, OpenVINO) from the package'sruntimesdirectory to the build output directory. This ensures they are present where the .NET runtime (and DllImport in NativeMethods.shared.cs) expects them.2. DllImportResolver Simplification
.../runtimes/{rid}/native.onnxruntimetolibonnxruntime.soorlibonnxruntime.dylib).LogLibLoad).Motivation
This is a follow up of #27266 (comment).
Previously, the DllImportResolver had to manually traverse directory structures to find native assets on non-Windows platforms because they weren't being copied to the bin folder. This was fragile and inconsistent with the Windows behavior. By copying the assets, we leverage standard .NET loading mechanisms and simplify the custom resolver code.