diff --git a/BenchmarkDotNet.sln.DotSettings b/BenchmarkDotNet.sln.DotSettings index d2f6784607..fe4359db34 100644 --- a/BenchmarkDotNet.sln.DotSettings +++ b/BenchmarkDotNet.sln.DotSettings @@ -195,6 +195,7 @@ True True True + True True True True diff --git a/samples/BenchmarkDotNet.Samples/nuget.config b/samples/BenchmarkDotNet.Samples/nuget.config deleted file mode 100644 index df7045f7e2..0000000000 --- a/samples/BenchmarkDotNet.Samples/nuget.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/src/BenchmarkDotNet/ConsoleArguments/CommandLineOptions.cs b/src/BenchmarkDotNet/ConsoleArguments/CommandLineOptions.cs index ce1b3200b4..d3c50b4c93 100644 --- a/src/BenchmarkDotNet/ConsoleArguments/CommandLineOptions.cs +++ b/src/BenchmarkDotNet/ConsoleArguments/CommandLineOptions.cs @@ -168,13 +168,13 @@ public class CommandLineOptions [Option("envVars", Required = false, HelpText = "Colon separated environment variables (key:value)")] public IEnumerable EnvironmentVariables { get; set; } - [Option("wasmJavascriptEnginePath", Required = false, Default = "v8", HelpText = "Full path to a java script engine used to run the benchmarks, if using the WASM runtime.")] - public string WasmJavascriptEnginePath { get; set; } + [Option("wasmEngine", Required = false, HelpText = "Full path to a java script engine used to run the benchmarks, used by WASM toolchain.")] + public FileInfo WasmJavascriptEngine { get; set; } - [Option("wasmMainJS", Required = false, HelpText = "Path to the main.js file used for wasm apps.")] - public string WasmMainJS { get; set; } + [Option("wasmMainJS", Required = false, HelpText = "Path to the main.js file used by WASM toolchain. Mandatory when using \"--runtimes wasm\"")] + public FileInfo WasmMainJs { get; set; } - [Option("wasmJavaScriptEngineArguments", Required = false, Default = "--expose_wasm", HelpText = "Arguments for the javascript engine used by wasm.")] + [Option("wasmArgs", Required = false, Default = "--expose_wasm", HelpText = "Arguments for the javascript engine used by WASM toolchain.")] public string WasmJavaScriptEngineArguments { get; set; } internal bool UserProvidedFilters => Filters.Any() || AttributeNames.Any() || AllCategories.Any() || AnyCategories.Any(); diff --git a/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs b/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs index 506b1e644d..68a9a84294 100644 --- a/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs +++ b/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs @@ -105,11 +105,18 @@ private static bool Validate(CommandLineOptions options, ILogger logger) } foreach (string runtime in options.Runtimes) - if (!Enum.TryParse(runtime.Replace(".", string.Empty), ignoreCase: true, out _)) + { + if (!Enum.TryParse(runtime.Replace(".", string.Empty), ignoreCase: true, out var parsed)) { logger.WriteLineError($"The provided runtime \"{runtime}\" is invalid. Available options are: {string.Join(", ", Enum.GetNames(typeof(RuntimeMoniker)).Select(name => name.ToLower()))}."); return false; } + else if (parsed == RuntimeMoniker.Wasm && (options.WasmMainJs == null || options.WasmMainJs.IsNotNullButDoesNotExist())) + { + logger.WriteLineError($"The provided {nameof(options.WasmMainJs)} \"{options.WasmMainJs}\" does NOT exist. It MUST be provided."); + return false; + } + } foreach (string exporter in options.Exporters) if (!AvailableExporters.ContainsKey(exporter)) @@ -137,6 +144,12 @@ private static bool Validate(CommandLineOptions options, ILogger logger) return false; } + if (options.WasmJavascriptEngine.IsNotNullButDoesNotExist()) + { + logger.WriteLineError($"The provided {nameof(options.WasmJavascriptEngine)} \"{options.WasmJavascriptEngine}\" does NOT exist."); + return false; + } + if (options.CoreRtPath.IsNotNullButDoesNotExist()) { logger.WriteLineError($"The provided {nameof(options.CoreRtPath)} \"{options.CoreRtPath}\" does NOT exist."); @@ -361,18 +374,19 @@ private static Job CreateJobForGivenRuntime(Job baseJob, string runtimeId, Comma return baseJob.WithRuntime(runtime).WithToolchain(builder.ToToolchain()); case RuntimeMoniker.Wasm: - var wasmRuntime = runtimeMoniker.GetRuntime(); - - WasmSettings wasmSettings = new WasmSettings(wasmMainJS: options.WasmMainJS, - wasmJavaScriptEngine: options.WasmJavascriptEnginePath, - wasmjavaScriptEngineArguments: options.WasmJavaScriptEngineArguments); - - IToolchain toolChain = new WasmToolChain(name: "Wasm", - targetFrameworkMoniker: wasmRuntime.MsBuildMoniker, - cliPath: options.CliPath.FullName, - packagesPath: options.RestorePath?.FullName, - wasmSettings: wasmSettings, - timeout: timeOut ?? NetCoreAppSettings.DefaultBuildTimeout); + var wasmRuntime = new WasmRuntime( + mainJs: options.WasmMainJs, + msBuildMoniker: "net5.0", + javaScriptEngine: options.WasmJavascriptEngine?.FullName ?? "v8", + javaScriptEngineArguments: options.WasmJavaScriptEngineArguments); + + var toolChain = WasmToolChain.From(new NetCoreAppSettings( + targetFrameworkMoniker: wasmRuntime.MsBuildMoniker, + runtimeFrameworkVersion: null, + name: wasmRuntime.Name, + customDotNetCliPath: options.CliPath?.FullName, + packagesPath: options.RestorePath?.FullName, + timeout: timeOut ?? NetCoreAppSettings.DefaultBuildTimeout)); return baseJob.WithRuntime(wasmRuntime).WithToolchain(toolChain); default: diff --git a/src/BenchmarkDotNet/Environments/Runtimes/WasmRuntime.cs b/src/BenchmarkDotNet/Environments/Runtimes/WasmRuntime.cs index 43401e0220..f906726da9 100644 --- a/src/BenchmarkDotNet/Environments/Runtimes/WasmRuntime.cs +++ b/src/BenchmarkDotNet/Environments/Runtimes/WasmRuntime.cs @@ -1,22 +1,60 @@ using System; +using System.ComponentModel; +using System.IO; +using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Jobs; namespace BenchmarkDotNet.Environments { public class WasmRuntime : Runtime, IEquatable { - public static readonly WasmRuntime Default = new WasmRuntime("Wasm"); + [EditorBrowsable(EditorBrowsableState.Never)] + internal static readonly WasmRuntime Default = new WasmRuntime(); - public WasmRuntime(string name) : base(RuntimeMoniker.Wasm, "net5.0", name) + public FileInfo MainJs { get; } + + public string JavaScriptEngine { get; } + + public string JavaScriptEngineArguments { get; } + + /// + /// creates new instance of WasmRuntime + /// + /// MANDATORY path to the main.js file. + /// Full path to a java script engine used to run the benchmarks. "v8" by default + /// Arguments for the javascript engine. "--expose_wasm" by default + /// moniker, default: "net5.0" + /// default: "WASM" + /// path to mainJs MUST be provided + public WasmRuntime(FileInfo mainJs, string msBuildMoniker = "net5.0", string displayName = "WASM", string javaScriptEngine = "v8", string javaScriptEngineArguments = "--expose_wasm") : base(RuntimeMoniker.Wasm, msBuildMoniker, displayName) { + if (mainJs == null) + throw new ArgumentNullException(paramName: nameof(mainJs)); + if (mainJs.IsNotNullButDoesNotExist()) + throw new FileNotFoundException($"Provided {nameof(mainJs)} file: \"{mainJs.FullName}\" doest NOT exist"); + if (!string.IsNullOrEmpty(javaScriptEngine) && javaScriptEngine != "v8" && !File.Exists(javaScriptEngine)) + throw new FileNotFoundException($"Provided {nameof(javaScriptEngine)} file: \"{javaScriptEngine}\" doest NOT exist"); + + MainJs = mainJs; + JavaScriptEngine = javaScriptEngine; + JavaScriptEngineArguments = javaScriptEngineArguments; } - - public WasmRuntime(string name, string msBuildMoniker) : base(RuntimeMoniker.Wasm, msBuildMoniker, name) + + // this ctor exists only for the purpose of having .Default property that returns something consumable by RuntimeInformation.GetCurrentRuntime() + private WasmRuntime(string msBuildMoniker = "net5.0", string displayName = "WASM", string javaScriptEngine = "v8", string javaScriptEngineArguments = "--expose_wasm") : base(RuntimeMoniker.Wasm, msBuildMoniker, displayName) { + MainJs = new FileInfo("fake"); + JavaScriptEngine = javaScriptEngine; + JavaScriptEngineArguments = javaScriptEngineArguments; } - public override bool Equals(object obj) => obj is WasmRuntime other && Equals(other); + public override bool Equals(object obj) + => obj is WasmRuntime other && Equals(other); + + public bool Equals(WasmRuntime other) + => other != null && base.Equals(other) && other.MainJs == MainJs && other.JavaScriptEngine == JavaScriptEngine && other.JavaScriptEngineArguments == JavaScriptEngineArguments; - public bool Equals(WasmRuntime other) => base.Equals(other); + public override int GetHashCode() + => base.GetHashCode() ^ MainJs.GetHashCode() ^ (JavaScriptEngine?.GetHashCode() ?? 0) ^ (JavaScriptEngineArguments?.GetHashCode() ?? 0); } } diff --git a/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs b/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs index 81937ea8e7..2916b6ef3a 100644 --- a/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs +++ b/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs @@ -48,8 +48,6 @@ internal static Runtime GetRuntime(this RuntimeMoniker runtimeMoniker) return CoreRtRuntime.CoreRt31; case RuntimeMoniker.CoreRt50: return CoreRtRuntime.CoreRt50; - case RuntimeMoniker.Wasm: - return WasmRuntime.Default; default: throw new ArgumentOutOfRangeException(nameof(runtimeMoniker), runtimeMoniker, "Runtime Moniker not supported"); } diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliBuilder.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliBuilder.cs index 465955bd34..3af73e4e81 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliBuilder.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliBuilder.cs @@ -24,7 +24,7 @@ public DotNetCliBuilder(string targetFrameworkMoniker, string customDotNetCliPat Timeout = timeout ?? NetCoreAppSettings.DefaultBuildTimeout; } - public virtual BuildResult Build(GenerateResult generateResult, BuildPartition buildPartition, ILogger logger) + public BuildResult Build(GenerateResult generateResult, BuildPartition buildPartition, ILogger logger) => new DotNetCliCommand( CustomDotNetCliPath, string.Empty, diff --git a/src/BenchmarkDotNet/Toolchains/Executor.cs b/src/BenchmarkDotNet/Toolchains/Executor.cs index 5aaa89ef8c..34d2afc6c8 100644 --- a/src/BenchmarkDotNet/Toolchains/Executor.cs +++ b/src/BenchmarkDotNet/Toolchains/Executor.cs @@ -25,6 +25,7 @@ public class Executor : IExecutor public ExecuteResult Execute(ExecuteParameters executeParameters) { string exePath = executeParameters.BuildResult.ArtifactsPaths.ExecutablePath; + string programName = executeParameters.BuildResult.ArtifactsPaths.ProgramName; string args = executeParameters.BenchmarkId.ToArguments(); if (!File.Exists(exePath)) @@ -32,14 +33,14 @@ public ExecuteResult Execute(ExecuteParameters executeParameters) return new ExecuteResult(false, -1, default, Array.Empty(), Array.Empty()); } - return Execute(executeParameters.BenchmarkCase, executeParameters.BenchmarkId, executeParameters.Logger, exePath, null, args, executeParameters.Diagnoser, executeParameters.Resolver); + return Execute(executeParameters.BenchmarkCase, executeParameters.BenchmarkId, executeParameters.Logger, exePath, null, args, executeParameters.Diagnoser, executeParameters.Resolver, programName); } - private ExecuteResult Execute(BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, ILogger logger, string exePath, string workingDirectory, string args, IDiagnoser diagnoser, IResolver resolver) + private ExecuteResult Execute(BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, ILogger logger, string exePath, string workingDirectory, string args, IDiagnoser diagnoser, IResolver resolver, string programName) { try { - using (var process = new Process { StartInfo = CreateStartInfo(benchmarkCase, exePath, args, workingDirectory, resolver) }) + using (var process = new Process { StartInfo = CreateStartInfo(benchmarkCase, exePath, args, workingDirectory, resolver, programName) }) using (new ConsoleExitHandler(process, logger)) { var loggerWithDiagnoser = new SynchronousProcessOutputLoggerWithDiagnoser(logger, process, diagnoser, benchmarkCase, benchmarkId); @@ -82,7 +83,7 @@ private ExecuteResult Execute(Process process, BenchmarkCase benchmarkCase, Sync return new ExecuteResult(true, process.ExitCode, process.Id, Array.Empty(), Array.Empty()); } - private ProcessStartInfo CreateStartInfo(BenchmarkCase benchmarkCase, string exePath, string args, string workingDirectory, IResolver resolver) + private ProcessStartInfo CreateStartInfo(BenchmarkCase benchmarkCase, string exePath, string args, string workingDirectory, IResolver resolver, string programName) { var start = new ProcessStartInfo { @@ -113,6 +114,10 @@ private ProcessStartInfo CreateStartInfo(BenchmarkCase benchmarkCase, string exe start.FileName = mono.CustomPath ?? "mono"; start.Arguments = GetMonoArguments(benchmarkCase.Job, exePath, args, resolver); break; + case WasmRuntime wasm: + start.FileName = wasm.JavaScriptEngine; + start.Arguments = $"{wasm.JavaScriptEngineArguments} runtime.js -- --run {programName}.dll {args} "; + break; default: throw new NotSupportedException("Runtime = " + runtime); } diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmAppBuilder.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmAppBuilder.cs deleted file mode 100644 index 707b8a5bc9..0000000000 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmAppBuilder.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using System.Reflection; -using BenchmarkDotNet.Toolchains.MonoWasm; - -public class WasmAppBuilder -{ - private readonly WasmSettings WasmSettings; - private readonly string TargetFrameworkMoniker; - - public WasmAppBuilder(WasmSettings wasmSettings, string targetFrameworkMoniker) - { - WasmSettings = wasmSettings; - TargetFrameworkMoniker = targetFrameworkMoniker; - } - - public bool BuildApp (string programName, string projectRoot) - { - string[] assemblies; - string appDir = Path.Combine(projectRoot, $"bin", TargetFrameworkMoniker, "browser-wasm", "publish"); - string outputDir = Path.Combine(appDir, "output"); - - string mainAssemblyPath = Path.Combine(appDir, $"{programName}.dll"); - - if (!File.Exists(mainAssemblyPath)) - throw new ArgumentException($"File MainAssembly='{mainAssemblyPath}' doesn't exist."); - if (!File.Exists(WasmSettings.WasmMainJS)) - throw new ArgumentException($"File MainJS='{WasmSettings.WasmMainJS}' doesn't exist."); - - var paths = new List(); - assemblies = Directory.GetFiles(appDir, "*.dll"); - - // Create app - Directory.CreateDirectory(outputDir); - Directory.CreateDirectory(Path.Combine(outputDir, "managed")); - foreach (var assembly in assemblies) - File.Copy(assembly, Path.Combine(outputDir, "managed", Path.GetFileName(assembly)), true); - - foreach (var f in new string[] { "dotnet.wasm", "dotnet.js" }) - File.Copy(Path.Combine(appDir, f), Path.Combine(outputDir, f), true); - - File.Copy(WasmSettings.WasmMainJS, Path.Combine(outputDir, "runtime.js"), true); - - using (var sw = File.CreateText(Path.Combine(outputDir, "mono-config.js"))) - { - sw.WriteLine("config = {"); - sw.WriteLine("\tvfs_prefix: \"managed\","); - sw.WriteLine("\tdeploy_prefix: \"managed\","); - sw.WriteLine("\tenable_debugging: 0,"); - sw.WriteLine("\tassembly_list: ["); - foreach (var assembly in assemblies) - { - sw.Write("\t\t\"" + Path.GetFileName(assembly) + "\""); - sw.WriteLine(","); - } - sw.WriteLine ("\t],"); - sw.WriteLine("\tfiles_to_map: [],"); - - sw.WriteLine ("}"); - } - - return true; - } -} diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmBuilder.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmBuilder.cs index 5e17b9de0a..bd28ba7601 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmBuilder.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmBuilder.cs @@ -6,25 +6,74 @@ using BenchmarkDotNet.Toolchains.Results; using BenchmarkDotNet.Toolchains.DotNetCli; using System.IO; +using BenchmarkDotNet.Environments; namespace BenchmarkDotNet.Toolchains.MonoWasm { - public class WasmBuilder : DotNetCliBuilder + public class WasmBuilder : IBuilder { - private WasmAppBuilder WasmAppBuilder; + private readonly DotNetCliBuilder dotNetCliBuilder; + private readonly string targetFrameworkMoniker; - public WasmBuilder(string targetFrameworkMoniker, WasmSettings wasmSettings, string customDotNetCliPath = null, TimeSpan? timeout = null) - :base (targetFrameworkMoniker, customDotNetCliPath, timeout) + public WasmBuilder(string targetFrameworkMoniker, string customDotNetCliPath = null, TimeSpan? timeout = null) { - WasmAppBuilder = new WasmAppBuilder(wasmSettings, targetFrameworkMoniker); + this.targetFrameworkMoniker = targetFrameworkMoniker; + + dotNetCliBuilder = new DotNetCliBuilder(targetFrameworkMoniker, customDotNetCliPath, timeout); } - public override BuildResult Build(GenerateResult generateResult, BuildPartition buildPartition, ILogger logger) + public BuildResult Build(GenerateResult generateResult, BuildPartition buildPartition, ILogger logger) { - BuildResult buildResult = base.Build(generateResult, buildPartition, logger); - WasmAppBuilder.BuildApp(buildPartition.ProgramName, generateResult.ArtifactsPaths.BuildArtifactsDirectoryPath); + BuildResult buildResult = dotNetCliBuilder.Build(generateResult, buildPartition, logger); + + if (buildResult.IsBuildSuccess) + { + BuildApp(buildPartition.ProgramName, generateResult.ArtifactsPaths.BuildArtifactsDirectoryPath, (WasmRuntime)buildPartition.Runtime); + } return buildResult; } + + private void BuildApp(string programName, string projectRoot, WasmRuntime runtime) + { + string appDir = Path.Combine(projectRoot, $"bin", targetFrameworkMoniker, "browser-wasm", "publish"); + string outputDir = Path.Combine(appDir, "output"); + + string mainAssemblyPath = Path.Combine(appDir, $"{programName}.dll"); + + if (!File.Exists(mainAssemblyPath)) + throw new ArgumentException($"File MainAssembly='{mainAssemblyPath}' doesn't exist."); + + var assemblies = Directory.GetFiles(appDir, "*.dll"); + + // Create app + Directory.CreateDirectory(outputDir); + Directory.CreateDirectory(Path.Combine(outputDir, "managed")); + foreach (var assembly in assemblies) + File.Copy(assembly, Path.Combine(outputDir, "managed", Path.GetFileName(assembly)), true); + + foreach (var f in new string[] { "dotnet.wasm", "dotnet.js" }) + File.Copy(Path.Combine(appDir, f), Path.Combine(outputDir, f), true); + + File.Copy(runtime.MainJs.FullName, Path.Combine(outputDir, "runtime.js"), true); + + using (var sw = File.CreateText(Path.Combine(outputDir, "mono-config.js"))) + { + sw.WriteLine("config = {"); + sw.WriteLine("\tvfs_prefix: \"managed\","); + sw.WriteLine("\tdeploy_prefix: \"managed\","); + sw.WriteLine("\tenable_debugging: 0,"); + sw.WriteLine("\tassembly_list: ["); + foreach (var assembly in assemblies) + { + sw.Write("\t\t\"" + Path.GetFileName(assembly) + "\""); + sw.WriteLine(","); + } + sw.WriteLine ("\t],"); + sw.WriteLine("\tfiles_to_map: [],"); + + sw.WriteLine ("}"); + } + } } } diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs deleted file mode 100644 index ea9e941f8e..0000000000 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs +++ /dev/null @@ -1,166 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using BenchmarkDotNet.Characteristics; -using BenchmarkDotNet.Diagnosers; -using BenchmarkDotNet.Engines; -using BenchmarkDotNet.Extensions; -using BenchmarkDotNet.Helpers; -using BenchmarkDotNet.Jobs; -using BenchmarkDotNet.Loggers; -using BenchmarkDotNet.Running; -using BenchmarkDotNet.Toolchains.Parameters; -using BenchmarkDotNet.Toolchains.Results; -using JetBrains.Annotations; - -namespace BenchmarkDotNet.Toolchains.MonoWasm -{ - [PublicAPI] - public class WasmExecutor : IExecutor - { - public WasmExecutor(string customDotNetCliPath, string javaScriptEngine, string javaScriptEngineArguments) - { - CustomDotNetCliPath = customDotNetCliPath; - JavaScriptEngine = javaScriptEngine; - JavaScriptEngineArguments = javaScriptEngineArguments; - RuntimeJavaScriptName = "runtime.js"; - ExtraRuntimeArguments = ""; - } - - private string CustomDotNetCliPath { get; } - - private string JavaScriptEngine { get; set; } - - private string JavaScriptEngineArguments { get; set; } - - private string RuntimeJavaScriptName { get; set; } - - private string ExtraRuntimeArguments { get; set; } - - public ExecuteResult Execute(ExecuteParameters executeParameters) - { - if (!File.Exists(executeParameters.BuildResult.ArtifactsPaths.ExecutablePath)) - { - executeParameters.Logger.WriteLineError($"Did not find {executeParameters.BuildResult.ArtifactsPaths.ExecutablePath}, but the folder contained:"); - foreach (var file in new DirectoryInfo(executeParameters.BuildResult.ArtifactsPaths.BinariesDirectoryPath).GetFiles("*.*")) - executeParameters.Logger.WriteLineError(file.Name); - - return new ExecuteResult(false, -1, default, Array.Empty(), Array.Empty()); - } - - try - { - return Execute( - executeParameters.BenchmarkCase, - executeParameters.BenchmarkId, - executeParameters.Logger, - executeParameters.BuildResult.ArtifactsPaths, - executeParameters.Diagnoser, - Path.GetFileName(executeParameters.BuildResult.ArtifactsPaths.ExecutablePath), - executeParameters.Resolver); - } - finally - { - executeParameters.Diagnoser?.Handle( - HostSignal.AfterProcessExit, - new DiagnoserActionParameters(null, executeParameters.BenchmarkCase, executeParameters.BenchmarkId)); - } - } - - private ExecuteResult Execute(BenchmarkCase benchmarkCase, - BenchmarkId benchmarkId, - ILogger logger, - ArtifactsPaths artifactsPaths, - IDiagnoser diagnoser, - string executableName, - IResolver resolver) - { - ProcessStartInfo startInfo = WasmExecutor.BuildStartInfo( - customDotNetCliPath: CustomDotNetCliPath, - workingDirectory: artifactsPaths.BinariesDirectoryPath, - programName: artifactsPaths.ProgramName, - benchmarkId: benchmarkId.ToArguments(), - javaScriptEngine: JavaScriptEngine, - runtimeJavaScriptName: RuntimeJavaScriptName, - javaScriptEngineArguments: JavaScriptEngineArguments, - extraRuntimeArguments: ExtraRuntimeArguments, - redirectStandardInput: true); - - - startInfo.SetEnvironmentVariables(benchmarkCase, resolver); - - using (var process = new Process { StartInfo = startInfo }) - using (new ConsoleExitHandler(process, logger)) - { - var loggerWithDiagnoser = new SynchronousProcessOutputLoggerWithDiagnoser(logger, process, diagnoser, benchmarkCase, benchmarkId); - - logger.WriteLineInfo($"// Execute: {process.StartInfo.FileName} {process.StartInfo.Arguments} in {process.StartInfo.WorkingDirectory}"); - - diagnoser?.Handle(HostSignal.BeforeProcessStart, new DiagnoserActionParameters(process, benchmarkCase, benchmarkId)); - - process.Start(); - - process.EnsureHighPriority(logger); - if (benchmarkCase.Job.Environment.HasValue(EnvironmentMode.AffinityCharacteristic)) - { - process.TrySetAffinity(benchmarkCase.Job.Environment.Affinity, logger); - } - - loggerWithDiagnoser.ProcessInput(); - string standardError = process.StandardError.ReadToEnd(); - - process.WaitForExit(); // should we add timeout here? - - if (process.ExitCode == 0) - { - return new ExecuteResult(true, process.ExitCode, process.Id, loggerWithDiagnoser.LinesWithResults, loggerWithDiagnoser.LinesWithExtraOutput); - } - - if (!string.IsNullOrEmpty(standardError)) - { - logger.WriteError(standardError); - } - - return new ExecuteResult(true, process.ExitCode, process.Id, Array.Empty(), Array.Empty()); - } - } - - - internal static ProcessStartInfo BuildStartInfo(string customDotNetCliPath, - string workingDirectory, - string programName, - string benchmarkId, - string javaScriptEngine, - string runtimeJavaScriptName, - string javaScriptEngineArguments, - string extraRuntimeArguments, - IReadOnlyList environmentVariables = null, - bool redirectStandardInput = false) - { - const string dotnetMultiLevelLookupEnvVarName = "DOTNET_MULTILEVEL_LOOKUP"; - - ProcessStartInfo startInfo = new ProcessStartInfo - { - FileName = javaScriptEngine, - WorkingDirectory = workingDirectory, - Arguments = $"{javaScriptEngineArguments} {runtimeJavaScriptName} -- {extraRuntimeArguments} --run {programName}.dll {benchmarkId} ", - UseShellExecute = false, - CreateNoWindow = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - RedirectStandardInput = redirectStandardInput - }; - - if (environmentVariables != null) - foreach (var environmentVariable in environmentVariables) - startInfo.EnvironmentVariables[environmentVariable.Key] = environmentVariable.Value; - - if (!string.IsNullOrEmpty(customDotNetCliPath) && (environmentVariables == null || environmentVariables.All(envVar => envVar.Key != dotnetMultiLevelLookupEnvVarName))) - startInfo.EnvironmentVariables[dotnetMultiLevelLookupEnvVarName] = "0"; - - return startInfo; - } - } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs index b92b403aeb..7cddc3e567 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs @@ -1,35 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; +using System.IO; using System.Text; using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Loggers; -using BenchmarkDotNet.Portability; using BenchmarkDotNet.Running; using BenchmarkDotNet.Toolchains.CsProj; -using BenchmarkDotNet.Toolchains.DotNetCli; namespace BenchmarkDotNet.Toolchains.MonoWasm { public class WasmGenerator : CsProjGenerator { - private string MainJS; - - public WasmGenerator(string targetFrameworkMoniker, - string cliPath, - string packagesPath, - string mainJS - ) - : base(targetFrameworkMoniker, - cliPath, - packagesPath, - null) - { - MainJS = mainJS; - } + public WasmGenerator(string targetFrameworkMoniker, string cliPath, string packagesPath) + : base(targetFrameworkMoniker, cliPath, packagesPath, runtimeFrameworkVersion: null) { } protected override void GenerateProject(BuildPartition buildPartition, ArtifactsPaths artifactsPaths, ILogger logger) { @@ -40,23 +22,23 @@ protected override void GenerateProject(BuildPartition buildPartition, Artifacts var (customProperties, sdkName) = GetSettingsThatNeedsToBeCopied(file, projectFile); string content = new StringBuilder(ResourceHelper.LoadTemplate("WasmCsProj.txt")) - .Replace("$PLATFORM$", buildPartition.Platform.ToConfig()) - .Replace("$CODEFILENAME$", Path.GetFileName(artifactsPaths.ProgramCodePath)) - .Replace("$CSPROJPATH$", projectFile.FullName) - .Replace("$TFM$", TargetFrameworkMoniker) - .Replace("$PROGRAMNAME$", artifactsPaths.ProgramName) - .Replace("$COPIEDSETTINGS$", customProperties) - .Replace("$CONFIGURATIONNAME$", buildPartition.BuildConfiguration) - .Replace("$SDKNAME$", sdkName) - .ToString(); + .Replace("$PLATFORM$", buildPartition.Platform.ToConfig()) + .Replace("$CODEFILENAME$", Path.GetFileName(artifactsPaths.ProgramCodePath)) + .Replace("$CSPROJPATH$", projectFile.FullName) + .Replace("$TFM$", TargetFrameworkMoniker) + .Replace("$PROGRAMNAME$", artifactsPaths.ProgramName) + .Replace("$COPIEDSETTINGS$", customProperties) + .Replace("$CONFIGURATIONNAME$", buildPartition.BuildConfiguration) + .Replace("$SDKNAME$", sdkName) + .ToString(); File.WriteAllText(artifactsPaths.ProjectFilePath, content); } } - protected override string GetExecutablePath(string binariesDirectoryPath, string programName) => Path.Combine(binariesDirectoryPath, "runtime.js"); + protected override string GetExecutablePath(string binariesDirectoryPath, string programName) => Path.Combine(binariesDirectoryPath, "runtime.js"); protected override string GetBinariesDirectoryPath(string buildArtifactsDirectoryPath, string configuration) => Path.Combine(buildArtifactsDirectoryPath, "bin", TargetFrameworkMoniker, "browser-wasm", "publish", "output"); } -} +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmSettings.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmSettings.cs deleted file mode 100644 index b58e5f93ac..0000000000 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmSettings.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -namespace BenchmarkDotNet.Toolchains.MonoWasm -{ - public class WasmSettings - { - public string WasmMainJS { get; } - - public string WasmJavaScriptEngine { get; } - - public string WasmJavaScriptEngineArguments { get; } - - public WasmSettings(string wasmMainJS, string wasmJavaScriptEngine, string wasmjavaScriptEngineArguments) - { - WasmMainJS = wasmMainJS; - WasmJavaScriptEngine = wasmJavaScriptEngine; - WasmJavaScriptEngineArguments = wasmjavaScriptEngineArguments; - } - } -} diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmToolChain.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmToolChain.cs index 78f8101da4..3ec306ebe3 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmToolChain.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmToolChain.cs @@ -1,58 +1,49 @@ -using System; -using System.Runtime.InteropServices; -using BenchmarkDotNet.Toolchains.DotNetCli; +using BenchmarkDotNet.Toolchains.DotNetCli; using JetBrains.Annotations; using BenchmarkDotNet.Characteristics; using BenchmarkDotNet.Running; using BenchmarkDotNet.Loggers; +using BenchmarkDotNet.Portability; namespace BenchmarkDotNet.Toolchains.MonoWasm { [PublicAPI] public class WasmToolChain : Toolchain { - [PublicAPI] public static readonly IToolchain NetCoreApp50Wasm = From(NetCoreAppSettings.NetCoreApp50, new WasmSettings(null, null, null)); + private string CustomDotNetCliPath { get; } - private WasmToolChain(string name, IGenerator generator, IBuilder builder, IExecutor executor, string runtimeJavaScriptPath) + private WasmToolChain(string name, IGenerator generator, IBuilder builder, IExecutor executor, string customDotNetCliPath) : base(name, generator, builder, executor) { - } - - public WasmToolChain(string name, - string targetFrameworkMoniker, - string cliPath, - string packagesPath, - WasmSettings wasmSettings, - TimeSpan timeout) - : base(name, - new WasmGenerator(targetFrameworkMoniker, - cliPath, - packagesPath, - wasmSettings.WasmMainJS), - new WasmBuilder(targetFrameworkMoniker, wasmSettings, cliPath, timeout), - new WasmExecutor(wasmSettings.WasmMainJS, wasmSettings.WasmJavaScriptEngine, wasmSettings.WasmJavaScriptEngineArguments) - ) - { + CustomDotNetCliPath = customDotNetCliPath; } public override bool IsSupported(BenchmarkCase benchmarkCase, ILogger logger, IResolver resolver) { - return !System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + if (!base.IsSupported(benchmarkCase, logger, resolver)) + return false; + + if (InvalidCliPath(CustomDotNetCliPath, benchmarkCase, logger)) + return false; + + if (RuntimeInformation.IsWindows()) + { + logger.WriteLineError($"{nameof(WasmToolChain)} is supported only on Unix, benchmark '{benchmarkCase.DisplayInfo}' will not be executed"); + return false; + } + return true; } [PublicAPI] - public static IToolchain From(NetCoreAppSettings netCoreAppSettings, WasmSettings wasmSettings) - { - return new WasmToolChain(netCoreAppSettings.Name, - new WasmGenerator(netCoreAppSettings.TargetFrameworkMoniker, - netCoreAppSettings.CustomDotNetCliPath, - netCoreAppSettings.PackagesPath, - wasmSettings.WasmMainJS), - new DotNetCliBuilder(netCoreAppSettings.TargetFrameworkMoniker, - netCoreAppSettings.CustomDotNetCliPath, - netCoreAppSettings.Timeout), - new WasmExecutor(wasmSettings.WasmMainJS, wasmSettings.WasmJavaScriptEngine, wasmSettings.WasmJavaScriptEngineArguments), - netCoreAppSettings.CustomDotNetCliPath); - } + public static IToolchain From(NetCoreAppSettings netCoreAppSettings) + => new WasmToolChain(netCoreAppSettings.Name, + new WasmGenerator(netCoreAppSettings.TargetFrameworkMoniker, + netCoreAppSettings.CustomDotNetCliPath, + netCoreAppSettings.PackagesPath), + new WasmBuilder(netCoreAppSettings.TargetFrameworkMoniker, + netCoreAppSettings.CustomDotNetCliPath, + netCoreAppSettings.Timeout), + new Executor(), + netCoreAppSettings.CustomDotNetCliPath); } } diff --git a/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs b/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs index a6fdf7f237..591af97b0e 100644 --- a/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs +++ b/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs @@ -6,6 +6,7 @@ using BenchmarkDotNet.Running; using BenchmarkDotNet.Toolchains.CoreRt; using BenchmarkDotNet.Toolchains.CsProj; +using BenchmarkDotNet.Toolchains.DotNetCli; using BenchmarkDotNet.Toolchains.InProcess; using BenchmarkDotNet.Toolchains.InProcess.Emit; using BenchmarkDotNet.Toolchains.InProcess.NoEmit; @@ -65,7 +66,7 @@ internal static IToolchain GetToolchain(this Runtime runtime, Descriptor descrip : CoreRtToolchain.CreateBuilder().UseCoreRtNuGet().TargetFrameworkMoniker(coreRtRuntime.MsBuildMoniker).ToToolchain(); case WasmRuntime wasmRuntime: - return WasmToolChain.NetCoreApp50Wasm; + return WasmToolChain.From(new NetCoreAppSettings(targetFrameworkMoniker: wasmRuntime.MsBuildMoniker, name: wasmRuntime.Name, runtimeFrameworkVersion: null)); default: throw new ArgumentOutOfRangeException(nameof(runtime), runtime, "Runtime not supported");