From 5dd8c888c231ba0589617a15ed0870daa41fedd8 Mon Sep 17 00:00:00 2001 From: Parker Bibus Date: Tue, 8 Sep 2026 12:54:37 -0700 Subject: [PATCH] Add .NET 12 runtime and toolchain support Add .NET 12 monikers, cached runtimes and toolchains including composite ReadyToRun and Mono WebAssembly. Update development defaults and templates, and cover runtime parsing and CLI resolution. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- NuGet.Config | 1 + .../Jobs/RuntimeMoniker.cs | 12 ++++++ .../Environments/Runtimes/CoreRuntime.cs | 5 ++- .../Environments/Runtimes/MonoCoreRuntime.cs | 2 + .../Runtimes/MonoWasmAotRuntime.cs | 2 + .../Environments/Runtimes/MonoWasmRuntime.cs | 2 + .../Environments/Runtimes/NativeAotRuntime.cs | 6 +++ .../Environments/Runtimes/R2RRuntime.cs | 2 + .../Toolchains/CoreRun/CoreRunToolchain.cs | 2 +- .../Mono/CsProjMonoCoreToolchain.cs | 2 + .../NativeAot/CsProjNativeAotGenerator.cs | 4 +- .../NativeAot/CsProjNativeAotToolchain.cs | 3 ++ .../Toolchains/NativeAot/NativeAotSettings.cs | 2 +- .../NetCoreApp/CsProjCoreToolchain.cs | 2 + .../Toolchains/R2R/CsProjR2RToolchain.cs | 2 + .../Wasm/CsProjMonoWasmAotToolchain.cs | 2 + .../Wasm/CsProjMonoWasmToolchain.cs | 2 + .../.template.config/template.json | 4 ++ .../.template.config/template.json | 4 ++ .../.template.config/template.json | 4 ++ .../ConfigParserTests.cs | 31 +++++++++++++- .../RuntimeParseTests.cs | 40 +++++++++++++++++++ .../RuntimeVersionDetectionTests.cs | 1 + 23 files changed, 131 insertions(+), 6 deletions(-) diff --git a/NuGet.Config b/NuGet.Config index 3a788394a9..e1d60e18ec 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -15,6 +15,7 @@ + diff --git a/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs b/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs index 32307d419c..1dcfc242fa 100644 --- a/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs +++ b/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs @@ -53,6 +53,8 @@ public static class RuntimeMoniker public const string Net10_0 = "net10.0"; /// .NET 11.0 public const string Net11_0 = "net11.0"; + /// .NET 12.0 + public const string Net12_0 = "net12.0"; /// NativeAOT compiled as net7.0 public const string NativeAot70 = "nativeaot7.0"; @@ -64,6 +66,8 @@ public static class RuntimeMoniker public const string NativeAot10_0 = "nativeaot10.0"; /// NativeAOT compiled as net11.0 public const string NativeAot11_0 = "nativeaot11.0"; + /// NativeAOT compiled as net12.0 + public const string NativeAot12_0 = "nativeaot12.0"; /// .NET 6 using MonoVM (not CLR which is the default) public const string Mono60 = "mono6.0"; @@ -77,6 +81,8 @@ public static class RuntimeMoniker public const string Mono10_0 = "mono10.0"; /// .NET 11 using MonoVM (not CLR which is the default) public const string Mono11_0 = "mono11.0"; + /// .NET 12 using MonoVM (not CLR which is the default) + public const string Mono12_0 = "mono12.0"; /// .NET 8 CLR with composite ReadyToRun compilation public const string R2R80 = "r2r8.0"; @@ -86,6 +92,8 @@ public static class RuntimeMoniker public const string R2R10_0 = "r2r10.0"; /// .NET 11 CLR with composite ReadyToRun compilation public const string R2R11_0 = "r2r11.0"; + /// .NET 12 CLR with composite ReadyToRun compilation + public const string R2R12_0 = "r2r12.0"; /// Mono WebAssembly with net8.0 public const string MonoWasm80 = "monowasm8.0"; @@ -95,6 +103,8 @@ public static class RuntimeMoniker public const string MonoWasm10_0 = "monowasm10.0"; /// Mono WebAssembly with net11.0 public const string MonoWasm11_0 = "monowasm11.0"; + /// Mono WebAssembly with net12.0 + public const string MonoWasm12_0 = "monowasm12.0"; /// Mono WebAssembly AOT with net8.0 public const string MonoWasmAot80 = "monowasmaot8.0"; @@ -104,6 +114,8 @@ public static class RuntimeMoniker public const string MonoWasmAot10_0 = "monowasmaot10.0"; /// Mono WebAssembly AOT with net11.0 public const string MonoWasmAot11_0 = "monowasmaot11.0"; + /// Mono WebAssembly AOT with net12.0 + public const string MonoWasmAot12_0 = "monowasmaot12.0"; // Still experimental //public const string CoreWasm11_0 = "corewasm11.0"; diff --git a/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs b/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs index 6bac4c19fe..fb751cf150 100644 --- a/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs +++ b/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs @@ -29,8 +29,9 @@ public sealed class CoreRuntime : Runtime public static readonly CoreRuntime Core90 = new(new(9, 0)); public static readonly CoreRuntime Core10_0 = new(new(10, 0)); public static readonly CoreRuntime Core11_0 = new(new(11, 0)); + public static readonly CoreRuntime Core12_0 = new(new(12, 0)); - public static CoreRuntime Latest => Core11_0; // when dotnet/runtime branches for 12.0, this will need to get updated + public static CoreRuntime Latest => Core12_0; // when dotnet/runtime branches for 13.0, this will need to get updated private readonly string? platform; @@ -125,6 +126,7 @@ public static CoreRuntime From(Version version, string? platform = null) (9, 0) => Core90, (10, 0) => Core10_0, (11, 0) => Core11_0, + (12, 0) => Core12_0, _ => new CoreRuntime(version), }; @@ -314,6 +316,7 @@ private static CoreRuntime GetPlatformSpecific(Version version, Assembly? assemb 9 => Core90, 10 => Core10_0, 11 => Core11_0, + 12 => Core12_0, _ => new(version), }; diff --git a/src/BenchmarkDotNet/Environments/Runtimes/MonoCoreRuntime.cs b/src/BenchmarkDotNet/Environments/Runtimes/MonoCoreRuntime.cs index 634ec95a9d..3d3cc7993c 100644 --- a/src/BenchmarkDotNet/Environments/Runtimes/MonoCoreRuntime.cs +++ b/src/BenchmarkDotNet/Environments/Runtimes/MonoCoreRuntime.cs @@ -17,6 +17,7 @@ public sealed class MonoCoreRuntime : Runtime public static readonly MonoCoreRuntime Net90 = new(new(9, 0)); public static readonly MonoCoreRuntime Net10_0 = new(new(10, 0)); public static readonly MonoCoreRuntime Net11_0 = new(new(11, 0)); + public static readonly MonoCoreRuntime Net12_0 = new(new(12, 0)); private MonoCoreRuntime(Version version) => Version = ToRuntimeVersion(version); @@ -36,6 +37,7 @@ public static MonoCoreRuntime From(Version version) 9 => Net90, 10 => Net10_0, 11 => Net11_0, + 12 => Net12_0, _ => new(version), }; diff --git a/src/BenchmarkDotNet/Environments/Runtimes/MonoWasmAotRuntime.cs b/src/BenchmarkDotNet/Environments/Runtimes/MonoWasmAotRuntime.cs index 154d9244f0..ae7b1c90e5 100644 --- a/src/BenchmarkDotNet/Environments/Runtimes/MonoWasmAotRuntime.cs +++ b/src/BenchmarkDotNet/Environments/Runtimes/MonoWasmAotRuntime.cs @@ -13,6 +13,7 @@ public sealed class MonoWasmAotRuntime : WasmRuntime public static readonly MonoWasmAotRuntime Net90 = new(new(9, 0)); public static readonly MonoWasmAotRuntime Net10_0 = new(new(10, 0)); public static readonly MonoWasmAotRuntime Net11_0 = new(new(11, 0)); + public static readonly MonoWasmAotRuntime Net12_0 = new(new(12, 0)); private MonoWasmAotRuntime(Version version) : base(version) { } @@ -26,6 +27,7 @@ public static MonoWasmAotRuntime From(Version version) 9 => Net90, 10 => Net10_0, 11 => Net11_0, + 12 => Net12_0, _ => new(version), }; diff --git a/src/BenchmarkDotNet/Environments/Runtimes/MonoWasmRuntime.cs b/src/BenchmarkDotNet/Environments/Runtimes/MonoWasmRuntime.cs index 4e7f6d05d4..2463c418e4 100644 --- a/src/BenchmarkDotNet/Environments/Runtimes/MonoWasmRuntime.cs +++ b/src/BenchmarkDotNet/Environments/Runtimes/MonoWasmRuntime.cs @@ -13,6 +13,7 @@ public sealed class MonoWasmRuntime : WasmRuntime public static readonly MonoWasmRuntime Net90 = new(new(9, 0)); public static readonly MonoWasmRuntime Net10_0 = new(new(10, 0)); public static readonly MonoWasmRuntime Net11_0 = new(new(11, 0)); + public static readonly MonoWasmRuntime Net12_0 = new(new(12, 0)); private MonoWasmRuntime(Version version) : base(version) { } @@ -26,6 +27,7 @@ public static MonoWasmRuntime From(Version version) 9 => Net90, 10 => Net10_0, 11 => Net11_0, + 12 => Net12_0, _ => new(version), }; diff --git a/src/BenchmarkDotNet/Environments/Runtimes/NativeAotRuntime.cs b/src/BenchmarkDotNet/Environments/Runtimes/NativeAotRuntime.cs index 23badab98f..0fe871a33a 100644 --- a/src/BenchmarkDotNet/Environments/Runtimes/NativeAotRuntime.cs +++ b/src/BenchmarkDotNet/Environments/Runtimes/NativeAotRuntime.cs @@ -28,6 +28,11 @@ public sealed class NativeAotRuntime : Runtime /// public static readonly NativeAotRuntime Net11_0 = new(new(11, 0)); + /// + /// NativeAOT compiled as net12.0 + /// + public static readonly NativeAotRuntime Net12_0 = new(new(12, 0)); + private NativeAotRuntime(Version version) => Version = ToRuntimeVersion(version); public override string Name => "NativeAOT"; @@ -53,6 +58,7 @@ public static NativeAotRuntime From(Version version) 9 => Net90, 10 => Net10_0, 11 => Net11_0, + 12 => Net12_0, _ => new(version), }; diff --git a/src/BenchmarkDotNet/Environments/Runtimes/R2RRuntime.cs b/src/BenchmarkDotNet/Environments/Runtimes/R2RRuntime.cs index 8c4a81f41a..6b22993fc7 100644 --- a/src/BenchmarkDotNet/Environments/Runtimes/R2RRuntime.cs +++ b/src/BenchmarkDotNet/Environments/Runtimes/R2RRuntime.cs @@ -10,6 +10,7 @@ public sealed class R2RRuntime : Runtime public static readonly R2RRuntime Net90 = new(new(9, 0)); public static readonly R2RRuntime Net10_0 = new(new(10, 0)); public static readonly R2RRuntime Net11_0 = new(new(11, 0)); + public static readonly R2RRuntime Net12_0 = new(new(12, 0)); private R2RRuntime(Version version) => Version = ToRuntimeVersion(version); @@ -25,6 +26,7 @@ public static R2RRuntime From(Version version) 9 => Net90, 10 => Net10_0, 11 => Net11_0, + 12 => Net12_0, _ => new(version), }; diff --git a/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunToolchain.cs b/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunToolchain.cs index 9382be944e..af3d3ccc51 100644 --- a/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunToolchain.cs @@ -9,7 +9,7 @@ namespace BenchmarkDotNet.Toolchains.CoreRun { public sealed class CoreRunToolchain : IToolchain, IHasSettings { - private const string DefaultTargetFrameworkMoniker = "net11.0"; + private const string DefaultTargetFrameworkMoniker = "net12.0"; private CoreRunToolchain(CoreRunSettings settings) { diff --git a/src/BenchmarkDotNet/Toolchains/Mono/CsProjMonoCoreToolchain.cs b/src/BenchmarkDotNet/Toolchains/Mono/CsProjMonoCoreToolchain.cs index f010101f03..81c7cbbc4d 100644 --- a/src/BenchmarkDotNet/Toolchains/Mono/CsProjMonoCoreToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/Mono/CsProjMonoCoreToolchain.cs @@ -13,6 +13,7 @@ public sealed class CsProjMonoCoreToolchain : CsProjNetToolchain public static readonly CsProjMonoCoreToolchain Mono90 = new(MonoCoreRuntime.Net90, MonoCoreSettings.Default); public static readonly CsProjMonoCoreToolchain Mono10_0 = new(MonoCoreRuntime.Net10_0, MonoCoreSettings.Default); public static readonly CsProjMonoCoreToolchain Mono11_0 = new(MonoCoreRuntime.Net11_0, MonoCoreSettings.Default); + public static readonly CsProjMonoCoreToolchain Mono12_0 = new(MonoCoreRuntime.Net12_0, MonoCoreSettings.Default); private CsProjMonoCoreToolchain(MonoCoreRuntime runtime, MonoCoreSettings settings) : this(runtime, settings, Resolve(settings, runtime)) { } @@ -46,6 +47,7 @@ public static CsProjMonoCoreToolchain From(MonoCoreRuntime runtime, MonoCoreSett 9 => Mono90, 10 => Mono10_0, 11 => Mono11_0, + 12 => Mono12_0, _ => new CsProjMonoCoreToolchain(runtime, settings), }; } diff --git a/src/BenchmarkDotNet/Toolchains/NativeAot/CsProjNativeAotGenerator.cs b/src/BenchmarkDotNet/Toolchains/NativeAot/CsProjNativeAotGenerator.cs index df514dc56b..e3bc4720d3 100644 --- a/src/BenchmarkDotNet/Toolchains/NativeAot/CsProjNativeAotGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/NativeAot/CsProjNativeAotGenerator.cs @@ -19,7 +19,7 @@ internal sealed class CsProjNativeAotGenerator : CsProjGenerator { internal const string NativeAotNuGetFeed = "nativeAotNuGetFeed"; private const string DefaultNuGetFeed = "https://api.nuget.org/v3/index.json"; - private const string LocalBuildDotNetFeed = "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet11/nuget/v3/index.json"; + private const string LocalBuildDotNetFeed = "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet12/nuget/v3/index.json"; internal const string GeneratedRdXmlFileName = "bdn_generated.rd.xml"; private readonly NativeAotSettings settings; @@ -95,7 +95,7 @@ protected override async ValueTask GenerateNuGetConfigAsync(ArtifactsPaths artif // The ILCompiler is restored either from a NuGet feed, or from a local runtime build (which also needs the dotnet nightly feed). private KeyValuePair[] GetFeeds() - => settings.LocalIlcPackages is not null ? [new("local", settings.LocalIlcPackages.FullName), new("dotnet11", LocalBuildDotNetFeed)] + => settings.LocalIlcPackages is not null ? [new("local", settings.LocalIlcPackages.FullName), new("dotnet12", LocalBuildDotNetFeed)] : settings.NuGetFeedUrl.IsNotBlank() ? [new(NativeAotNuGetFeed, settings.NuGetFeedUrl!)] : []; diff --git a/src/BenchmarkDotNet/Toolchains/NativeAot/CsProjNativeAotToolchain.cs b/src/BenchmarkDotNet/Toolchains/NativeAot/CsProjNativeAotToolchain.cs index a412b644f7..40519aa89e 100644 --- a/src/BenchmarkDotNet/Toolchains/NativeAot/CsProjNativeAotToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/NativeAot/CsProjNativeAotToolchain.cs @@ -17,6 +17,8 @@ public sealed class CsProjNativeAotToolchain : CsProjNetToolchain public static readonly CsProjNativeAotToolchain Net10_0 = new(NativeAotRuntime.Net10_0, NativeAotSettings.Default); /// compiled as net11.0 public static readonly CsProjNativeAotToolchain Net11_0 = new(NativeAotRuntime.Net11_0, NativeAotSettings.Default); + /// compiled as net12.0 + public static readonly CsProjNativeAotToolchain Net12_0 = new(NativeAotRuntime.Net12_0, NativeAotSettings.Default); private CsProjNativeAotToolchain(NativeAotRuntime runtime, NativeAotSettings settings) : this(runtime, settings, Resolve(settings, runtime)) { } @@ -49,6 +51,7 @@ public static CsProjNativeAotToolchain From(NativeAotRuntime runtime, NativeAotS 9 => Net90, 10 => Net10_0, 11 => Net11_0, + 12 => Net12_0, _ => new CsProjNativeAotToolchain(runtime, settings), }; } diff --git a/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotSettings.cs b/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotSettings.cs index ba4972644f..a134ca3dec 100644 --- a/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotSettings.cs +++ b/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotSettings.cs @@ -8,7 +8,7 @@ namespace BenchmarkDotNet.Toolchains.NativeAot; public sealed record NativeAotSettings : DotNetCliSettings { - private const string LocalBuildIlCompilerVersion = "11.0.0-dev"; + private const string LocalBuildIlCompilerVersion = "12.0.0-dev"; public static readonly NativeAotSettings Default = new(); diff --git a/src/BenchmarkDotNet/Toolchains/NetCoreApp/CsProjCoreToolchain.cs b/src/BenchmarkDotNet/Toolchains/NetCoreApp/CsProjCoreToolchain.cs index 7038ae50e0..23337f6920 100644 --- a/src/BenchmarkDotNet/Toolchains/NetCoreApp/CsProjCoreToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/NetCoreApp/CsProjCoreToolchain.cs @@ -19,6 +19,7 @@ public sealed class CsProjCoreToolchain : CsProjNetToolchain public static readonly CsProjCoreToolchain NetCoreApp90 = new(CoreRuntime.Core90, NetCoreAppSettings.Default); public static readonly CsProjCoreToolchain NetCoreApp10_0 = new(CoreRuntime.Core10_0, NetCoreAppSettings.Default); public static readonly CsProjCoreToolchain NetCoreApp11_0 = new(CoreRuntime.Core11_0, NetCoreAppSettings.Default); + public static readonly CsProjCoreToolchain NetCoreApp12_0 = new(CoreRuntime.Core12_0, NetCoreAppSettings.Default); private CsProjCoreToolchain(CoreRuntime runtime, NetCoreAppSettings settings) : this(runtime, settings, Resolve(settings, runtime)) { } @@ -61,6 +62,7 @@ public static CsProjCoreToolchain From(CoreRuntime runtime, NetCoreAppSettings s (9, 0) => NetCoreApp90, (10, 0) => NetCoreApp10_0, (11, 0) => NetCoreApp11_0, + (12, 0) => NetCoreApp12_0, _ => new CsProjCoreToolchain(runtime, settings), }; } diff --git a/src/BenchmarkDotNet/Toolchains/R2R/CsProjR2RToolchain.cs b/src/BenchmarkDotNet/Toolchains/R2R/CsProjR2RToolchain.cs index f2478d0729..f84f2f3344 100644 --- a/src/BenchmarkDotNet/Toolchains/R2R/CsProjR2RToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/R2R/CsProjR2RToolchain.cs @@ -11,6 +11,7 @@ public sealed class CsProjR2RToolchain : CsProjNetToolchain public static readonly CsProjR2RToolchain R2R90 = new(R2RRuntime.Net90, R2RSettings.Default); public static readonly CsProjR2RToolchain R2R10_0 = new(R2RRuntime.Net10_0, R2RSettings.Default); public static readonly CsProjR2RToolchain R2R11_0 = new(R2RRuntime.Net11_0, R2RSettings.Default); + public static readonly CsProjR2RToolchain R2R12_0 = new(R2RRuntime.Net12_0, R2RSettings.Default); private CsProjR2RToolchain(R2RRuntime runtime, R2RSettings settings) : this(runtime, settings, Resolve(settings, runtime)) { } @@ -42,6 +43,7 @@ public static CsProjR2RToolchain From(R2RRuntime runtime, R2RSettings settings) 9 => R2R90, 10 => R2R10_0, 11 => R2R11_0, + 12 => R2R12_0, _ => new CsProjR2RToolchain(runtime, settings), }; } diff --git a/src/BenchmarkDotNet/Toolchains/Wasm/CsProjMonoWasmAotToolchain.cs b/src/BenchmarkDotNet/Toolchains/Wasm/CsProjMonoWasmAotToolchain.cs index 83ad6e8558..4da4b09865 100644 --- a/src/BenchmarkDotNet/Toolchains/Wasm/CsProjMonoWasmAotToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/Wasm/CsProjMonoWasmAotToolchain.cs @@ -9,6 +9,7 @@ public sealed class CsProjMonoWasmAotToolchain : CsProjWasmToolchain public static readonly CsProjMonoWasmAotToolchain Net90 = new(MonoWasmAotRuntime.Net90, WasmSettings.Default); public static readonly CsProjMonoWasmAotToolchain Net10_0 = new(MonoWasmAotRuntime.Net10_0, WasmSettings.Default); public static readonly CsProjMonoWasmAotToolchain Net11_0 = new(MonoWasmAotRuntime.Net11_0, WasmSettings.Default); + public static readonly CsProjMonoWasmAotToolchain Net12_0 = new(MonoWasmAotRuntime.Net12_0, WasmSettings.Default); private CsProjMonoWasmAotToolchain(MonoWasmAotRuntime runtime, WasmSettings settings) : base("MonoWasmAot", runtime, settings, aot: true, useCoreClrRuntime: false) { } @@ -25,6 +26,7 @@ public static CsProjMonoWasmAotToolchain From(MonoWasmAotRuntime runtime, WasmSe 9 => Net90, 10 => Net10_0, 11 => Net11_0, + 12 => Net12_0, _ => new CsProjMonoWasmAotToolchain(runtime, settings), }; } diff --git a/src/BenchmarkDotNet/Toolchains/Wasm/CsProjMonoWasmToolchain.cs b/src/BenchmarkDotNet/Toolchains/Wasm/CsProjMonoWasmToolchain.cs index a33f2b521e..1663ee4fda 100644 --- a/src/BenchmarkDotNet/Toolchains/Wasm/CsProjMonoWasmToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/Wasm/CsProjMonoWasmToolchain.cs @@ -9,6 +9,7 @@ public sealed class CsProjMonoWasmToolchain : CsProjWasmToolchain public static readonly CsProjMonoWasmToolchain Net90 = new(MonoWasmRuntime.Net90, WasmSettings.Default); public static readonly CsProjMonoWasmToolchain Net10_0 = new(MonoWasmRuntime.Net10_0, WasmSettings.Default); public static readonly CsProjMonoWasmToolchain Net11_0 = new(MonoWasmRuntime.Net11_0, WasmSettings.Default); + public static readonly CsProjMonoWasmToolchain Net12_0 = new(MonoWasmRuntime.Net12_0, WasmSettings.Default); private CsProjMonoWasmToolchain(MonoWasmRuntime runtime, WasmSettings settings) : base("MonoWasm", runtime, settings, aot: false, useCoreClrRuntime: false) { } @@ -25,6 +26,7 @@ public static CsProjMonoWasmToolchain From(MonoWasmRuntime runtime, WasmSettings 9 => Net90, 10 => Net10_0, 11 => Net11_0, + 12 => Net12_0, _ => new CsProjMonoWasmToolchain(runtime, settings), }; } diff --git a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json index 14e6a20eac..6dac89bb95 100644 --- a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json +++ b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json @@ -34,6 +34,10 @@ "description": "The target framework for the project.", "datatype": "choice", "choices": [ + { + "choice": "net12.0", + "description": ".NET 12" + }, { "choice": "net11.0", "description": ".NET 11" diff --git a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json index 2f22459e41..6c3696b4e2 100644 --- a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json +++ b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json @@ -34,6 +34,10 @@ "description": "The target framework for the project.", "datatype": "choice", "choices": [ + { + "choice": "net12.0", + "description": ".NET 12" + }, { "choice": "net11.0", "description": ".NET 11" diff --git a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json index 857f13fcaa..6cc44f1de2 100644 --- a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json +++ b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json @@ -34,6 +34,10 @@ "description": "The target framework for the project.", "datatype": "choice", "choices": [ + { + "choice": "net12.0", + "description": ".NET 12" + }, { "choice": "net11.0", "description": ".NET 11" diff --git a/tests/BenchmarkDotNet.Tests/ConfigParserTests.cs b/tests/BenchmarkDotNet.Tests/ConfigParserTests.cs index b873e2b1f7..2ae4efa37d 100644 --- a/tests/BenchmarkDotNet.Tests/ConfigParserTests.cs +++ b/tests/BenchmarkDotNet.Tests/ConfigParserTests.cs @@ -29,6 +29,7 @@ using Perfolizer.Horology; using System.Reflection; using BenchmarkDotNet.Toolchains.NetCoreApp; +using BenchmarkDotNet.Toolchains.R2R; namespace BenchmarkDotNet.Tests { @@ -281,7 +282,7 @@ public void SpecifyingCoreRunWithFullFrameworkTargetsMostRecentTfm() CoreRunToolchain coreRunToolchain = (CoreRunToolchain)coreRunJob.GetToolchain(); DotNetCliGenerator generator = (DotNetCliGenerator)coreRunToolchain.Generator; - Assert.Equal("net11.0", generator.Settings.TargetFrameworkMoniker); + Assert.Equal("net12.0", generator.Settings.TargetFrameworkMoniker); } [FactEnvSpecific("It's impossible to determine TFM for CoreRunToolchain if host process is not .NET (Core) process", EnvRequirement.DotNetCoreOnly)] @@ -396,6 +397,31 @@ public void WasmRuntimesResolveToTheirToolchains(string moniker, Type expectedTo Assert.IsType(expectedToolchain, config.GetJobs().Single().GetToolchain()); } + [Theory] + [InlineData(RuntimeMoniker.Net12_0, typeof(CsProjCoreToolchain))] + [InlineData(RuntimeMoniker.NativeAot12_0, typeof(CsProjNativeAotToolchain))] + [InlineData(RuntimeMoniker.Mono12_0, typeof(CsProjMonoCoreToolchain))] + [InlineData(RuntimeMoniker.R2R12_0, typeof(CsProjR2RToolchain))] + [InlineData(RuntimeMoniker.MonoWasm12_0, typeof(CsProjMonoWasmToolchain))] + [InlineData(RuntimeMoniker.MonoWasmAot12_0, typeof(CsProjMonoWasmAotToolchain))] + [InlineData("corewasm12.0", typeof(CsProjCoreWasmToolchain))] + public void Net12RuntimesResolveToTheirToolchains(string moniker, Type expectedToolchain) + { + string[] args = moniker.Contains("wasm") + ? ["-r", moniker, GetDummyWasmEngine()] + : ["-r", moniker]; + var (isSuccess, config, _) = ConfigParser.Parse(args, new OutputLogger(Output)); + + Assert.True(isSuccess); + Assert.NotNull(config); + var job = Assert.Single(config.GetJobs()); + var toolchain = job.GetToolchain(); + Assert.IsType(expectedToolchain, toolchain); + Assert.Equal(Runtime.Parse(moniker), job.GetRuntime()); + var generator = Assert.IsAssignableFrom(toolchain.Generator); + Assert.Equal("net12.0", generator.Settings.TargetFrameworkMoniker); + } + [Fact] public void IlCompilerPathParsedCorrectly() { @@ -407,6 +433,7 @@ public void IlCompilerPathParsedCorrectly() CsProjNativeAotToolchain? toolchain = config.GetJobs().Single().GetToolchain() as CsProjNativeAotToolchain; Assert.NotNull(toolchain); Assert.Equal(fakePath.FullName, ((NativeAotSettings)toolchain.Settings).LocalIlcPackages?.FullName); + Assert.Equal("12.0.0-dev", ((NativeAotSettings)toolchain.Settings).IlCompilerVersion); } [Theory] @@ -650,6 +677,8 @@ public void NetFrameworkMonikerParsedCorrectly(string tfm, string expectedVersio [InlineData("net10.0", "net10.0")] [InlineData("net11_0", "net11.0")] [InlineData("net11.0", "net11.0")] + [InlineData("net12_0", "net12.0")] + [InlineData("net12.0", "net12.0")] public void NetMonikersAreRecognizedAsNetCoreMonikers(string tfm, string expectedTfm) { var config = ConfigParser.Parse(["-r", tfm], new OutputLogger(Output)).config; diff --git a/tests/BenchmarkDotNet.Tests/RuntimeParseTests.cs b/tests/BenchmarkDotNet.Tests/RuntimeParseTests.cs index d66c9b9e8e..94dae4b64c 100644 --- a/tests/BenchmarkDotNet.Tests/RuntimeParseTests.cs +++ b/tests/BenchmarkDotNet.Tests/RuntimeParseTests.cs @@ -1,5 +1,11 @@ using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Toolchains; +using BenchmarkDotNet.Toolchains.Mono; +using BenchmarkDotNet.Toolchains.NativeAot; +using BenchmarkDotNet.Toolchains.NetCoreApp; +using BenchmarkDotNet.Toolchains.R2R; +using BenchmarkDotNet.Toolchains.Wasm; namespace BenchmarkDotNet.Tests; @@ -9,6 +15,13 @@ public class RuntimeParseTests // dotted / CLI spellings [InlineData("net8.0", typeof(CoreRuntime), 8, 0)] [InlineData("net10.0", typeof(CoreRuntime), 10, 0)] + [InlineData(RuntimeMoniker.Net12_0, typeof(CoreRuntime), 12, 0)] + [InlineData(RuntimeMoniker.NativeAot12_0, typeof(NativeAotRuntime), 12, 0)] + [InlineData(RuntimeMoniker.Mono12_0, typeof(MonoCoreRuntime), 12, 0)] + [InlineData(RuntimeMoniker.MonoWasm12_0, typeof(MonoWasmRuntime), 12, 0)] + [InlineData(RuntimeMoniker.MonoWasmAot12_0, typeof(MonoWasmAotRuntime), 12, 0)] + [InlineData(RuntimeMoniker.R2R12_0, typeof(R2RRuntime), 12, 0)] + [InlineData("corewasm12.0", typeof(CoreWasmRuntime), 12, 0)] [InlineData("netcoreapp3.1", typeof(CoreRuntime), 3, 1)] [InlineData("net472", typeof(ClrRuntime), 4, 7)] [InlineData("net48", typeof(ClrRuntime), 4, 8)] @@ -21,6 +34,13 @@ public class RuntimeParseTests // compact / enum-name spellings [InlineData("Net80", typeof(CoreRuntime), 8, 0)] [InlineData("Net10_0", typeof(CoreRuntime), 10, 0)] + [InlineData("Net12_0", typeof(CoreRuntime), 12, 0)] + [InlineData("NativeAot12_0", typeof(NativeAotRuntime), 12, 0)] + [InlineData("Mono12_0", typeof(MonoCoreRuntime), 12, 0)] + [InlineData("MonoWasm12_0", typeof(MonoWasmRuntime), 12, 0)] + [InlineData("MonoWasmAot12_0", typeof(MonoWasmAotRuntime), 12, 0)] + [InlineData("R2R12_0", typeof(R2RRuntime), 12, 0)] + [InlineData("CoreWasm12_0", typeof(CoreWasmRuntime), 12, 0)] [InlineData("NetCoreApp31", typeof(CoreRuntime), 3, 1)] [InlineData("NativeAot70", typeof(NativeAotRuntime), 7, 0)] [InlineData("Mono60", typeof(MonoCoreRuntime), 6, 0)] @@ -59,6 +79,7 @@ public void RejectsNonRuntimeMonikers(string moniker) [InlineData("net5.0-windows", "net5.0-windows")] [InlineData("net8.0-ios", "net8.0-ios")] [InlineData("net10.0-windows10.0.19041.0", "net10.0-windows10.0.19041.0")] + [InlineData("net12.0-windows", "net12.0-windows")] // The platform is never normalized, so the casing survives into the moniker. [InlineData("net10.0-WINDOWS", "net10.0-WINDOWS")] public void PreservesPlatformSpecificMonikers(string moniker, string expectedTfm) @@ -239,6 +260,25 @@ public void ParsesToTheSameInstanceIdentityAsKnownStatics() Assert.Equal(NativeAotRuntime.Net80, Runtime.Parse("nativeaot8.0")); } + [Fact] + public void Net12RuntimesAndDefaultToolchainsUseCachedInstances() + { + Assert.Same(CoreRuntime.Core12_0, Runtime.Parse(RuntimeMoniker.Net12_0)); + Assert.Same(NativeAotRuntime.Net12_0, Runtime.Parse(RuntimeMoniker.NativeAot12_0)); + Assert.Same(MonoCoreRuntime.Net12_0, Runtime.Parse(RuntimeMoniker.Mono12_0)); + Assert.Same(MonoWasmRuntime.Net12_0, Runtime.Parse(RuntimeMoniker.MonoWasm12_0)); + Assert.Same(MonoWasmAotRuntime.Net12_0, Runtime.Parse(RuntimeMoniker.MonoWasmAot12_0)); + Assert.Same(R2RRuntime.Net12_0, Runtime.Parse(RuntimeMoniker.R2R12_0)); + Assert.Same(CoreRuntime.Core12_0, CoreRuntime.Latest); + + Assert.Same(CsProjCoreToolchain.NetCoreApp12_0, CsProjCoreToolchain.From(CoreRuntime.Core12_0, NetCoreAppSettings.Default)); + Assert.Same(CsProjNativeAotToolchain.Net12_0, CsProjNativeAotToolchain.From(NativeAotRuntime.Net12_0, NativeAotSettings.Default)); + Assert.Same(CsProjMonoCoreToolchain.Mono12_0, CsProjMonoCoreToolchain.From(MonoCoreRuntime.Net12_0, MonoCoreSettings.Default)); + Assert.Same(CsProjMonoWasmToolchain.Net12_0, CsProjMonoWasmToolchain.From(MonoWasmRuntime.Net12_0, WasmSettings.Default)); + Assert.Same(CsProjMonoWasmAotToolchain.Net12_0, CsProjMonoWasmAotToolchain.From(MonoWasmAotRuntime.Net12_0, WasmSettings.Default)); + Assert.Same(CsProjR2RToolchain.R2R12_0, CsProjR2RToolchain.From(R2RRuntime.Net12_0, R2RSettings.Default)); + } + [Theory] // Every example the "invalid runtime" error advertises, so the message cannot drift from the parser. If a form is // added there, add it here; if one stops parsing, this fails rather than the CLI recommending something invalid. diff --git a/tests/BenchmarkDotNet.Tests/RuntimeVersionDetectionTests.cs b/tests/BenchmarkDotNet.Tests/RuntimeVersionDetectionTests.cs index bc0ee9e031..6f67d3117f 100644 --- a/tests/BenchmarkDotNet.Tests/RuntimeVersionDetectionTests.cs +++ b/tests/BenchmarkDotNet.Tests/RuntimeVersionDetectionTests.cs @@ -20,6 +20,7 @@ public class RuntimeVersionDetectionTests [InlineData(".NETCoreApp,Version=v9.0", 9, 0, "net9.0")] [InlineData(".NETCoreApp,Version=v10.0", 10, 0, "net10.0")] [InlineData(".NETCoreApp,Version=v11.0", 11, 0, "net11.0")] + [InlineData(".NETCoreApp,Version=v12.0", 12, 0, "net12.0")] [InlineData(".NETCoreApp,Version=v123.0", 123, 0, "net123.0")] public void TryGetVersionFromFrameworkNameHandlesValidInput(string frameworkName, int expectedMajor, int expectedMinor, string expectedTfm) {