diff --git a/csharp/ApiDocs/ApiDocs.csproj b/csharp/ApiDocs/ApiDocs.csproj index a56e605022..644dff0c7e 100644 --- a/csharp/ApiDocs/ApiDocs.csproj +++ b/csharp/ApiDocs/ApiDocs.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 enable diff --git a/csharp/Nuget.CSharp.config b/csharp/NuGet.CSharp.config similarity index 100% rename from csharp/Nuget.CSharp.config rename to csharp/NuGet.CSharp.config diff --git a/csharp/src/Microsoft.ML.OnnxRuntime/Microsoft.ML.OnnxRuntime.csproj b/csharp/src/Microsoft.ML.OnnxRuntime/Microsoft.ML.OnnxRuntime.csproj index 78083a8cc1..39d90c55b7 100644 --- a/csharp/src/Microsoft.ML.OnnxRuntime/Microsoft.ML.OnnxRuntime.csproj +++ b/csharp/src/Microsoft.ML.OnnxRuntime/Microsoft.ML.OnnxRuntime.csproj @@ -21,7 +21,7 @@ --> PreNet6 - netstandard1.1;netstandard2.0;net5.0;netcoreapp3.1 + netstandard1.1;netstandard2.0;netcoreapp3.1;net6.0 An span of integers that represent the size of each dimension of the Tensor to create. - /// False (default) to indicate that the first dimension is most major (farthest apart) and the last dimension is most minor (closest together): akin to row-major in a rank-2 tensor. True to indicate that the last dimension is most major (farthest apart) and the first dimension is most minor (closest together): akin to column-major in a rank-2 tensor. + /// + /// An span of integers that represent the size of each dimension of the Tensor to create. + /// + /// False (default) to indicate that the first dimension is most major (farthest apart) and the last dimension + /// is most minor (closest together): akin to row-major in a rank-2 tensor. + /// True to indicate that the last dimension is most major (farthest apart) and the first dimension is most + /// minor (closest together): akin to column-major in a rank-2 tensor. + /// If you pass `null` for dimensions it will implicitly convert to an empty ReadOnlySpan, which is + /// equivalent to the dimensions for a scalar value. protected Tensor(ReadOnlySpan dimensions, bool reverseStride) : base(typeof(T)) { - if (dimensions == null) - { - throw new ArgumentNullException(nameof(dimensions)); - } - this.dimensions = new int[dimensions.Length]; long size = 1; for (int i = 0; i < dimensions.Length; i++) diff --git a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/InferenceTest.cs b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/InferenceTest.cs index e662676da9..5ebca9b05b 100644 --- a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/InferenceTest.cs +++ b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/InferenceTest.cs @@ -108,7 +108,19 @@ namespace Microsoft.ML.OnnxRuntime.Tests var directml_dll_path = AppDomain.CurrentDomain.BaseDirectory; SetDllDirectory(directml_dll_path); - opt.AppendExecutionProvider_DML(0); + + try + { + opt.AppendExecutionProvider_DML(0); + } + catch (OnnxRuntimeException ortException) + { + // if we run on a CI machine with the incorrect hardware we might get an error due to that. + // allow that as the call made it through to the DML EP so the C# layer is working correctly. + // any other exception type or error message is considered a failure. + Assert.Contains("The specified device interface or feature level is not supported on this system.", + ortException.Message); + } // Restore the default dll search order SetDllDirectory(null); diff --git a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/Microsoft.ML.OnnxRuntime.Tests.Common.csproj b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/Microsoft.ML.OnnxRuntime.Tests.Common.csproj index cc4cc5ea65..79de2b95ca 100644 --- a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/Microsoft.ML.OnnxRuntime.Tests.Common.csproj +++ b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/Microsoft.ML.OnnxRuntime.Tests.Common.csproj @@ -1,17 +1,19 @@  - netstandard2.0 + + netstandard2.0;net6.0 false $(ProjectDir)..\.. - AnyCPU;x86 + AnyCPU bin\$(Configuration)\ - true - true - true - $(OnnxRuntimeCsharpRoot)\..\cmake\external\onnx;\..\cmake\external\onnx\onnx + true + true + true + $(OnnxRuntimeCsharpRoot)\..\cmake\external\onnx - + 7.2 True true @@ -48,6 +50,7 @@ + @@ -75,9 +78,9 @@ - - - + + + @@ -92,9 +95,27 @@ TextTemplatingFileGenerator TensorOperations.cs - + + + PreserveNewest + false + + + + PreserveNewest + false + + + + PreserveNewest + false + + + + + @@ -106,11 +127,11 @@ - + - + @@ -137,4 +158,4 @@ - \ No newline at end of file + diff --git a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/OrtEnvTests.cs b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/OrtEnvTests.cs index 63ddf51116..83d32fa54b 100644 --- a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/OrtEnvTests.cs +++ b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/OrtEnvTests.cs @@ -148,7 +148,6 @@ namespace Microsoft.ML.OnnxRuntime.Tests [Collection("Ort Inference Tests")] public class OrtEnvWithCustomLogger : CustomLoggingFunctionTestBase { - [Fact(DisplayName = "TesEnvWithCustomLogger")] public void TesEnvWithCustomLogger() { @@ -172,7 +171,8 @@ namespace Microsoft.ML.OnnxRuntime.Tests // Trigger some logging // Empty stmt intentional using (var session = new InferenceSession(model)) - ; + { } + Assert.True(LoggingInvokes > 0); } } @@ -205,8 +205,9 @@ namespace Microsoft.ML.OnnxRuntime.Tests var model = TestDataLoader.LoadModelFromEmbeddedResource("squeezenet.onnx"); // Trigger some logging // Empty stmt intentional - using (var session = new InferenceSession(model)) - ; + using (var session = new InferenceSession(model)) + { } + Assert.True(LoggingInvokes > 0); } } diff --git a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/Tensors/NativeMemory.cs b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/Tensors/NativeMemory.cs index 019c3d58cd..d014dc0766 100644 --- a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/Tensors/NativeMemory.cs +++ b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/Tensors/NativeMemory.cs @@ -38,10 +38,14 @@ namespace Microsoft.ML.OnnxRuntime.Tensors.Tests this.length = length; } + // A finalizer to allow debugging in test code is allowable. + // https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2015 +#pragma warning disable CA2015 ~NativeMemory() { Dispose(false); } +#pragma warning restore CA2015 public static NativeMemory Allocate(int length) { diff --git a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/Tensors/TensorTests.cs b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/Tensors/TensorTests.cs index 7816babf48..4088663ea7 100644 --- a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/Tensors/TensorTests.cs +++ b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/Tensors/TensorTests.cs @@ -159,7 +159,12 @@ namespace Microsoft.ML.OnnxRuntime.Tensors.Tests Assert.Equal(24, tensor.Length); Assert.Equal(tensorConstructor.IsReversedStride, tensor.IsReversedStride); - Assert.Throws("dimensions", () => tensorConstructor.CreateFromDimensions(dimensions: null)); + // The null is converted to a 'null' ReadOnlySpan which is a valid instance with length zero. + // https://learn.microsoft.com/en-us/dotnet/api/system.readonlyspan-1.-ctor?view=netstandard-2.1#system-readonlyspan-1-ctor(-0()) + // Such a span is valid as dimensions as it represents a scalar. + // If we need to differentiate between the two we'd need to change the Tensor ctor to accept a + // nullable span in order to tell the user that's invalid. + // Assert.Throws("dimensions", () => tensorConstructor.CreateFromDimensions(dimensions: null)); Assert.Throws("dimensions", () => tensorConstructor.CreateFromDimensions(dimensions: new[] { 1, -1 })); // ensure dimensions are immutable diff --git a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/TestDataLoader.cs b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/TestDataLoader.cs index 42a780b328..08d3d9d6f2 100644 --- a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/TestDataLoader.cs +++ b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/TestDataLoader.cs @@ -1,12 +1,8 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; -using System.Net.NetworkInformation; -using Google.Protobuf; using Microsoft.ML.OnnxRuntime.Tensors; -using Xunit; namespace Microsoft.ML.OnnxRuntime.Tests { diff --git a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Droid/Microsoft.ML.OnnxRuntime.Tests.Droid.csproj b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Droid/Microsoft.ML.OnnxRuntime.Tests.Droid.csproj index 78cfc5ca80..020a4745e2 100644 --- a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Droid/Microsoft.ML.OnnxRuntime.Tests.Droid.csproj +++ b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Droid/Microsoft.ML.OnnxRuntime.Tests.Droid.csproj @@ -173,4 +173,4 @@ --> - + \ No newline at end of file diff --git a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.NetCoreApp/Microsoft.ML.OnnxRuntime.Tests.NetCoreApp.csproj b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.NetCoreApp/Microsoft.ML.OnnxRuntime.Tests.NetCoreApp.csproj index 4f99be4882..38cea3f23a 100644 --- a/csharp/test/Microsoft.ML.OnnxRuntime.Tests.NetCoreApp/Microsoft.ML.OnnxRuntime.Tests.NetCoreApp.csproj +++ b/csharp/test/Microsoft.ML.OnnxRuntime.Tests.NetCoreApp/Microsoft.ML.OnnxRuntime.Tests.NetCoreApp.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net6.0 false $(ProjectDir)..\.. AnyCPU;x86 @@ -53,13 +53,39 @@ - + + - + + PreserveNewest + false + + + + PreserveNewest + false + + + PreserveNewest false diff --git a/tools/ci_build/github/azure-pipelines/linux-ci-pipeline.yml b/tools/ci_build/github/azure-pipelines/linux-ci-pipeline.yml index 1ea19e5734..d372699aa5 100644 --- a/tools/ci_build/github/azure-pipelines/linux-ci-pipeline.yml +++ b/tools/ci_build/github/azure-pipelines/linux-ci-pipeline.yml @@ -78,6 +78,7 @@ stages: --build_shared_lib \ --parallel \ --build_wheel \ + --build_csharp \ --enable_onnx_tests \ --enable_transformers_tool_test \ --use_cache \ @@ -86,6 +87,44 @@ stages: ccache -z" workingDirectory: $(Build.SourcesDirectory) + - task: UseDotNet@2 + displayName: "Setup dotnet" + inputs: + version: '6.0.408' + + - task: DotNetCoreCLI@2 + displayName: "Restore C# packages" + inputs: + command: 'restore' + projects: '$(Build.SourcesDirectory)/csharp/OnnxRuntime.DesktopOnly.CSharp.sln' + + # the props file was generated with docker container paths. convert to the 'real' path by replacing the + # the container path of '/build'. The '>' prefix is to match the closing angle bracket of the tag. + # e.g. /build/... so we only match the start of a path. + # We use powershell so we don't need extra escaping of the '/' chars in the path. + - task: CmdLine@2 + displayName: 'Update props from docker path to local and create models link' + inputs: + script: | + pwsh -Command '(Get-Content $(Build.SourcesDirectory)/csharp/Directory.Build.props) -replace ">/build", ">$(Build.BinariesDirectory)" | Set-Content $(Build.SourcesDirectory)/csharp/Directory.Build.props' + cat $(Build.SourcesDirectory)/csharp/Directory.Build.props + ln -s /data/models $(Build.BinariesDirectory)/models + + - task: DotNetCoreCLI@2 + displayName: 'dotnet build C# sln' + inputs: + command: 'build' + projects: '$(Build.SourcesDirectory)/csharp/OnnxRuntime.DesktopOnly.CSharp.sln' + + - task: DotNetCoreCLI@2 + displayName: 'dotnet test C#' + inputs: + command: 'test' + projects: '$(Build.SourcesDirectory)/csharp/OnnxRuntime.DesktopOnly.CSharp.sln' + # extra logging so all tests are listed in output to validate what's actually run + arguments: '-f net6.0 --no-build -l "console;verbosity=normal"' + workingDirectory: $(Build.SourcesDirectory)/csharp + - task: CmdLine@2 displayName: 'Install python deps and run java tests' inputs: