From 131269b35f59ec056c5fa64445297d20874607ac Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Wed, 11 Feb 2026 18:13:41 +0100 Subject: [PATCH 01/14] Fix. --- src/BenchmarkDotNet/Templates/WasmCsProj.txt | 4 +++- .../BenchmarkDotNet/Templates}/test-main.js | 0 .../Toolchains/DotNetCli/DotNetCliCommand.cs | 1 - .../Toolchains/MonoWasm/WasmExecutor.cs | 6 ++++-- .../Toolchains/MonoWasm/WasmGenerator.cs | 11 +++++++++-- .../BenchmarkDotNet.IntegrationTests.csproj | 2 +- tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs | 2 +- 7 files changed, 18 insertions(+), 8 deletions(-) rename {tests/BenchmarkDotNet.IntegrationTests/AppBundle => src/BenchmarkDotNet/Templates}/test-main.js (100%) diff --git a/src/BenchmarkDotNet/Templates/WasmCsProj.txt b/src/BenchmarkDotNet/Templates/WasmCsProj.txt index 3af58fc514..901b8cd91f 100644 --- a/src/BenchmarkDotNet/Templates/WasmCsProj.txt +++ b/src/BenchmarkDotNet/Templates/WasmCsProj.txt @@ -32,8 +32,10 @@ - + + diff --git a/tests/BenchmarkDotNet.IntegrationTests/AppBundle/test-main.js b/src/BenchmarkDotNet/Templates/test-main.js similarity index 100% rename from tests/BenchmarkDotNet.IntegrationTests/AppBundle/test-main.js rename to src/BenchmarkDotNet/Templates/test-main.js diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommand.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommand.cs index 9cb1a7b56e..dababc3548 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommand.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommand.cs @@ -140,7 +140,6 @@ internal static string GetRestoreCommand(ArtifactsPaths artifactsPaths, BuildPar .AppendArgument($"\"{filePath}\"") // restore doesn't support -f argument. .AppendArgument(artifactsPaths.PackagesDirectoryName.IsBlank() ? string.Empty : $"--packages \"{artifactsPaths.PackagesDirectoryName}\"") - .AppendArgument(buildPartition?.Runtime is WasmRuntime ? "-r browser-wasm" : string.Empty) .AppendArgument(GetCustomMsBuildArguments(buildPartition.RepresentativeBenchmarkCase, buildPartition.Resolver)) .AppendArgument(extraArguments) .AppendArgument(GetMandatoryMsBuildSettings(buildPartition.BuildConfiguration)) diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs index f58eb786a5..75972caa98 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs @@ -57,12 +57,14 @@ private static Process CreateProcess(BenchmarkCase benchmarkCase, ArtifactsPaths { WasmRuntime runtime = (WasmRuntime)benchmarkCase.GetRuntime(); string mainJs = runtime.RuntimeMoniker < RuntimeMoniker.WasmNet70 ? "main.js" : "test-main.js"; + // test-main.js (net7.0+) uses ES module imports, V8 requires --module for that + string moduleFlag = runtime.RuntimeMoniker >= RuntimeMoniker.WasmNet70 ? "--module" : ""; var start = new ProcessStartInfo { FileName = runtime.JavaScriptEngine, - Arguments = $"{runtime.JavaScriptEngineArguments} {mainJs} -- --run {artifactsPaths.ProgramName}.dll {args} ", - WorkingDirectory = Path.Combine(artifactsPaths.BinariesDirectoryPath, "AppBundle"), + Arguments = $"{runtime.JavaScriptEngineArguments} {moduleFlag} {mainJs} -- --run {artifactsPaths.ProgramName}.dll {args} ", + WorkingDirectory = Path.Combine(artifactsPaths.BinariesDirectoryPath, "wwwroot"), UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardInput = false, // not supported by WASM! diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs index d17ad8c304..9378fd043f 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs @@ -46,7 +46,9 @@ protected void GenerateProjectFile(BuildPartition buildPartition, ArtifactsPaths var xmlDoc = new XmlDocument(); xmlDoc.Load(projectFile.FullName); - var (customProperties, sdkName) = GetSettingsThatNeedToBeCopied(xmlDoc, projectFile); + var (customProperties, _) = GetSettingsThatNeedToBeCopied(xmlDoc, projectFile); + // Always use Microsoft.NET.Sdk.WebAssembly for WASM projects. It auto-defaults UseMonoRuntime=true. + const string sdkName = "Microsoft.NET.Sdk.WebAssembly"; string content = new StringBuilder(ResourceHelper.LoadTemplate("WasmCsProj.txt")) .Replace("$PLATFORM$", buildPartition.Platform.ToConfig()) @@ -63,10 +65,15 @@ protected void GenerateProjectFile(BuildPartition buildPartition, ArtifactsPaths File.WriteAllText(artifactsPaths.ProjectFilePath, content); + // Place test-main.js in wwwroot/ next to the generated csproj. + string projectWwwroot = Path.Combine(Path.GetDirectoryName(artifactsPaths.ProjectFilePath)!, "wwwroot"); + Directory.CreateDirectory(projectWwwroot); + File.WriteAllText(Path.Combine(projectWwwroot, MainJS), ResourceHelper.LoadTemplate(MainJS)); + GatherReferences(buildPartition, artifactsPaths, logger); } - protected override string GetExecutablePath(string binariesDirectoryPath, string programName) => Path.Combine(binariesDirectoryPath, "AppBundle", MainJS); + protected override string GetExecutablePath(string binariesDirectoryPath, string programName) => Path.Combine(binariesDirectoryPath, "wwwroot", MainJS); protected override string GetBinariesDirectoryPath(string buildArtifactsDirectoryPath, string configuration) => Path.Combine(buildArtifactsDirectoryPath, "bin", configuration, TargetFrameworkMoniker, "browser-wasm"); diff --git a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj index 219bd88d02..2bb18fe619 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj +++ b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj @@ -59,7 +59,7 @@ - + diff --git a/tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs b/tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs index 2f4493c99d..6a4f8ee7cc 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs @@ -33,7 +33,7 @@ private ManualConfig GetConfig(MonoAotCompilerMode aotCompilerMode) var dotnetVersion = "net8.0"; var logger = new OutputLogger(Output); var netCoreAppSettings = new NetCoreAppSettings(dotnetVersion, null, "Wasm", aotCompilerMode: aotCompilerMode); - var mainJsPath = Path.Combine(AppContext.BaseDirectory, "AppBundle", "test-main.js"); + var mainJsPath = Path.Combine(AppContext.BaseDirectory, "wwwroot", "test-main.js"); return ManualConfig.CreateEmpty() .AddLogger(logger) From c642f31655a4b84dcacc7532d6a43de8d352a036 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Thu, 12 Feb 2026 10:57:48 +0100 Subject: [PATCH 02/14] Move `test-main.js` to Templates as embedded resource for WasmGenerator, --- tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs b/tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs index 6a4f8ee7cc..ed46536389 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs @@ -33,7 +33,10 @@ private ManualConfig GetConfig(MonoAotCompilerMode aotCompilerMode) var dotnetVersion = "net8.0"; var logger = new OutputLogger(Output); var netCoreAppSettings = new NetCoreAppSettings(dotnetVersion, null, "Wasm", aotCompilerMode: aotCompilerMode); - var mainJsPath = Path.Combine(AppContext.BaseDirectory, "wwwroot", "test-main.js"); + var mainJsDir = Path.Combine(Path.GetTempPath(), "BenchmarkDotNet.IntegrationTests"); + Directory.CreateDirectory(mainJsDir); + var mainJsPath = Path.Combine(mainJsDir, "test-main.js"); + File.WriteAllText(mainJsPath, ResourceHelper.LoadTemplate("test-main.js")); return ManualConfig.CreateEmpty() .AddLogger(logger) From 431e5dea117047ead286ca599c12d9cfc754069c Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Thu, 12 Feb 2026 11:06:31 +0100 Subject: [PATCH 03/14] Add missing namespace. --- tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs b/tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs index ed46536389..cd9dc8050a 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs @@ -5,6 +5,7 @@ using BenchmarkDotNet.Configs; using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Helpers; using BenchmarkDotNet.IntegrationTests.Diagnosers; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Portability; From 758df2b403168ed3512fb3eb2f04c8386e05e36c Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Thu, 12 Feb 2026 15:41:34 +0100 Subject: [PATCH 04/14] Rename test-main.js to benchmark-main.mjs; remove unused WasmGenerateRunV8Script and redundant PublishTrimmed. --- src/BenchmarkDotNet/Templates/WasmCsProj.txt | 2 -- .../Templates/{test-main.js => benchmark-main.mjs} | 0 src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs | 6 ++---- src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs | 4 ++-- .../BenchmarkDotNet.IntegrationTests.csproj | 1 - 5 files changed, 4 insertions(+), 9 deletions(-) rename src/BenchmarkDotNet/Templates/{test-main.js => benchmark-main.mjs} (100%) diff --git a/src/BenchmarkDotNet/Templates/WasmCsProj.txt b/src/BenchmarkDotNet/Templates/WasmCsProj.txt index 901b8cd91f..c0f4ec5043 100644 --- a/src/BenchmarkDotNet/Templates/WasmCsProj.txt +++ b/src/BenchmarkDotNet/Templates/WasmCsProj.txt @@ -20,8 +20,6 @@ browser-wasm true $RUN_AOT$ - $(RunAOTCompilation) - true false false false diff --git a/src/BenchmarkDotNet/Templates/test-main.js b/src/BenchmarkDotNet/Templates/benchmark-main.mjs similarity index 100% rename from src/BenchmarkDotNet/Templates/test-main.js rename to src/BenchmarkDotNet/Templates/benchmark-main.mjs diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs index 75972caa98..60fe7722ee 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs @@ -56,14 +56,12 @@ private static ExecuteResult Execute(BenchmarkCase benchmarkCase, BenchmarkId be private static Process CreateProcess(BenchmarkCase benchmarkCase, ArtifactsPaths artifactsPaths, string args, IResolver resolver) { WasmRuntime runtime = (WasmRuntime)benchmarkCase.GetRuntime(); - string mainJs = runtime.RuntimeMoniker < RuntimeMoniker.WasmNet70 ? "main.js" : "test-main.js"; - // test-main.js (net7.0+) uses ES module imports, V8 requires --module for that - string moduleFlag = runtime.RuntimeMoniker >= RuntimeMoniker.WasmNet70 ? "--module" : ""; + string mainJs = runtime.RuntimeMoniker < RuntimeMoniker.WasmNet70 ? "main.js" : "benchmark-main.mjs"; var start = new ProcessStartInfo { FileName = runtime.JavaScriptEngine, - Arguments = $"{runtime.JavaScriptEngineArguments} {moduleFlag} {mainJs} -- --run {artifactsPaths.ProgramName}.dll {args} ", + Arguments = $"{runtime.JavaScriptEngineArguments} {mainJs} -- --run {artifactsPaths.ProgramName}.dll {args} ", WorkingDirectory = Path.Combine(artifactsPaths.BinariesDirectoryPath, "wwwroot"), UseShellExecute = false, RedirectStandardOutput = true, diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs index 9378fd043f..224bc18136 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs @@ -19,7 +19,7 @@ public WasmGenerator(string targetFrameworkMoniker, string cliPath, string packa : base(targetFrameworkMoniker, cliPath, packagesPath, runtimeFrameworkVersion: null) { CustomRuntimePack = customRuntimePack; - MainJS = (targetFrameworkMoniker == "net5.0" || targetFrameworkMoniker == "net6.0") ? "main.js" : "test-main.js"; + MainJS = (targetFrameworkMoniker == "net5.0" || targetFrameworkMoniker == "net6.0") ? "main.js" : "benchmark-main.mjs"; BenchmarkRunCallType = aot ? Code.CodeGenBenchmarkRunCallType.Direct : Code.CodeGenBenchmarkRunCallType.Reflection; } @@ -65,7 +65,7 @@ protected void GenerateProjectFile(BuildPartition buildPartition, ArtifactsPaths File.WriteAllText(artifactsPaths.ProjectFilePath, content); - // Place test-main.js in wwwroot/ next to the generated csproj. + // Place benchmark-main.mjs in wwwroot/ next to the generated csproj. string projectWwwroot = Path.Combine(Path.GetDirectoryName(artifactsPaths.ProjectFilePath)!, "wwwroot"); Directory.CreateDirectory(projectWwwroot); File.WriteAllText(Path.Combine(projectWwwroot, MainJS), ResourceHelper.LoadTemplate(MainJS)); diff --git a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj index 2bb18fe619..09c750d424 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj +++ b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj @@ -59,7 +59,6 @@ - From 708faafa3c569afeebb85c2fc8140a8a944687de Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Thu, 12 Feb 2026 16:08:00 +0100 Subject: [PATCH 05/14] Comment was out-of-date. --- src/BenchmarkDotNet/Templates/WasmCsProj.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BenchmarkDotNet/Templates/WasmCsProj.txt b/src/BenchmarkDotNet/Templates/WasmCsProj.txt index c0f4ec5043..6f62f395e5 100644 --- a/src/BenchmarkDotNet/Templates/WasmCsProj.txt +++ b/src/BenchmarkDotNet/Templates/WasmCsProj.txt @@ -32,7 +32,7 @@ + Since BDN uses dotnet build, explicitly copy benchmark-main.mjs to the output. --> From bd7110c034fbfc042bf2772be1833e19573ed205 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Thu, 12 Feb 2026 17:37:46 +0100 Subject: [PATCH 06/14] Missing changes. --- tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs b/tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs index cd9dc8050a..84f15f28dd 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs @@ -34,10 +34,11 @@ private ManualConfig GetConfig(MonoAotCompilerMode aotCompilerMode) var dotnetVersion = "net8.0"; var logger = new OutputLogger(Output); var netCoreAppSettings = new NetCoreAppSettings(dotnetVersion, null, "Wasm", aotCompilerMode: aotCompilerMode); + const string mainJsName = "benchmark-main.mjs"; var mainJsDir = Path.Combine(Path.GetTempPath(), "BenchmarkDotNet.IntegrationTests"); Directory.CreateDirectory(mainJsDir); - var mainJsPath = Path.Combine(mainJsDir, "test-main.js"); - File.WriteAllText(mainJsPath, ResourceHelper.LoadTemplate("test-main.js")); + var mainJsPath = Path.Combine(mainJsDir, mainJsName); + File.WriteAllText(mainJsPath, ResourceHelper.LoadTemplate(mainJsName)); return ManualConfig.CreateEmpty() .AddLogger(logger) From a42ec61f3faed1e1f9ff2230ff182a212455fe5b Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Fri, 13 Feb 2026 15:20:09 +0100 Subject: [PATCH 07/14] Feedback: enable coreclr microbenchmarks. --- .../Environments/Runtimes/WasmRuntime.cs | 15 ++++++++++++--- .../Toolchains/MonoWasm/WasmGenerator.cs | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/BenchmarkDotNet/Environments/Runtimes/WasmRuntime.cs b/src/BenchmarkDotNet/Environments/Runtimes/WasmRuntime.cs index 66247c49d9..be51bebff6 100644 --- a/src/BenchmarkDotNet/Environments/Runtimes/WasmRuntime.cs +++ b/src/BenchmarkDotNet/Environments/Runtimes/WasmRuntime.cs @@ -21,6 +21,13 @@ public class WasmRuntime : Runtime, IEquatable public string WasmDataDir { get; } + /// + /// When true (default), the generated project uses Microsoft.NET.Sdk.WebAssembly which sets UseMonoRuntime=true + /// and resolves the Mono runtime pack (Microsoft.NETCore.App.Runtime.Mono.browser-wasm). When false, the generated + /// project uses Microsoft.NET.Sdk which resolves the CoreCLR runtime pack (Microsoft.NETCore.App.Runtime.browser-wasm). + /// + public bool IsMonoRuntime { get; } + /// /// creates new instance of WasmRuntime /// @@ -31,7 +38,8 @@ public class WasmRuntime : Runtime, IEquatable /// Specifies whether AOT or Interpreter (default) project should be generated. /// Specifies a wasm data directory surfaced as $(WasmDataDir) for the project /// Runtime moniker - public WasmRuntime(string msBuildMoniker = "net5.0", string displayName = "Wasm", string javaScriptEngine = "v8", string javaScriptEngineArguments = "--expose_wasm", bool aot = false, string wasmDataDir = "", RuntimeMoniker moniker = RuntimeMoniker.Wasm) : base(moniker, msBuildMoniker, displayName) + /// When true (default), use Mono runtime pack; when false, use CoreCLR runtime pack. + public WasmRuntime(string msBuildMoniker = "net5.0", string displayName = "Wasm", string javaScriptEngine = "v8", string javaScriptEngineArguments = "--expose_wasm", bool aot = false, string wasmDataDir = "", RuntimeMoniker moniker = RuntimeMoniker.Wasm, bool isMonoRuntime = true) : base(moniker, msBuildMoniker, displayName) { if (javaScriptEngine.IsNotBlank() && javaScriptEngine != "v8" && !File.Exists(javaScriptEngine)) throw new FileNotFoundException($"Provided {nameof(javaScriptEngine)} file: \"{javaScriptEngine}\" doest NOT exist"); @@ -40,15 +48,16 @@ public WasmRuntime(string msBuildMoniker = "net5.0", string displayName = "Wasm" JavaScriptEngineArguments = javaScriptEngineArguments; Aot = aot; WasmDataDir = wasmDataDir; + IsMonoRuntime = isMonoRuntime; } public override bool Equals(object? obj) => obj is WasmRuntime other && Equals(other); public bool Equals(WasmRuntime? other) - => other != null && base.Equals(other) && other.JavaScriptEngine == JavaScriptEngine && other.JavaScriptEngineArguments == JavaScriptEngineArguments && other.Aot == Aot; + => other != null && base.Equals(other) && other.JavaScriptEngine == JavaScriptEngine && other.JavaScriptEngineArguments == JavaScriptEngineArguments && other.Aot == Aot && other.IsMonoRuntime == IsMonoRuntime; public override int GetHashCode() - => HashCode.Combine(base.GetHashCode(), JavaScriptEngine, JavaScriptEngineArguments, Aot); + => HashCode.Combine(base.GetHashCode(), JavaScriptEngine, JavaScriptEngineArguments, Aot, IsMonoRuntime); } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs index 224bc18136..11cddd091a 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs @@ -48,7 +48,7 @@ protected void GenerateProjectFile(BuildPartition buildPartition, ArtifactsPaths xmlDoc.Load(projectFile.FullName); var (customProperties, _) = GetSettingsThatNeedToBeCopied(xmlDoc, projectFile); // Always use Microsoft.NET.Sdk.WebAssembly for WASM projects. It auto-defaults UseMonoRuntime=true. - const string sdkName = "Microsoft.NET.Sdk.WebAssembly"; + string sdkName = runtime.IsMonoRuntime ? "Microsoft.NET.Sdk.WebAssembly" : "Microsoft.NET.Sdk"; string content = new StringBuilder(ResourceHelper.LoadTemplate("WasmCsProj.txt")) .Replace("$PLATFORM$", buildPartition.Platform.ToConfig()) From 62df686b51dde089d2d71ba55d08ccff47c81725 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Fri, 13 Feb 2026 15:34:26 +0100 Subject: [PATCH 08/14] Add runtime flavor switch for CLI. --- src/BenchmarkDotNet/ConsoleArguments/CommandLineOptions.cs | 3 +++ src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/BenchmarkDotNet/ConsoleArguments/CommandLineOptions.cs b/src/BenchmarkDotNet/ConsoleArguments/CommandLineOptions.cs index 397443767a..5a965aefbf 100644 --- a/src/BenchmarkDotNet/ConsoleArguments/CommandLineOptions.cs +++ b/src/BenchmarkDotNet/ConsoleArguments/CommandLineOptions.cs @@ -221,6 +221,9 @@ public bool UseDisassemblyDiagnoser [Option("wasmDataDir", Required = false, HelpText = "Wasm data directory")] public DirectoryInfo? WasmDataDirectory { get; set; } + [Option("wasmCoreCLR", Required = false, Default = false, HelpText = "Use CoreCLR runtime pack (Microsoft.NETCore.App.Runtime.browser-wasm) instead of the Mono runtime pack for WASM benchmarks.")] + public bool WasmCoreCLR { get; set; } + [Option("noForcedGCs", Required = false, HelpText = "Specifying would not forcefully induce any GCs.")] public bool NoForcedGCs { get; set; } diff --git a/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs b/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs index 292b27cca8..20fa357c59 100644 --- a/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs +++ b/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs @@ -735,7 +735,8 @@ private static Job MakeWasmJob(Job baseJob, CommandLineOptions options, string m javaScriptEngineArguments: options.WasmJavaScriptEngineArguments ?? "", aot: wasmAot, wasmDataDir: options.WasmDataDirectory?.FullName ?? "", - moniker: moniker); + moniker: moniker, + isMonoRuntime: !options.WasmCoreCLR); var toolChain = WasmToolchain.From(new NetCoreAppSettings( targetFrameworkMoniker: wasmRuntime.MsBuildMoniker, From 79c54342f7bebbda7351197093f0c37209756933 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Fri, 13 Feb 2026 16:46:59 +0100 Subject: [PATCH 09/14] Comply with feedback: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Require --output (BuildPartition.cs) — WASM needs explicit output paths because the WebAssembly SDK publishes to wwwroot/ inside the output directory. Without --output, build artifacts land in the default bin/ tree where the executor doesn't look. excludeArtifactsPath (DotNetCliCommand.cs) — BDN normally passes /p:ArtifactsPath to redirect output. But the SDK auto-sets UseArtifactsOutput=true when ArtifactsPath is non-empty, which adds $(ArtifactsPath)/** to DefaultItemExcludes — excluding every file in the project directory (including wwwroot/) from default Content globs. For WASM, we pass /p:UseArtifactsOutput=false instead, since OutDir/OutputPath/--output already control where output goes. Content Update (WasmCsProj.txt) — With UseArtifactsOutput=false, the SDK's default glob works, so Content Update can upgrade those items with CopyToOutputDirectory=PreserveNewest. --- src/BenchmarkDotNet/Running/BuildPartition.cs | 10 +++++- src/BenchmarkDotNet/Templates/WasmCsProj.txt | 4 +-- .../Toolchains/DotNetCli/DotNetCliCommand.cs | 31 ++++++++++++++----- .../WasmTests.cs | 8 ----- 4 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/BenchmarkDotNet/Running/BuildPartition.cs b/src/BenchmarkDotNet/Running/BuildPartition.cs index 4baff0aa0d..6c7fab62d5 100644 --- a/src/BenchmarkDotNet/Running/BuildPartition.cs +++ b/src/BenchmarkDotNet/Running/BuildPartition.cs @@ -12,6 +12,7 @@ using BenchmarkDotNet.Toolchains; using BenchmarkDotNet.Toolchains.CsProj; using BenchmarkDotNet.Toolchains.DotNetCli; +using BenchmarkDotNet.Toolchains.MonoWasm; using BenchmarkDotNet.Toolchains.Roslyn; using JetBrains.Annotations; @@ -115,7 +116,14 @@ internal bool ForcedNoDependenciesForIntegrationTests return false; var job = RepresentativeBenchmarkCase.Job; - if (job.GetToolchain().Builder is not DotNetCliBuilder) + var toolchain = job.GetToolchain(); + if (toolchain.Builder is not DotNetCliBuilder) + return false; + + // WASM builds require --output to place binaries in the RID-qualified path + // (e.g. browser-wasm/) that GetBinariesDirectoryPath expects. + // --no-dependencies with excludeOutput skips --output, breaking the build. + if (toolchain is WasmToolchain) return false; return !job.HasDynamicBuildCharacteristic(); diff --git a/src/BenchmarkDotNet/Templates/WasmCsProj.txt b/src/BenchmarkDotNet/Templates/WasmCsProj.txt index 6f62f395e5..6d0fc2117b 100644 --- a/src/BenchmarkDotNet/Templates/WasmCsProj.txt +++ b/src/BenchmarkDotNet/Templates/WasmCsProj.txt @@ -31,9 +31,7 @@ - - + diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommand.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommand.cs index dababc3548..11c19d5bed 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommand.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommand.cs @@ -10,6 +10,7 @@ using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Portability; using BenchmarkDotNet.Running; +using BenchmarkDotNet.Toolchains.MonoWasm; using BenchmarkDotNet.Toolchains.Results; using JetBrains.Annotations; @@ -135,7 +136,9 @@ public DotNetCliCommandResult PublishNoRestore() GetPublishCommand(GenerateResult.ArtifactsPaths, BuildPartition, FilePath, TargetFrameworkMoniker, $"{Arguments} --no-restore", "publish-no-restore"))); internal static string GetRestoreCommand(ArtifactsPaths artifactsPaths, BuildPartition buildPartition, string filePath, string? extraArguments = null, string? binLogSuffix = null, bool excludeOutput = false) - => new StringBuilder() + { + bool excludeArtifactsPath = buildPartition.RepresentativeBenchmarkCase.Job.GetToolchain() is WasmToolchain; + return new StringBuilder() .AppendArgument("restore") .AppendArgument($"\"{filePath}\"") // restore doesn't support -f argument. @@ -144,11 +147,14 @@ internal static string GetRestoreCommand(ArtifactsPaths artifactsPaths, BuildPar .AppendArgument(extraArguments) .AppendArgument(GetMandatoryMsBuildSettings(buildPartition.BuildConfiguration)) .AppendArgument(GetMsBuildBinLogArgument(buildPartition, binLogSuffix)) - .MaybeAppendOutputPaths(artifactsPaths, true, excludeOutput) + .MaybeAppendOutputPaths(artifactsPaths, isRestore: true, excludeOutput: excludeOutput, excludeArtifactsPath: excludeArtifactsPath) .ToString(); + } internal static string GetBuildCommand(ArtifactsPaths artifactsPaths, BuildPartition buildPartition, string filePath, string tfm, string? extraArguments = null, string? binLogSuffix = null, bool excludeOutput = false) - => new StringBuilder() + { + bool excludeArtifactsPath = buildPartition.RepresentativeBenchmarkCase.Job.GetToolchain() is WasmToolchain; + return new StringBuilder() .AppendArgument("build") .AppendArgument($"\"{filePath}\"") .AppendArgument($"-f {tfm}") @@ -158,11 +164,14 @@ internal static string GetBuildCommand(ArtifactsPaths artifactsPaths, BuildParti .AppendArgument(GetMandatoryMsBuildSettings(buildPartition.BuildConfiguration)) .AppendArgument(artifactsPaths.PackagesDirectoryName.IsBlank() ? string.Empty : $"/p:NuGetPackageRoot=\"{artifactsPaths.PackagesDirectoryName}\"") .AppendArgument(GetMsBuildBinLogArgument(buildPartition, binLogSuffix)) - .MaybeAppendOutputPaths(artifactsPaths, excludeOutput: excludeOutput) + .MaybeAppendOutputPaths(artifactsPaths, excludeOutput: excludeOutput, excludeArtifactsPath: excludeArtifactsPath) .ToString(); + } internal static string GetPublishCommand(ArtifactsPaths artifactsPaths, BuildPartition buildPartition, string filePath, string tfm, string? extraArguments = null, string? binLogSuffix = null) - => new StringBuilder() + { + bool excludeArtifactsPath = buildPartition.RepresentativeBenchmarkCase.Job.GetToolchain() is WasmToolchain; + return new StringBuilder() .AppendArgument("publish") .AppendArgument($"\"{filePath}\"") .AppendArgument($"-f {tfm}") @@ -172,8 +181,9 @@ internal static string GetPublishCommand(ArtifactsPaths artifactsPaths, BuildPar .AppendArgument(GetMandatoryMsBuildSettings(buildPartition.BuildConfiguration)) .AppendArgument(artifactsPaths.PackagesDirectoryName.IsBlank() ? string.Empty : $"/p:NuGetPackageRoot=\"{artifactsPaths.PackagesDirectoryName}\"") .AppendArgument(GetMsBuildBinLogArgument(buildPartition, binLogSuffix)) - .MaybeAppendOutputPaths(artifactsPaths) + .MaybeAppendOutputPaths(artifactsPaths, excludeArtifactsPath: excludeArtifactsPath) .ToString(); + } private static string GetMsBuildBinLogArgument(BuildPartition buildPartition, string? suffix) { @@ -215,12 +225,17 @@ internal static class DotNetCliCommandExtensions // We force the project to output binaries to a new directory. // Specifying --output and --no-dependencies breaks the build (because the previous build was not done using the custom output path), // so we don't include it if we're building no-deps (only supported for integration tests). - internal static StringBuilder MaybeAppendOutputPaths(this StringBuilder stringBuilder, ArtifactsPaths artifactsPaths, bool isRestore = false, bool excludeOutput = false) + internal static StringBuilder MaybeAppendOutputPaths(this StringBuilder stringBuilder, ArtifactsPaths artifactsPaths, bool isRestore = false, bool excludeOutput = false, bool excludeArtifactsPath = false) => excludeOutput ? stringBuilder : stringBuilder // Use AltDirectorySeparatorChar so it's not interpreted as an escaped quote `\"`. - .AppendArgument($"/p:ArtifactsPath=\"{artifactsPaths.BuildArtifactsDirectoryPath}{Path.AltDirectorySeparatorChar}\"") + // When excludeArtifactsPath is true, we skip /p:ArtifactsPath and set /p:UseArtifactsOutput=false instead. + // This prevents the SDK from adding $(ArtifactsPath)/** to DefaultItemExcludes, which would + // exclude all project files (including wwwroot/) from default Content globs. + .AppendArgument(excludeArtifactsPath + ? "/p:UseArtifactsOutput=false" + : $"/p:ArtifactsPath=\"{artifactsPaths.BuildArtifactsDirectoryPath}{Path.AltDirectorySeparatorChar}\"") .AppendArgument($"/p:OutDir=\"{artifactsPaths.BinariesDirectoryPath}{Path.AltDirectorySeparatorChar}\"") // OutputPath is legacy, per-project version of OutDir. We set both just in case. https://github.com/dotnet/msbuild/issues/87 .AppendArgument($"/p:OutputPath=\"{artifactsPaths.BinariesDirectoryPath}{Path.AltDirectorySeparatorChar}\"") diff --git a/tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs b/tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs index 84f15f28dd..690f5a5173 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/WasmTests.cs @@ -1,11 +1,9 @@ using System; -using System.IO; using System.Linq; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Environments; -using BenchmarkDotNet.Helpers; using BenchmarkDotNet.IntegrationTests.Diagnosers; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Portability; @@ -34,16 +32,10 @@ private ManualConfig GetConfig(MonoAotCompilerMode aotCompilerMode) var dotnetVersion = "net8.0"; var logger = new OutputLogger(Output); var netCoreAppSettings = new NetCoreAppSettings(dotnetVersion, null, "Wasm", aotCompilerMode: aotCompilerMode); - const string mainJsName = "benchmark-main.mjs"; - var mainJsDir = Path.Combine(Path.GetTempPath(), "BenchmarkDotNet.IntegrationTests"); - Directory.CreateDirectory(mainJsDir); - var mainJsPath = Path.Combine(mainJsDir, mainJsName); - File.WriteAllText(mainJsPath, ResourceHelper.LoadTemplate(mainJsName)); return ManualConfig.CreateEmpty() .AddLogger(logger) .AddJob(Job.Dry - .WithArguments([new MsBuildArgument($"/p:WasmMainJSPath={mainJsPath}")]) .WithRuntime(new WasmRuntime(dotnetVersion, moniker: RuntimeMoniker.WasmNet80, javaScriptEngineArguments: "--expose_wasm --module")) .WithToolchain(WasmToolchain.From(netCoreAppSettings))) .WithBuildTimeout(TimeSpan.FromSeconds(240)) From d71e719e6cc81e4be6df6ea5579db3e61b650024 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Mon, 16 Feb 2026 09:08:34 +0100 Subject: [PATCH 10/14] Feedback: clean out of support targets. --- src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs index 11cddd091a..cd8cf607cf 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs @@ -13,13 +13,12 @@ namespace BenchmarkDotNet.Toolchains.MonoWasm public class WasmGenerator : CsProjGenerator { private readonly string CustomRuntimePack; - private readonly string MainJS; + private const string MainJS = "benchmark-main.mjs"; public WasmGenerator(string targetFrameworkMoniker, string cliPath, string packagesPath, string customRuntimePack, bool aot) : base(targetFrameworkMoniker, cliPath, packagesPath, runtimeFrameworkVersion: null) { CustomRuntimePack = customRuntimePack; - MainJS = (targetFrameworkMoniker == "net5.0" || targetFrameworkMoniker == "net6.0") ? "main.js" : "benchmark-main.mjs"; BenchmarkRunCallType = aot ? Code.CodeGenBenchmarkRunCallType.Direct : Code.CodeGenBenchmarkRunCallType.Reflection; } From 6d169345eb5c5304cfdef1b4452adf44e770ef0c Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Mon, 16 Feb 2026 15:03:04 +0100 Subject: [PATCH 11/14] Publish to avoid chagnes to `ForcedNoDependenciesForIntegrationTests` + subdir for artifacts to avoid issues with excluding wwwroot files from contents. --- src/BenchmarkDotNet/Running/BuildPartition.cs | 10 +----- .../Toolchains/DotNetCli/DotNetCliCommand.cs | 33 ++++++------------- .../DotNetCli/DotNetCliPublisher.cs | 5 +-- .../Toolchains/MonoWasm/WasmExecutor.cs | 2 +- .../Toolchains/MonoWasm/WasmToolchain.cs | 2 +- 5 files changed, 16 insertions(+), 36 deletions(-) diff --git a/src/BenchmarkDotNet/Running/BuildPartition.cs b/src/BenchmarkDotNet/Running/BuildPartition.cs index 6c7fab62d5..4baff0aa0d 100644 --- a/src/BenchmarkDotNet/Running/BuildPartition.cs +++ b/src/BenchmarkDotNet/Running/BuildPartition.cs @@ -12,7 +12,6 @@ using BenchmarkDotNet.Toolchains; using BenchmarkDotNet.Toolchains.CsProj; using BenchmarkDotNet.Toolchains.DotNetCli; -using BenchmarkDotNet.Toolchains.MonoWasm; using BenchmarkDotNet.Toolchains.Roslyn; using JetBrains.Annotations; @@ -116,14 +115,7 @@ internal bool ForcedNoDependenciesForIntegrationTests return false; var job = RepresentativeBenchmarkCase.Job; - var toolchain = job.GetToolchain(); - if (toolchain.Builder is not DotNetCliBuilder) - return false; - - // WASM builds require --output to place binaries in the RID-qualified path - // (e.g. browser-wasm/) that GetBinariesDirectoryPath expects. - // --no-dependencies with excludeOutput skips --output, breaking the build. - if (toolchain is WasmToolchain) + if (job.GetToolchain().Builder is not DotNetCliBuilder) return false; return !job.HasDynamicBuildCharacteristic(); diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommand.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommand.cs index 11c19d5bed..bda01f9487 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommand.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommand.cs @@ -10,7 +10,6 @@ using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Portability; using BenchmarkDotNet.Running; -using BenchmarkDotNet.Toolchains.MonoWasm; using BenchmarkDotNet.Toolchains.Results; using JetBrains.Annotations; @@ -136,9 +135,7 @@ public DotNetCliCommandResult PublishNoRestore() GetPublishCommand(GenerateResult.ArtifactsPaths, BuildPartition, FilePath, TargetFrameworkMoniker, $"{Arguments} --no-restore", "publish-no-restore"))); internal static string GetRestoreCommand(ArtifactsPaths artifactsPaths, BuildPartition buildPartition, string filePath, string? extraArguments = null, string? binLogSuffix = null, bool excludeOutput = false) - { - bool excludeArtifactsPath = buildPartition.RepresentativeBenchmarkCase.Job.GetToolchain() is WasmToolchain; - return new StringBuilder() + => new StringBuilder() .AppendArgument("restore") .AppendArgument($"\"{filePath}\"") // restore doesn't support -f argument. @@ -147,14 +144,11 @@ internal static string GetRestoreCommand(ArtifactsPaths artifactsPaths, BuildPar .AppendArgument(extraArguments) .AppendArgument(GetMandatoryMsBuildSettings(buildPartition.BuildConfiguration)) .AppendArgument(GetMsBuildBinLogArgument(buildPartition, binLogSuffix)) - .MaybeAppendOutputPaths(artifactsPaths, isRestore: true, excludeOutput: excludeOutput, excludeArtifactsPath: excludeArtifactsPath) + .MaybeAppendOutputPaths(artifactsPaths, true, excludeOutput) .ToString(); - } internal static string GetBuildCommand(ArtifactsPaths artifactsPaths, BuildPartition buildPartition, string filePath, string tfm, string? extraArguments = null, string? binLogSuffix = null, bool excludeOutput = false) - { - bool excludeArtifactsPath = buildPartition.RepresentativeBenchmarkCase.Job.GetToolchain() is WasmToolchain; - return new StringBuilder() + => new StringBuilder() .AppendArgument("build") .AppendArgument($"\"{filePath}\"") .AppendArgument($"-f {tfm}") @@ -164,14 +158,11 @@ internal static string GetBuildCommand(ArtifactsPaths artifactsPaths, BuildParti .AppendArgument(GetMandatoryMsBuildSettings(buildPartition.BuildConfiguration)) .AppendArgument(artifactsPaths.PackagesDirectoryName.IsBlank() ? string.Empty : $"/p:NuGetPackageRoot=\"{artifactsPaths.PackagesDirectoryName}\"") .AppendArgument(GetMsBuildBinLogArgument(buildPartition, binLogSuffix)) - .MaybeAppendOutputPaths(artifactsPaths, excludeOutput: excludeOutput, excludeArtifactsPath: excludeArtifactsPath) + .MaybeAppendOutputPaths(artifactsPaths, excludeOutput: excludeOutput) .ToString(); - } internal static string GetPublishCommand(ArtifactsPaths artifactsPaths, BuildPartition buildPartition, string filePath, string tfm, string? extraArguments = null, string? binLogSuffix = null) - { - bool excludeArtifactsPath = buildPartition.RepresentativeBenchmarkCase.Job.GetToolchain() is WasmToolchain; - return new StringBuilder() + => new StringBuilder() .AppendArgument("publish") .AppendArgument($"\"{filePath}\"") .AppendArgument($"-f {tfm}") @@ -181,9 +172,8 @@ internal static string GetPublishCommand(ArtifactsPaths artifactsPaths, BuildPar .AppendArgument(GetMandatoryMsBuildSettings(buildPartition.BuildConfiguration)) .AppendArgument(artifactsPaths.PackagesDirectoryName.IsBlank() ? string.Empty : $"/p:NuGetPackageRoot=\"{artifactsPaths.PackagesDirectoryName}\"") .AppendArgument(GetMsBuildBinLogArgument(buildPartition, binLogSuffix)) - .MaybeAppendOutputPaths(artifactsPaths, excludeArtifactsPath: excludeArtifactsPath) + .MaybeAppendOutputPaths(artifactsPaths) .ToString(); - } private static string GetMsBuildBinLogArgument(BuildPartition buildPartition, string? suffix) { @@ -225,17 +215,14 @@ internal static class DotNetCliCommandExtensions // We force the project to output binaries to a new directory. // Specifying --output and --no-dependencies breaks the build (because the previous build was not done using the custom output path), // so we don't include it if we're building no-deps (only supported for integration tests). - internal static StringBuilder MaybeAppendOutputPaths(this StringBuilder stringBuilder, ArtifactsPaths artifactsPaths, bool isRestore = false, bool excludeOutput = false, bool excludeArtifactsPath = false) + internal static StringBuilder MaybeAppendOutputPaths(this StringBuilder stringBuilder, ArtifactsPaths artifactsPaths, bool isRestore = false, bool excludeOutput = false) => excludeOutput ? stringBuilder : stringBuilder // Use AltDirectorySeparatorChar so it's not interpreted as an escaped quote `\"`. - // When excludeArtifactsPath is true, we skip /p:ArtifactsPath and set /p:UseArtifactsOutput=false instead. - // This prevents the SDK from adding $(ArtifactsPath)/** to DefaultItemExcludes, which would - // exclude all project files (including wwwroot/) from default Content globs. - .AppendArgument(excludeArtifactsPath - ? "/p:UseArtifactsOutput=false" - : $"/p:ArtifactsPath=\"{artifactsPaths.BuildArtifactsDirectoryPath}{Path.AltDirectorySeparatorChar}\"") + // Use a subdirectory for ArtifactsPath so that DefaultItemExcludes (which the SDK + // sets to $(ArtifactsPath)/**) doesn't cover project-level files like wwwroot/. + .AppendArgument($"/p:ArtifactsPath=\"{artifactsPaths.BuildArtifactsDirectoryPath}{Path.AltDirectorySeparatorChar}.artifacts{Path.AltDirectorySeparatorChar}\"") .AppendArgument($"/p:OutDir=\"{artifactsPaths.BinariesDirectoryPath}{Path.AltDirectorySeparatorChar}\"") // OutputPath is legacy, per-project version of OutDir. We set both just in case. https://github.com/dotnet/msbuild/issues/87 .AppendArgument($"/p:OutputPath=\"{artifactsPaths.BinariesDirectoryPath}{Path.AltDirectorySeparatorChar}\"") diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliPublisher.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliPublisher.cs index e46864b98a..35f375cbd1 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliPublisher.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliPublisher.cs @@ -6,7 +6,7 @@ namespace BenchmarkDotNet.Toolchains.DotNetCli; -public class DotNetCliPublisher(string tfm, string? customDotNetCliPath = null, string? extraArguments = null, IReadOnlyList? environmentVariables = null) : IBuilder +public class DotNetCliPublisher(string tfm, string? customDotNetCliPath = null, string? extraArguments = null, IReadOnlyList? environmentVariables = null, bool logOutput = false) : IBuilder { public string TargetFrameworkMoniker { get; } = tfm; public string CustomDotNetCliPath { get; } = customDotNetCliPath; @@ -21,6 +21,7 @@ public virtual BuildResult Build(GenerateResult generateResult, BuildPartition b logger, buildPartition, environmentVariables, - buildPartition.Timeout + buildPartition.Timeout, + logOutput: logOutput ).RestoreThenBuildThenPublish(); } diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs index 60fe7722ee..4534670f50 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs @@ -56,7 +56,7 @@ private static ExecuteResult Execute(BenchmarkCase benchmarkCase, BenchmarkId be private static Process CreateProcess(BenchmarkCase benchmarkCase, ArtifactsPaths artifactsPaths, string args, IResolver resolver) { WasmRuntime runtime = (WasmRuntime)benchmarkCase.GetRuntime(); - string mainJs = runtime.RuntimeMoniker < RuntimeMoniker.WasmNet70 ? "main.js" : "benchmark-main.mjs"; + const string mainJs = "benchmark-main.mjs"; var start = new ProcessStartInfo { diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmToolchain.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmToolchain.cs index 71ad2ade2b..6a546714ce 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmToolchain.cs @@ -48,7 +48,7 @@ public static IToolchain From(NetCoreAppSettings netCoreAppSettings) netCoreAppSettings.PackagesPath, netCoreAppSettings.CustomRuntimePack, netCoreAppSettings.AOTCompilerMode == MonoAotLLVM.MonoAotCompilerMode.wasm), - new DotNetCliBuilder(netCoreAppSettings.TargetFrameworkMoniker, + new DotNetCliPublisher(netCoreAppSettings.TargetFrameworkMoniker, netCoreAppSettings.CustomDotNetCliPath, // aot builds can be very slow logOutput: netCoreAppSettings.AOTCompilerMode == MonoAotLLVM.MonoAotCompilerMode.wasm), From 8fce16d4bd57933ebf82f4362823ee5c1c04c1e6 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Mon, 16 Feb 2026 15:03:26 +0100 Subject: [PATCH 12/14] `PublishTrimmed` should be kept as it was. --- src/BenchmarkDotNet/Templates/WasmCsProj.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/BenchmarkDotNet/Templates/WasmCsProj.txt b/src/BenchmarkDotNet/Templates/WasmCsProj.txt index 6d0fc2117b..bf35251428 100644 --- a/src/BenchmarkDotNet/Templates/WasmCsProj.txt +++ b/src/BenchmarkDotNet/Templates/WasmCsProj.txt @@ -20,6 +20,7 @@ browser-wasm true $RUN_AOT$ + $(RunAOTCompilation) false false false From a3a92c3ce3fd803da2cd8a2a31cbd0df791f5fb6 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Mon, 16 Feb 2026 15:05:06 +0100 Subject: [PATCH 13/14] Comment could be clearer. --- src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs index cd8cf607cf..5619e9ce76 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs @@ -46,7 +46,7 @@ protected void GenerateProjectFile(BuildPartition buildPartition, ArtifactsPaths var xmlDoc = new XmlDocument(); xmlDoc.Load(projectFile.FullName); var (customProperties, _) = GetSettingsThatNeedToBeCopied(xmlDoc, projectFile); - // Always use Microsoft.NET.Sdk.WebAssembly for WASM projects. It auto-defaults UseMonoRuntime=true. + // Microsoft.NET.Sdk.WebAssembly auto-defaults UseMonoRuntime=true. string sdkName = runtime.IsMonoRuntime ? "Microsoft.NET.Sdk.WebAssembly" : "Microsoft.NET.Sdk"; string content = new StringBuilder(ResourceHelper.LoadTemplate("WasmCsProj.txt")) From 762a7927df48710003ea3ce7aa9f1c8af2f923c5 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Mon, 16 Feb 2026 17:04:13 +0100 Subject: [PATCH 14/14] ci: retrigger run-tests