Various fixes to the CSharp setup (#15782)

### Description
<!-- Describe your changes. -->
Various fixes to the CSharp setup
- fix warnings
- fix invalid tests
- update test sdk nuget package
  - enables testing on linux
  - fixes issue with some unit tests not running in CI
- run unit tests in linux pipeline using dotnet

### Motivation and Context
<!-- - Why is this change required? What problem does it solve?
- If it fixes an open issue, please link to the issue here. -->
Unit tests weren't breaking in CIs for both Windows and Linux builds and
should have been.
This commit is contained in:
Scott McKay 2023-05-05 14:27:30 +10:00 committed by GitHub
parent 272aab4afa
commit d1b2b35cd2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 147 additions and 39 deletions

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>

View file

@ -21,7 +21,7 @@
-->
<PropertyGroup>
<SelectedTargets>PreNet6</SelectedTargets>
<BaseTargets>netstandard1.1;netstandard2.0;net5.0;netcoreapp3.1</BaseTargets>
<BaseTargets>netstandard1.1;netstandard2.0;netcoreapp3.1;net6.0</BaseTargets>
</PropertyGroup>
<!-- only set the Xamarin mobile targets if we're building an ORT package,
@ -187,7 +187,7 @@
</PropertyGroup>
<!-- CoreML may be valid for one of these targets as they support macOS. we do a runtime check that the OS is
macOS before attempting to enable CoreML. This includes net5.0, netcoreapp3.1, net6.0
macOS before attempting to enable CoreML. This includes netcoreapp3.1 and net6.0
NOTE: $(TargetFrameworkIdentitier) may not be set yet, so we need to call GetTargetFrameworkIdentifier
-->
<PropertyGroup Condition="$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)'))=='.NETCoreApp' AND

View file

@ -660,17 +660,21 @@ namespace Microsoft.ML.OnnxRuntime.Tensors
}
/// <summary>
/// Initialize an n-dimensional tensor with the specified dimensions and layout. ReverseStride=true gives a stride of 1-element width to the first dimension (0). ReverseStride=false gives a stride of 1-element width to the last dimension (n-1).
/// Initialize an n-dimensional tensor with the specified dimensions and layout.
/// ReverseStride=true gives a stride of 1-element width to the first dimension (0).
/// ReverseStride=false gives a stride of 1-element width to the last dimension (n-1).
/// </summary>
/// <param name="dimensions">An span of integers that represent the size of each dimension of the Tensor to create.</param>
/// <param name="reverseStride">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.</param>
/// <param name="dimensions">
/// An span of integers that represent the size of each dimension of the Tensor to create.</param>
/// <param name="reverseStride">
/// 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.</param>
/// <remarks>If you pass `null` for dimensions it will implicitly convert to an empty ReadOnlySpan, which is
/// equivalent to the dimensions for a scalar value.</remarks>
protected Tensor(ReadOnlySpan<int> 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++)

View file

@ -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);

View file

@ -1,17 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<!-- netstandard2.0 is required by xamarin and used by most platforms.
net6.0 is required for linux. -->
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<OnnxRuntimeCsharpRoot>$(ProjectDir)..\..</OnnxRuntimeCsharpRoot>
<Platforms>AnyCPU;x86</Platforms>
<Platforms>AnyCPU</Platforms>
<OutputPath>bin\$(Configuration)\</OutputPath>
<IsLinuxBuild Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinuxBuild>
<IsWindowsBuild Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindowsBuild>
<IsMacOSBuild Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsMacOSBuild>
<ProtoSrc>$(OnnxRuntimeCsharpRoot)\..\cmake\external\onnx;\..\cmake\external\onnx\onnx</ProtoSrc>
<IsLinuxBuild Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinuxBuild>
<IsWindowsBuild Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindowsBuild>
<IsMacOSBuild Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsMacOSBuild>
<ProtoSrc>$(OnnxRuntimeCsharpRoot)\..\cmake\external\onnx</ProtoSrc>
<!-- following attributes were necessary for the migrated Tensor tests -->
<!-- following attributes were necessary for the migrated Tensor tests -->
<LangVersion>7.2</LangVersion>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<SignAssembly>true</SignAssembly> <!-- need signing for friend access to the internals of the Tensors assembly -->
@ -48,6 +50,7 @@
<ItemGroup>
<Compile Remove="InferenceTest.cs" />
<Compile Remove="OrtEnvTests.cs" />
<Compile Remove="OrtIoBindingAllocationTest.cs" />
<Compile Remove="Tensors\TensorTests.cs" />
<Compile Remove="TrainingTest.cs" />
@ -75,9 +78,9 @@
<ItemGroup>
<!-- include common files for visibility, however they're compiled directly by the target specific test projects -->
<None Include="InferenceTest.cs"/>
<None Include="OrtEnvTests.cs"/>
<None Include="OnnxData.cs"/>
<None Include="InferenceTest.cs" />
<None Include="OrtEnvTests.cs" />
<None Include="OnnxData.cs" />
<None Include="OrtIoBindingAllocationTest.cs" Condition=" '$(EnableDefaultCompileItems)' == 'true' " />
<None Include="Tensors\TensorTests.cs" Condition=" '$(EnableDefaultCompileItems)' == 'true' " />
<None Include="Tensors\ArrayTensorExtensionTests.cs" Condition=" '$(EnableDefaultCompileItems)' == 'true' " />
@ -92,9 +95,27 @@
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>TensorOperations.cs</LastGenOutput>
</None>
</ItemGroup>
</ItemGroup>
<ItemGroup>
<None Condition="'$(IsWindowsBuild)'=='true'" Include="$(NativeBuildOutputDir)\onnxruntime.dll;$(NativeBuildOutputDir)\onnxruntime.pdb">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
<None Condition="'$(IsLinuxBuild)'=='true'" Include="$(NativeBuildOutputDir)\libonnxruntime.so">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
<None Condition="'$(IsMacOSBuild)'=='true'" Include="$(NativeBuildOutputDir)\libonnxruntime.dylib">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="Google.Protobuf" Version="3.16.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="xunit" Version="2.4.1" />
@ -106,11 +127,11 @@
<!-- generate OnnxMl.cs from ONNX protobuf definition -->
<Target Name="ProtoGen" BeforeTargets="BeforeBuild" Condition="Exists('$(ProtocExe)')">
<Exec Command="$(ProtocExe) -I=$(ProtoSrc) --csharp_out=. $(ProtoSrc)\onnx-ml.proto3" ContinueOnError="false"></Exec>
<Exec Command="$(ProtocExe) -I=$(ProtoSrc) --csharp_out=. $(ProtoSrc)\onnx\onnx-ml.proto3" ContinueOnError="false"></Exec>
</Target>
<Target Name="ProtoDataGen" BeforeTargets="BeforeBuild" Condition="Exists('$(ProtocExe)')">
<Exec Command="$(ProtocExe) -I=$(ProtoSrc) --csharp_out=. $(ProtoSrc)\onnx-data.proto3" ContinueOnError="false"></Exec>
<Exec Command="$(ProtocExe) -I=$(ProtoSrc) --csharp_out=. $(ProtoSrc)\onnx\onnx-data.proto3" ContinueOnError="false"></Exec>
</Target>
<ItemGroup>
@ -137,4 +158,4 @@
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
</ItemGroup>
</Project>
</Project>

View file

@ -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);
}
}

View file

@ -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<T> Allocate(int length)
{

View file

@ -159,7 +159,12 @@ namespace Microsoft.ML.OnnxRuntime.Tensors.Tests
Assert.Equal(24, tensor.Length);
Assert.Equal(tensorConstructor.IsReversedStride, tensor.IsReversedStride);
Assert.Throws<ArgumentNullException>("dimensions", () => tensorConstructor.CreateFromDimensions<int>(dimensions: null));
// The null is converted to a 'null' ReadOnlySpan<T> 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<ArgumentNullException>("dimensions", () => tensorConstructor.CreateFromDimensions<int>(dimensions: null));
Assert.Throws<ArgumentOutOfRangeException>("dimensions", () => tensorConstructor.CreateFromDimensions<int>(dimensions: new[] { 1, -1 }));
// ensure dimensions are immutable

View file

@ -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
{

View file

@ -173,4 +173,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
<OnnxRuntimeCsharpRoot>$(ProjectDir)..\..</OnnxRuntimeCsharpRoot>
<Platforms>AnyCPU;x86</Platforms>
@ -53,13 +53,39 @@
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.Targets" Version="5.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
</ItemGroup>
<!--
Copy the required libraries for testing to the output directory.
NOTE: We use a wildcard for custom_op_library even though that isn't necessary, so it doesn't fail
if the custom op library isn't present, which it may not be depending on the ORT build settings.
-->
<ItemGroup>
<None Include="$(NativeBuildOutputDir)\*.dll;$(NativeBuildOutputDir)\*.pdb;$(NativeBuildOutputDir)\*.dylib;$(NativeBuildOutputDir)\libcustom*.so;$(NativeBuildOutputDir)\libcustom*.dylib">
<None Condition="'$(IsWindowsBuild)'=='true'"
Include="$(NativeBuildOutputDir)\onnxruntime.dll;
$(NativeBuildOutputDir)\onnxruntime.pdb;
$(NativeBuildOutputDir)\onnxruntime_providers_*.dll;
$(NativeBuildOutputDir)\onnxruntime_providers_*.pdb;
$(NativeBuildOutputDir)\custom_op_library*.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
<None Condition="'$(IsLinuxBuild)'=='true'"
Include="$(NativeBuildOutputDir)\libonnxruntime.so;
$(NativeBuildOutputDir)\libonnxruntime_providers_*.so;
$(NativeBuildOutputDir)\libcustom_op_library*.so">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
<None Condition="'$(IsMacOSBuild)'=='true'"
Include="$(NativeBuildOutputDir)\libonnxruntime.dylib;
$(NativeBuildOutputDir)\libonnxruntime_providers_*.dylib;
$(NativeBuildOutputDir)\libcustom_op_library*.dylib">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>

View file

@ -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. <OnnxRuntimeBuildDirectory>/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: