diff --git a/csharp/src/Microsoft.ML.OnnxRuntime/NativeMethods.shared.cs b/csharp/src/Microsoft.ML.OnnxRuntime/NativeMethods.shared.cs
index 1ae7b5c9eb991..abe73b77f4071 100644
--- a/csharp/src/Microsoft.ML.OnnxRuntime/NativeMethods.shared.cs
+++ b/csharp/src/Microsoft.ML.OnnxRuntime/NativeMethods.shared.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
+using System.Reflection;
using System.Runtime.InteropServices;
using static Microsoft.ML.OnnxRuntime.NativeMethods;
@@ -474,6 +475,12 @@ internal static class NativeMethods
static NativeMethods()
{
+#if !NETSTANDARD2_0 && !__ANDROID__ && !__IOS__
+ // Register a custom DllImportResolver to handle platform-specific library loading.
+ // Replaces default resolution specifically on Windows for case-sensitivity.
+ NativeLibrary.SetDllImportResolver(typeof(NativeMethods).Assembly, DllImportResolver);
+#endif
+
#if NETSTANDARD2_0
IntPtr ortApiBasePtr = OrtGetApiBase();
OrtApiBase ortApiBase = (OrtApiBase)Marshal.PtrToStructure(ortApiBasePtr, typeof(OrtApiBase));
@@ -847,7 +854,7 @@ static NativeMethods()
api_.CreateSyncStreamForEpDevice,
typeof(DOrtCreateSyncStreamForEpDevice));
- OrtSyncStream_GetHandle =
+ OrtSyncStream_GetHandle =
(DOrtSyncStream_GetHandle)Marshal.GetDelegateForFunctionPointer(
api_.SyncStream_GetHandle,
typeof(DOrtSyncStream_GetHandle));
@@ -872,11 +879,127 @@ internal class NativeLib
// Define the library name required for iOS
internal const string DllName = "__Internal";
#else
- // Note: the file name in ONNX Runtime nuget package must be onnxruntime.dll instead of onnxruntime.DLL(Windows filesystem can be case sensitive)
- internal const string DllName = "onnxruntime.dll";
+ // For desktop platforms (including .NET Standard 2.0), we use the simple name
+ // to allow .NET's automatic platform-specific resolution (lib*.so, lib*.dylib, *.dll).
+ // For .NET Core 3.0+, case-sensitivity on Windows is handled by DllImportResolver.
+ internal const string DllName = "onnxruntime";
#endif
}
+#if !NETSTANDARD2_0 && !__ANDROID__ && !__IOS__
+ ///
+ /// Custom DllImportResolver to handle platform-specific library loading.
+ /// On Windows, it explicitly loads the library with a lowercase .dll extension to handle
+ /// case-sensitive filesystems.
+ ///
+ private static IntPtr DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
+ {
+ if (libraryName == NativeLib.DllName || libraryName == OrtExtensionsNativeMethods.ExtensionsDllName)
+ {
+ string mappedName = null;
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ // Explicitly load with .dll extension to avoid issues where the OS might try .DLL
+ mappedName = libraryName + ".dll";
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ // Explicitly load with .so extension and lib prefix
+ mappedName = "lib" + libraryName + ".so";
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ // Explicitly load with .dylib extension and lib prefix
+ mappedName = "lib" + libraryName + ".dylib";
+ }
+
+ if (mappedName != null)
+ {
+ // 1. Try default loading (name only)
+ if (NativeLibrary.TryLoad(mappedName, assembly, searchPath, out IntPtr handle))
+ {
+ return handle;
+ }
+
+ // 2. Try relative to assembly location (look into runtimes subfolders)
+ string assemblyLocation = null;
+ try { assemblyLocation = assembly.Location; } catch { }
+ if (!string.IsNullOrEmpty(assemblyLocation))
+ {
+ string assemblyDir = System.IO.Path.GetDirectoryName(assemblyLocation);
+ string rid = RuntimeInformation.RuntimeIdentifier;
+
+ // Probe the specific RID first, then common fallbacks for the current OS
+ string[] ridsToTry;
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ ridsToTry = new[] { rid, "win-x64", "win-arm64" };
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ ridsToTry = new[] { rid, "linux-x64", "linux-arm64" };
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ // We no longer provide osx-x64 in official package since 1.24.
+ // However, we keep it in the list for build-from-source users.
+ ridsToTry = new[] { rid, "osx-arm64", "osx-x64" };
+ }
+ else
+ {
+ ridsToTry = new[] { rid };
+ }
+
+ foreach (var tryRid in ridsToTry)
+ {
+ string probePath = System.IO.Path.Combine(assemblyDir, "runtimes", tryRid, "native", mappedName);
+ if (System.IO.File.Exists(probePath) && NativeLibrary.TryLoad(probePath, assembly, searchPath, out handle))
+ {
+ LogLibLoad($"[DllImportResolver] Loaded {mappedName} from: {probePath}");
+ return handle;
+ }
+ }
+ }
+
+ // 3. Try AppContext.BaseDirectory as a fallback
+ string baseDir = AppContext.BaseDirectory;
+ if (!string.IsNullOrEmpty(baseDir))
+ {
+ string probePath = System.IO.Path.Combine(baseDir, mappedName);
+ if (NativeLibrary.TryLoad(probePath, assembly, searchPath, out handle))
+ {
+ LogLibLoad($"[DllImportResolver] Loaded {mappedName} from: {probePath}");
+ return handle;
+ }
+
+ string rid = RuntimeInformation.RuntimeIdentifier;
+ probePath = System.IO.Path.Combine(baseDir, "runtimes", rid, "native", mappedName);
+ if (NativeLibrary.TryLoad(probePath, assembly, searchPath, out handle))
+ {
+ LogLibLoad($"[DllImportResolver] Loaded {mappedName} from: {probePath}");
+ return handle;
+ }
+ }
+
+ LogLibLoad($"[DllImportResolver] Failed loading {mappedName} (RID: {RuntimeInformation.RuntimeIdentifier}, Assembly: {assemblyLocation})");
+
+ }
+ }
+
+ // Fall back to default resolution
+ return IntPtr.Zero;
+ }
+
+ private static void LogLibLoad(string message)
+ {
+ System.Diagnostics.Trace.WriteLine(message);
+ if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("ORT_LOADER_VERBOSITY")))
+ {
+ Console.WriteLine(message);
+ }
+ }
+#endif
+
[DllImport(NativeLib.DllName, CharSet = CharSet.Ansi)]
#if NETSTANDARD2_0
public static extern IntPtr OrtGetApiBase();
@@ -2644,7 +2767,7 @@ public delegate void DOrtAddKeyValuePair(IntPtr /* OrtKeyValuePairs* */ kvps,
byte[] /* const char* */ value);
///
- /// Get the value for the provided key.
+ /// Get the value for the provided key.
///
/// Value. Returns IntPtr.Zero if key was not found.
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
@@ -2767,7 +2890,7 @@ out IntPtr /* OrtSyncStream** */ stream
// Auto Selection EP registration and selection customization
///
- /// Register an execution provider library.
+ /// Register an execution provider library.
/// The library must implement CreateEpFactories and ReleaseEpFactory.
///
/// Environment to add the EP library to.
@@ -2952,9 +3075,10 @@ internal static class OrtExtensionsNativeMethods
#elif __IOS__
internal const string ExtensionsDllName = "__Internal";
#else
- // For desktop platforms, explicitly specify the DLL name with extension to avoid
- // issues on case-sensitive filesystems. See NativeLib.DllName for detailed explanation.
- internal const string ExtensionsDllName = "ortextensions.dll";
+ // For desktop platforms, use the simple name to allow .NET's
+ // automatic platform-specific resolution (lib*.so, lib*.dylib, *.dll).
+ // Case-sensitivity on Windows is handled by DllImportResolver.
+ internal const string ExtensionsDllName = "ortextensions";
#endif
[DllImport(ExtensionsDllName, CharSet = CharSet.Ansi,
diff --git a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.NetCoreApp/InferenceTest.netcore.cs b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.NetCoreApp/InferenceTest.netcore.cs
index f0d1313783643..c0475bb6102c1 100644
--- a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.NetCoreApp/InferenceTest.netcore.cs
+++ b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.NetCoreApp/InferenceTest.netcore.cs
@@ -601,6 +601,29 @@ private static Dictionary GetSkippedModels(DirectoryInfo modelsD
skipModels["VGG 16-fp32"] = "bad allocation";
}
+ // The following models are from onnx repo and fail on MacOS nuget test pipeline.
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ var macOSSkips = new[]
+ {
+ "test_castlike_FLOAT_to_STRING_expanded",
+ "test_castlike_FLOAT_to_BFLOAT16_expanded",
+ "test_castlike_BFLOAT16_to_FLOAT",
+ "test_cast_FLOAT_to_STRING",
+ "test_castlike_FLOAT_to_BFLOAT16",
+ "test_castlike_STRING_to_FLOAT_expanded",
+ "test_castlike_STRING_to_FLOAT",
+ "test_cast_STRING_to_FLOAT",
+ "test_castlike_BFLOAT16_to_FLOAT_expanded",
+ "test_cast_BFLOAT16_to_FLOAT",
+ "test_castlike_FLOAT_to_STRING"
+ };
+ foreach (var model in macOSSkips)
+ {
+ skipModels[model] = "Skipped on macOS due to flakes or lack of support";
+ }
+ }
+
return skipModels;
}
@@ -934,6 +957,7 @@ public void TestPretrainedModelsWithOrtValue(string opsetDir, string modelName)
[MemberData(nameof(GetSkippedModelForTest), Skip = "Skipped due to Error, please fix the error and enable the test")]
private void TestPreTrainedModels(string opsetDir, string modelName, bool useOrtValueAPIs = false)
{
+
var opsetDirInfo = new DirectoryInfo(opsetDir);
var opset = opsetDirInfo.Name;
string onnxModelFileName = null;
diff --git a/tools/ci_build/github/azure-pipelines/c-api-noopenmp-test-pipelines.yml b/tools/ci_build/github/azure-pipelines/c-api-noopenmp-test-pipelines.yml
index 7242c5fe7b6a6..8d96c1ae99e0a 100644
--- a/tools/ci_build/github/azure-pipelines/c-api-noopenmp-test-pipelines.yml
+++ b/tools/ci_build/github/azure-pipelines/c-api-noopenmp-test-pipelines.yml
@@ -104,9 +104,18 @@ stages:
- template: nuget/templates/test_macos.yml
parameters:
- AgentPool: macOS-14
+ AgentPool: 'AcesShared'
+ UseHostedVmImage: 'false'
+ PoolDemands: 'ImageOverride -equals ACES_VM_SharedPool_Sequoia'
ArtifactSuffix: 'CPU'
+- template: nodejs/templates/test_macos.yml
+ parameters:
+ AgentPool: 'AcesShared'
+ UseHostedVmImage: 'false'
+ PoolDemands: 'ImageOverride -equals ACES_VM_SharedPool_Sequoia'
+ StageSuffix: 'MacOS_ARM64'
+
- template: nodejs/templates/test_win.yml
parameters:
AgentPool: 'onnxruntime-Win-CPU-VS2022-Latest'
@@ -117,10 +126,6 @@ stages:
AgentPool: 'onnxruntime-Ubuntu2204-AMD-CPU'
StageSuffix: 'Linux_CPU_x64'
-- template: nodejs/templates/test_macos.yml
- parameters:
- StageSuffix: 'macOS_CPU_x64'
-
- template: nuget/templates/test_win.yml
parameters:
AgentPool: 'onnxruntime-Win2022-GPU-A10'
@@ -225,7 +230,7 @@ stages:
- checkout: self
clean: true
submodules: none
-
+
- download: build
artifact: 'Windows_Packaging_tensorrt_build_artifacts'
displayName: 'Download Windows GPU Packages Build'
@@ -246,7 +251,7 @@ stages:
versionSpec: "17"
jdkArchitectureOption: x64
jdkSourceOption: 'PreInstalled'
-
+
- task: PythonScript@0
displayName: 'Update CTest Path References'
inputs:
diff --git a/tools/ci_build/github/azure-pipelines/nodejs/templates/test.yml b/tools/ci_build/github/azure-pipelines/nodejs/templates/test.yml
index ae595bbf0c96b..cd41fc575020b 100644
--- a/tools/ci_build/github/azure-pipelines/nodejs/templates/test.yml
+++ b/tools/ci_build/github/azure-pipelines/nodejs/templates/test.yml
@@ -6,12 +6,20 @@ steps:
- task: PowerShell@2
- displayName: 'Move Artifact Directory'
+ condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
+ displayName: 'Move Artifact Directory (Windows)'
inputs:
targetType: 'inline'
script: |
Move-Item -Path "$(Pipeline.Workspace)/build/NPM_packages" -Destination "$(Build.BinariesDirectory)/nodejs-artifact"
+- task: CmdLine@2
+ condition: and(succeeded(), ne(variables['Agent.OS'], 'Windows_NT'))
+ displayName: 'Move Artifact Directory (POSIX)'
+ inputs:
+ script: |
+ mv "$(Pipeline.Workspace)/build/NPM_packages" "$(Build.BinariesDirectory)/nodejs-artifact"
+
- script: mkdir e2e_test
workingDirectory: '$(Build.BinariesDirectory)'
@@ -38,4 +46,4 @@ steps:
npm init -y
npm install $(NpmPackageFilesForTest) --onnxruntime-node-install-cuda=skip
node -p "require('onnxruntime-node')"
- workingDirectory: '$(Build.BinariesDirectory)/e2e_test'
\ No newline at end of file
+ workingDirectory: '$(Build.BinariesDirectory)/e2e_test'
diff --git a/tools/ci_build/github/azure-pipelines/nodejs/templates/test_macos.yml b/tools/ci_build/github/azure-pipelines/nodejs/templates/test_macos.yml
index 4dd19ce2c250c..7e184492fab59 100644
--- a/tools/ci_build/github/azure-pipelines/nodejs/templates/test_macos.yml
+++ b/tools/ci_build/github/azure-pipelines/nodejs/templates/test_macos.yml
@@ -1,5 +1,9 @@
parameters:
StageSuffix: ''
+ AgentPool : 'macOS-15'
+ UseHostedVmImage: 'true'
+ PoolDemands: ''
+
stages:
- stage: Nodejs_Test_MacOS_${{ parameters.StageSuffix }}
dependsOn:
@@ -11,7 +15,12 @@ stages:
clean: all
timeoutInMinutes: 120
pool:
- vmImage: 'macOS-15'
+ ${{ if eq(parameters.UseHostedVmImage, 'true') }}:
+ vmImage: ${{ parameters.AgentPool }}
+ ${{ else }}:
+ name: ${{ parameters.AgentPool }}
+ ${{ if ne(parameters.PoolDemands, '') }}:
+ demands: ${{ parameters.PoolDemands }}
variables:
- name: OnnxRuntimeBuildDirectory
diff --git a/tools/ci_build/github/azure-pipelines/nuget/templates/test_macos.yml b/tools/ci_build/github/azure-pipelines/nuget/templates/test_macos.yml
index 1d122d64b1211..5fc52e2c76468 100644
--- a/tools/ci_build/github/azure-pipelines/nuget/templates/test_macos.yml
+++ b/tools/ci_build/github/azure-pipelines/nuget/templates/test_macos.yml
@@ -1,6 +1,10 @@
parameters:
+ AgentPool : 'macOS-15'
+ UseHostedVmImage: 'true'
IsMacOS : 'true'
ArtifactSuffix: ''
+ PoolDemands: ''
+
stages:
- stage: NuGet_Test_MacOS
dependsOn:
@@ -11,7 +15,12 @@ stages:
workspace:
clean: all
pool:
- vmImage: 'macOS-15'
+ ${{ if eq(parameters.UseHostedVmImage, 'true') }}:
+ vmImage: ${{ parameters.AgentPool }}
+ ${{ else }}:
+ name: ${{ parameters.AgentPool }}
+ ${{ if ne(parameters.PoolDemands, '') }}:
+ demands: ${{ parameters.PoolDemands }}
variables:
- name: OnnxRuntimeBuildDirectory
@@ -27,18 +36,36 @@ stages:
- script: |
mv $(Pipeline.Workspace)/build/drop-signed-nuget-${{ parameters.ArtifactSuffix }} $(Build.BinariesDirectory)/nuget-artifact
- mv $(Pipeline.Workspace)/build/onnxruntime-osx $(Build.BinariesDirectory)/testdata
+
+ # Artifact is a folder containing tgz. Extract it to testdata.
+ mkdir -p $(Build.BinariesDirectory)/testdata
+ for archive in $(Pipeline.Workspace)/build/onnxruntime-osx/*.tgz; do
+ tar -xzf "$archive" -C $(Build.BinariesDirectory)/testdata
+ done
+
+ # Ensure libcustom_op_library.dylib is where EndToEndTests expects it (testdata/testdata)
+ mkdir -p $(Build.BinariesDirectory)/testdata/testdata
+ find $(Build.BinariesDirectory)/testdata -name "libcustom_op_library.dylib" -exec cp {} $(Build.BinariesDirectory)/testdata/testdata/ \;
+
- template: get-nuget-package-version-as-variable.yml
parameters:
packageFolder: '$(Build.BinariesDirectory)/nuget-artifact'
+ - script: |
+ git submodule update --init cmake/external/onnx
+ cd cmake/external/onnx
+ git fetch origin v1.13.1 --depth=1
+ git checkout v1.13.1
+ cd ../../..
+ displayName: 'Initialize ONNX submodule for test data (pinned to v1.13.1 since new data types like float8 is not supported in nuget)'
+
- script: |
$(Build.SourcesDirectory)/csharp/test/Microsoft.ML.OnnxRuntime.EndToEndTests/runtest.sh \
$(Build.BinariesDirectory)/nuget-artifact \
$(NuGetPackageVersionNumber) \
true
-
+
if [ $? -ne 0 ]; then
echo "Failed to run test"
exit 1
@@ -48,4 +75,5 @@ stages:
OnnxRuntimeBuildDirectory: $(Build.BinariesDirectory)
DisableContribOps: $(DisableContribOps)
DisableMlOps: $(DisableMlOps)
- IsReleaseBuild: $(IsReleaseBuild)
\ No newline at end of file
+ IsReleaseBuild: $(IsReleaseBuild)
+ ORT_LOADER_VERBOSITY: 1
diff --git a/tools/ci_build/github/azure-pipelines/templates/mac-cpu-packaging-steps.yml b/tools/ci_build/github/azure-pipelines/templates/mac-cpu-packaging-steps.yml
index 45f7268b9661d..795945a8581ba 100644
--- a/tools/ci_build/github/azure-pipelines/templates/mac-cpu-packaging-steps.yml
+++ b/tools/ci_build/github/azure-pipelines/templates/mac-cpu-packaging-steps.yml
@@ -26,6 +26,15 @@ steps:
args: '-r $(Build.BinariesDirectory) -a onnxruntime-osx-${{ parameters.MacosArch }}-$(OnnxRuntimeVersion) -l libonnxruntime.$(OnnxRuntimeVersion).dylib -c Release -s $(Build.SourcesDirectory) -t $(Build.SourceVersion)'
workingDirectory: '$(Build.BinariesDirectory)/Release'
+- bash: |
+ mkdir -p $(Build.BinariesDirectory)/onnxruntime-osx-${{ parameters.MacosArch }}-$(OnnxRuntimeVersion)/testdata
+ cp $(Build.BinariesDirectory)/Release/libcustom_op_library.dylib $(Build.BinariesDirectory)/onnxruntime-osx-${{ parameters.MacosArch }}-$(OnnxRuntimeVersion)/testdata/libcustom_op_library.dylib
+ # Copy to testdata/testdata so EndToEndTests can find it when running in Debug configuration
+ mkdir -p $(Build.BinariesDirectory)/testdata/testdata
+ cp $(Build.BinariesDirectory)/Release/libcustom_op_library.dylib $(Build.BinariesDirectory)/testdata/testdata/libcustom_op_library.dylib
+ displayName: 'Copy custom op library'
+ condition: succeeded()
+
- task: ArchiveFiles@2
inputs:
rootFolderOrFile: '$(Build.BinariesDirectory)/onnxruntime-osx-${{ parameters.MacosArch }}-$(OnnxRuntimeVersion)'
diff --git a/tools/ci_build/github/linux/copy_strip_binary.sh b/tools/ci_build/github/linux/copy_strip_binary.sh
index f5b4c38c85d4c..88eff3ebff86a 100755
--- a/tools/ci_build/github/linux/copy_strip_binary.sh
+++ b/tools/ci_build/github/linux/copy_strip_binary.sh
@@ -27,6 +27,17 @@ if [[ $LIB_NAME == *.dylib ]]
then
dsymutil $BINARY_DIR/$ARTIFACT_NAME/lib/$LIB_NAME -o $BINARY_DIR/$ARTIFACT_NAME/lib/$LIB_NAME.dSYM
strip -S $BINARY_DIR/$ARTIFACT_NAME/lib/$LIB_NAME
+
+ # ORT NuGet packaging expects the unversioned library (libonnxruntime.dylib) to contain the binary content,
+ # because the versioned library is excluded by the nuspec generation script.
+ # We explicitly overwrite the symlink with the real file to ensure 'nuget pack' (especially on Windows)
+ # doesn't pack an empty/broken symlink.
+ # Only applies to versioned libonnxruntime libraries (e.g. libonnxruntime.1.24.0.dylib).
+ if [[ "$LIB_NAME" =~ ^libonnxruntime\..*\.dylib$ && -L "$BINARY_DIR/$ARTIFACT_NAME/lib/libonnxruntime.dylib" ]]; then
+ rm "$BINARY_DIR/$ARTIFACT_NAME/lib/libonnxruntime.dylib"
+ cp "$BINARY_DIR/$ARTIFACT_NAME/lib/$LIB_NAME" "$BINARY_DIR/$ARTIFACT_NAME/lib/libonnxruntime.dylib"
+ fi
+
# copy the CoreML EP header for macOS build (libs with .dylib ext)
cp $SOURCE_DIR/include/onnxruntime/core/providers/coreml/coreml_provider_factory.h $BINARY_DIR/$ARTIFACT_NAME/include
else
diff --git a/tools/ci_build/github/linux/docker/Dockerfile.package_ubuntu_2404_gpu b/tools/ci_build/github/linux/docker/Dockerfile.package_ubuntu_2404_gpu
index 766a2c8a8b73b..0c63b7775256a 100644
--- a/tools/ci_build/github/linux/docker/Dockerfile.package_ubuntu_2404_gpu
+++ b/tools/ci_build/github/linux/docker/Dockerfile.package_ubuntu_2404_gpu
@@ -49,7 +49,9 @@ RUN apt-get update && \
libnvonnxparsers-dev=${TRT_VERSION} \
libnvonnxparsers10=${TRT_VERSION} \
tensorrt-dev=${TRT_VERSION} \
- libnvinfer-bin=${TRT_VERSION} && \
+ libnvinfer-bin=${TRT_VERSION} \
+ libnvinfer-headers-python-plugin-dev=${TRT_VERSION} \
+ libnvinfer-win-builder-resource10=${TRT_VERSION} && \
rm -rf /var/lib/apt/lists/*
COPY scripts /tmp/scripts