Skip to content

Add Microsoft.ML.OnnxRuntime nuget props for Linux and MacOS#27328

Closed
tianleiwu wants to merge 1 commit intomainfrom
tlwu/20260212/ort_nuget_props_linux_macos
Closed

Add Microsoft.ML.OnnxRuntime nuget props for Linux and MacOS#27328
tianleiwu wants to merge 1 commit intomainfrom
tlwu/20260212/ort_nuget_props_linux_macos

Conversation

@tianleiwu
Copy link
Contributor

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 DllImportResolver in NativeMethods.shared.cs.

Technical Details

1. props.xml Update

  • File: csharp/src/Microsoft.ML.OnnxRuntime/targets/netstandard/props.xml
  • Change: Added ItemGroup entries for linux-x64, linux-arm64, osx-x64, and osx-arm64.
  • Logic: These entries conditionally copy the native libraries (e.g., libonnxruntime.so, libonnxruntime.dylib) and shared provider libraries (CUDA, DNNL, TensorRT, OpenVINO) from the package's runtimes directory 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

  • File: csharp/src/Microsoft.ML.OnnxRuntime/NativeMethods.shared.cs
  • Change: Removed the manual "Step 2" probing logic that searched .../runtimes/{rid}/native.
  • Reasoning: Since the libraries are now copied to the output directory (AppDomain base), the explicit directory probing is redundant.
  • Details:
    • Retained the platform-specific name mapping logic (e.g., mapping onnxruntime to libonnxruntime.so or libonnxruntime.dylib).
    • Removed unused helper methods (LogLibLoad).
    • Updated comments to reflect cross-platform support.

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.

@tianleiwu tianleiwu requested a review from skottmckay February 12, 2026 09:21
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 ItemGroup entries in props.xml to copy Linux/macOS native libraries (and some provider libs) from runtimes/{rid}/native into the output directory.
  • Simplified NativeMethods.shared.cs DllImportResolver by removing manual probing of runtimes/{rid}/native and 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.

Comment on lines +147 to +153
<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>
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

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., $(RuntimeIdentifier) / $(NETCoreSdkRuntimeIdentifier)) and/or $([MSBuild]::IsOSPlatform('Linux')) so only the intended platform’s native assets are copied.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

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

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>

Copy link
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Contributor

Choose a reason for hiding this comment

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

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.

Comment on lines +192 to +213
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
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

linux-arm64 assets are copied only when $(PlatformTarget) == 'ARM64'. In many .NET SDK-style projects (including the 'portable .NETCoreApp' case noted in targets.xml), PlatformTarget is often empty/AnyCPU even on arm64 machines, so this block may not run and the native library won’t be copied—while NativeMethods.shared.cs now relies on default probing. Please key this off the runtime RID (e.g., linux-arm64) or $(NETCoreSdkRuntimeIdentifier) to correctly select arm64 assets when running/building on arm64.

Suggested change
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

Copilot uses AI. Check for mistakes.
Comment on lines +235 to +257
<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>
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

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 $(RuntimeIdentifier)/$(NETCoreSdkRuntimeIdentifier) to choose between osx-arm64 vs osx-x64, and/or $([MSBuild]::IsOSPlatform('OSX')) to avoid copying macOS libs on other hosts.

Copilot uses AI. Check for mistakes.
@tianleiwu tianleiwu marked this pull request as draft February 12, 2026 17:54
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-x64\native\libonnxruntime_providers_tensorrt.so"
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we build this in a package?

@tianleiwu
Copy link
Contributor Author

Replaced by #27351 for a better solution.

@tianleiwu tianleiwu closed this Feb 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants