Skip to content

Commit

Permalink
Upgrade to support .NET 9 (#2275)
Browse files Browse the repository at this point in the history
* Upgrade to support .NET 9
* Update build targets
  • Loading branch information
alexeyzimarev authored Dec 16, 2024
1 parent ad6af08 commit 25922ce
Show file tree
Hide file tree
Showing 14 changed files with 138 additions and 119 deletions.
20 changes: 17 additions & 3 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,22 @@ jobs:
runs-on: windows-latest
strategy:
matrix:
dotnet: ['net48', 'net6.0', 'net7.0', 'net8.0']
dotnet: ['net48', 'net8.0', 'net9.0']

steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
9.0.x
-
name: Run tests
run: dotnet test -f ${{ matrix.dotnet }}
run: dotnet test -c Debug -f ${{ matrix.dotnet }}
-
name: Upload Test Results
if: always()
Expand All @@ -47,12 +54,19 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
dotnet: ['net6.0', 'net7.0', 'net8.0']
dotnet: ['net8.0', 'net9.0']

steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
9.0.x
-
name: Run tests
run: dotnet test -f ${{ matrix.dotnet }}
Expand Down
91 changes: 44 additions & 47 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,49 +1,46 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<PropertyGroup Label="Package versions for .NET 6" Condition="$(TargetFramework) == 'net6.0'">
<MicrosoftTestHostVer>[6.0.28,7)</MicrosoftTestHostVer>
</PropertyGroup>
<PropertyGroup Label="Package versions for .NET 7" Condition="$(TargetFramework) == 'net7.0'">
<MicrosoftTestHostVer>7.0.17</MicrosoftTestHostVer>
</PropertyGroup>
<PropertyGroup Label="Package versions for .NET 8" Condition="$(TargetFramework) == 'net8.0'">
<MicrosoftTestHostVer>8.0.3</MicrosoftTestHostVer>
</PropertyGroup>
<ItemGroup Label="Runtime dependencies">
<PackageVersion Include="HttpMultipartParser" Version="8.4.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="CsvHelper" Version="33.0.1" />
<PackageVersion Include="PolySharp" Version="1.14.1" />
<PackageVersion Include="System.Text.Json" Version="8.0.4" />
<PackageVersion Include="WireMock.Net" Version="1.5.60" />
<PackageVersion Include="WireMock.Net.FluentAssertions" Version="1.5.51" />
</ItemGroup>
<ItemGroup Label="Compile dependencies">
<PackageVersion Include="BenchmarkDotNet" Version="0.13.12" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.10.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="MinVer" Version="5.0.0" />
<PackageVersion Include="Nullable" Version="1.3.1" />
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies.net472" Version="1.0.3" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="JetBrains.Annotations" Version="2024.2.0" />
</ItemGroup>
<ItemGroup Label="Testing dependencies">
<PackageVersion Include="AutoFixture" Version="4.18.1" />
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
<PackageVersion Include="HttpTracer" Version="2.1.1" />
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="$(MicrosoftTestHostVer)" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageVersion Include="Moq" Version="4.20.70" />
<PackageVersion Include="Polly" Version="8.4.1" />
<PackageVersion Include="rest-mock-core" Version="0.7.12" />
<PackageVersion Include="RichardSzalay.MockHttp" Version="7.0.0" />
<PackageVersion Include="System.Net.Http.Json" Version="8.0.0" />
<PackageVersion Include="Xunit.Extensions.Logging" Version="1.1.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" PrivateAssets="All" />
<PackageVersion Include="xunit" Version="2.9.0" />
</ItemGroup>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<PropertyGroup Label="Package versions for .NET 8" Condition="$(TargetFramework) == 'net8.0'">
<MicrosoftTestHostVer>8.0.3</MicrosoftTestHostVer>
</PropertyGroup>
<PropertyGroup Label="Package versions for .NET 9" Condition="$(TargetFramework) == 'net9.0'">
<MicrosoftTestHostVer>9.0.0</MicrosoftTestHostVer>
</PropertyGroup>
<ItemGroup Label="Runtime dependencies">
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3"/>
<PackageVersion Include="CsvHelper" Version="33.0.1"/>
<PackageVersion Include="System.Text.Json" Version="9.0.0"/>
</ItemGroup>
<ItemGroup Label="Compile dependencies">
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0"/>
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0"/>
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0"/>
<PackageVersion Include="MinVer" Version="6.0.0"/>
<PackageVersion Include="Nullable" Version="1.3.1"/>
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies.net472" Version="1.0.3"/>
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0"/>
<PackageVersion Include="JetBrains.Annotations" Version="2024.3.0"/>
</ItemGroup>
<ItemGroup Label="Testing dependencies">
<PackageVersion Include="HttpMultipartParser" Version="8.4.0"/>
<PackageVersion Include="PolySharp" Version="1.15.0"/>
<PackageVersion Include="AutoFixture" Version="4.18.1"/>
<PackageVersion Include="coverlet.collector" Version="6.0.2"/>
<PackageVersion Include="FluentAssertions" Version="7.0.0"/>
<PackageVersion Include="HttpTracer" Version="2.1.1"/>
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="$(MicrosoftTestHostVer)"/>
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0"/>
<PackageVersion Include="Moq" Version="4.20.72"/>
<PackageVersion Include="Polly" Version="8.5.0"/>
<PackageVersion Include="rest-mock-core" Version="0.7.12"/>
<PackageVersion Include="RichardSzalay.MockHttp" Version="7.0.0"/>
<PackageVersion Include="System.Net.Http.Json" Version="9.0.0"/>
<PackageVersion Include="Xunit.Extensions.Logging" Version="1.1.0"/>
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" PrivateAssets="All"/>
<PackageVersion Include="xunit" Version="2.9.2"/>
<PackageVersion Include="WireMock.Net" Version="1.6.10"/>
<PackageVersion Include="WireMock.Net.FluentAssertions" Version="1.5.51"/>
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<IsPackable>false</IsPackable>
<LangVersion>preview</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
Expand Down
2 changes: 1 addition & 1 deletion gen/SourceGenerator/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public static IEnumerable<ClassDeclarationSyntax> FindClasses(this Compilation c
.SelectMany(model => model.SyntaxTree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>())
.Where(predicate);

public static IEnumerable<ClassDeclarationSyntax> FindAnnotatedClass(this Compilation compilation, string attributeName, bool strict) {
public static IEnumerable<ClassDeclarationSyntax> FindAnnotatedClasses(this Compilation compilation, string attributeName, bool strict) {
return compilation.FindClasses(
syntax => syntax.AttributeLists.Any(list => list.Attributes.Any(CheckAttribute))
);
Expand Down
30 changes: 20 additions & 10 deletions gen/SourceGenerator/ImmutableGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,28 @@

namespace SourceGenerator;

[Generator]
public class ImmutableGenerator : ISourceGenerator {
public void Initialize(GeneratorInitializationContext context) { }

public void Execute(GeneratorExecutionContext context) {
var compilation = context.Compilation;
[Generator(LanguageNames.CSharp)]
public class ImmutableGenerator : IIncrementalGenerator {
public void Initialize(IncrementalGeneratorInitializationContext context) {
var c = context.CompilationProvider.SelectMany((x, _) => GetImmutableClasses(x));

context.RegisterSourceOutput(
c.Collect(),
static (ctx, sources) => {
foreach (var source in sources) {
ctx.AddSource(source.Item1, source.Item2);
}
}
);
return;

var mutableClasses = compilation.FindAnnotatedClass("GenerateImmutable", strict: true);
IEnumerable<(string, SourceText)> GetImmutableClasses(Compilation compilation) {
var mutableClasses = compilation.FindAnnotatedClasses("GenerateImmutable", strict: true);

foreach (var mutableClass in mutableClasses) {
var immutableClass = GenerateImmutableClass(mutableClass, compilation);
context.AddSource($"ReadOnly{mutableClass.Identifier.Text}.cs", SourceText.From(immutableClass, Encoding.UTF8));
foreach (var mutableClass in mutableClasses) {
var immutableClass = GenerateImmutableClass(mutableClass, compilation);
yield return ($"ReadOnly{mutableClass.Identifier.Text}.cs", SourceText.From(immutableClass, Encoding.UTF8));
}
}
}

Expand Down
65 changes: 36 additions & 29 deletions gen/SourceGenerator/InheritedCloneGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,44 @@

namespace SourceGenerator;

[Generator]
public class InheritedCloneGenerator : ISourceGenerator {
[Generator(LanguageNames.CSharp)]
public class InheritedCloneGenerator : IIncrementalGenerator {
const string AttributeName = "GenerateClone";

public void Initialize(GeneratorInitializationContext context) { }

public void Execute(GeneratorExecutionContext context) {
var compilation = context.Compilation;

var candidates = compilation.FindAnnotatedClass(AttributeName, false);

foreach (var candidate in candidates) {
var semanticModel = compilation.GetSemanticModel(candidate.SyntaxTree);
var genericClassSymbol = semanticModel.GetDeclaredSymbol(candidate);
if (genericClassSymbol == null) continue;

// Get the method name from the attribute Name argument
var attributeData = genericClassSymbol.GetAttributes().FirstOrDefault(a => a.AttributeClass?.Name == $"{AttributeName}Attribute");
var methodName = (string)attributeData.NamedArguments.FirstOrDefault(arg => arg.Key == "Name").Value.Value;

// Get the generic argument type where properties need to be copied from
var attributeSyntax = candidate.AttributeLists
.SelectMany(l => l.Attributes)
.FirstOrDefault(a => a.Name.ToString().StartsWith(AttributeName));
if (attributeSyntax == null) continue; // This should never happen

var typeArgumentSyntax = ((GenericNameSyntax)attributeSyntax.Name).TypeArgumentList.Arguments[0];
var typeSymbol = (INamedTypeSymbol)semanticModel.GetSymbolInfo(typeArgumentSyntax).Symbol;

var code = GenerateMethod(candidate, genericClassSymbol, typeSymbol, methodName);
context.AddSource($"{genericClassSymbol.Name}.Clone.g.cs", SourceText.From(code, Encoding.UTF8));
public void Initialize(IncrementalGeneratorInitializationContext context) {
var c = context.CompilationProvider.SelectMany((x, _) => GetClones(x));

context.RegisterSourceOutput(
c.Collect(),
static (ctx, sources) => {
foreach (var source in sources) {
ctx.AddSource(source.Item1, source.Item2);
}
}
);
return;

IEnumerable<(string, SourceText)> GetClones(Compilation compilation) {
var candidates = compilation.FindAnnotatedClasses(AttributeName, false);

foreach (var candidate in candidates) {
var semanticModel = compilation.GetSemanticModel(candidate.SyntaxTree);
var genericClassSymbol = semanticModel.GetDeclaredSymbol(candidate);
if (genericClassSymbol == null) continue;

var attributeData = genericClassSymbol.GetAttributes().FirstOrDefault(a => a.AttributeClass?.Name == $"{AttributeName}Attribute");
var methodName = (string)attributeData.NamedArguments.FirstOrDefault(arg => arg.Key == "Name").Value.Value;
var baseType = attributeData.NamedArguments.FirstOrDefault(arg => arg.Key == "BaseType").Value.Value;

// Get the generic argument type where properties need to be copied from
var attributeSyntax = candidate.AttributeLists
.SelectMany(l => l.Attributes)
.FirstOrDefault(a => a.Name.ToString().StartsWith(AttributeName));
if (attributeSyntax == null) continue; // This should never happen

var code = GenerateMethod(candidate, genericClassSymbol, (INamedTypeSymbol)baseType, methodName);
yield return ($"{genericClassSymbol.Name}.Clone.g.cs", SourceText.From(code, Encoding.UTF8));
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), 'RestSharp.sln'))\props\Common.props"/>
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net471;net48;net6.0;net7.0;net8.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;net471;net48;net8.0;net9.0</TargetFrameworks>
<PackageIcon>restsharp.png</PackageIcon>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageProjectUrl>https://restsharp.dev</PackageProjectUrl>
Expand Down
6 changes: 0 additions & 6 deletions src/RestSharp/Authenticators/OAuth/OAuthTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,6 @@ public static string GetNonce() {
/// </summary>
/// <param name="value">The value to escape.</param>
/// <returns>The escaped value.</returns>
/// <remarks>
/// The <see cref="Uri.EscapeDataString" /> method is <i>supposed</i> to take on
/// RFC 3986 behavior if certain elements are present in a .config file. Even if this
/// actually worked (which in my experiments it <i>doesn't</i>), we can't rely on every
/// host actually having this configuration element present.
/// </remarks>
[return: NotNullIfNotNull(nameof(value))]
public static string? UrlEncodeRelaxed(string? value) {
if (value == null) return null;
Expand Down
5 changes: 3 additions & 2 deletions src/RestSharp/Extensions/GenerateImmutableAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ namespace RestSharp.Extensions;
class GenerateImmutableAttribute : Attribute;

[AttributeUsage(AttributeTargets.Class)]
class GenerateCloneAttribute<T> : Attribute where T : class {
public string? Name { get; set; }
class GenerateCloneAttribute : Attribute {
public Type? BaseType { get; set; }
public string? Name { get; set; }
};

[AttributeUsage(AttributeTargets.Property)]
Expand Down
10 changes: 0 additions & 10 deletions src/RestSharp/Options/RestClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,16 +181,6 @@ public RestClientOptions(string baseUrl) : this(new Uri(Ensure.NotEmptyString(ba
/// </summary>
public CookieContainer? CookieContainer { get; set; }

/// <summary>
/// Maximum request duration in milliseconds. When the request timeout is specified using <seealso cref="RestRequest.Timeout"/>,
/// the lowest value between the client timeout and request timeout will be used.
/// </summary>
[Obsolete("Use Timeout instead.")]
public int MaxTimeout {
get => (int) (Timeout?.TotalMilliseconds ?? 0);
set => Timeout = TimeSpan.FromMilliseconds(value);
}

/// <summary>
/// Request duration. Used when the request timeout is not specified using <seealso cref="RestRequest.Timeout"/>,
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/RestSharp/Response/RestResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace RestSharp;
/// Container for data sent back from API including deserialized data
/// </summary>
/// <typeparam name="T">Type of data to deserialize to</typeparam>
[GenerateClone<RestResponse>(Name = "FromResponse")]
[GenerateClone(BaseType = typeof(RestResponse), Name = "FromResponse")]
[DebuggerDisplay($"{{{nameof(DebuggerDisplay)}()}}")]
public partial class RestResponse<T>(RestRequest request) : RestResponse(request) {
/// <summary>
Expand Down
18 changes: 12 additions & 6 deletions src/RestSharp/RestSharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@
<ItemGroup Condition="$(AddPolyfills) == 'true'">
<PackageReference Include="Nullable" PrivateAssets="All"/>
</ItemGroup>
<ItemGroup Condition="$(AddSystemTextJson) == 'true'">
<PackageReference Include="System.Text.Json"/>
</ItemGroup>
<ItemGroup>
<Compile Update="RestClient.Extensions.Params.cs">
<DependentUpon>RestClient.Extensions.cs</DependentUpon>
Expand Down Expand Up @@ -63,12 +60,21 @@
</Compile>
</ItemGroup>
<ItemGroup Label="Source generator">
<ProjectReference Include="$(RepoRoot)\gen\SourceGenerator\SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
<ProjectReference Include="$(RepoRoot)\gen\SourceGenerator\SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
<ItemGroup Label="Package README">
<None Include="..\..\README.md" Pack="true" PackagePath="\"/>
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<Using Include="System.Net"/>
<Using Include="System.Net" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net471'">
<PackageReference Include="System.Text.Json" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net48'">
<PackageReference Include="System.Text.Json" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.Text.Json" />
</ItemGroup>
</Project>
2 changes: 1 addition & 1 deletion test/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<IsTestProject>true</IsTestProject>
<IsPackable>false</IsPackable>
<TargetFrameworks>net48;net6.0;net7.0;net8.0</TargetFrameworks>
<TargetFrameworks>net48;net8.0;net9.0</TargetFrameworks>
<Nullable>disable</Nullable>
<NoWarn>xUnit1033</NoWarn>
<VSTestLogger>trx%3bLogFileName=$(MSBuildProjectName).trx</VSTestLogger>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<IsTestProject>false</IsTestProject>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="$(RepoRoot)\src\RestSharp\RestSharp.csproj" />
Expand Down

0 comments on commit 25922ce

Please sign in to comment.