diff --git a/src/BenchmarkDotNet/Diagnosers/PerfCollectProfiler.cs b/src/BenchmarkDotNet/Diagnosers/PerfCollectProfiler.cs index 9a3d348665..cb745092ba 100644 --- a/src/BenchmarkDotNet/Diagnosers/PerfCollectProfiler.cs +++ b/src/BenchmarkDotNet/Diagnosers/PerfCollectProfiler.cs @@ -230,11 +230,13 @@ private void EnsureSymbolsForNativeRuntime(DiagnoserActionParameters parameters) string toolPath = Path.Combine(Path.GetTempPath(), "BenchmarkDotNet", "symbols"); DotNetCliCommand cliCommand = new( cliPath: cliPath, + filePath: string.Empty, + tfm: string.Empty, arguments: $"tool install dotnet-symbol --tool-path \"{toolPath}\"", generateResult: null, logger: logger, buildPartition: null, - environmentVariables: Array.Empty(), + environmentVariables: [], timeout: TimeSpan.FromMinutes(3), logOutput: true); // the following commands might take a while and fail, let's log them diff --git a/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs b/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs index 4e30d96ca3..ed6ce08bba 100644 --- a/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs +++ b/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs @@ -1,9 +1,11 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Versioning; +using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Portability; @@ -47,6 +49,13 @@ public static CoreRuntime CreateForNewVersion(string msBuildMoniker, string disp return new CoreRuntime(RuntimeMoniker.NotRecognized, msBuildMoniker, displayName); } + internal static CoreRuntime GetTargetOrCurrentVersion(Assembly? assembly) + // Try to determine the version that the assembly was compiled for. + => FrameworkVersionHelper.GetTargetCoreVersion(assembly) is { } version + ? FromVersion(version, assembly) + // Fallback to the current running version. + : GetCurrentVersion(); + internal static CoreRuntime GetCurrentVersion() { if (!RuntimeInformation.IsNetCore) @@ -59,28 +68,24 @@ internal static CoreRuntime GetCurrentVersion() throw new NotSupportedException("Unable to recognize .NET Core version, please report a bug at https://github.com/dotnet/BenchmarkDotNet"); } - return FromVersion(version); + return FromVersion(version, null); } - internal static CoreRuntime FromVersion(Version version) + internal static CoreRuntime FromVersion(Version version, Assembly? assembly = null) => version switch { - switch (version) - { - case Version v when v.Major == 2 && v.Minor == 0: return Core20; - case Version v when v.Major == 2 && v.Minor == 1: return Core21; - case Version v when v.Major == 2 && v.Minor == 2: return Core22; - case Version v when v.Major == 3 && v.Minor == 0: return Core30; - case Version v when v.Major == 3 && v.Minor == 1: return Core31; - case Version v when v.Major == 5 && v.Minor == 0: return GetPlatformSpecific(Core50); - case Version v when v.Major == 6 && v.Minor == 0: return GetPlatformSpecific(Core60); - case Version v when v.Major == 7 && v.Minor == 0: return GetPlatformSpecific(Core70); - case Version v when v.Major == 8 && v.Minor == 0: return GetPlatformSpecific(Core80); - case Version v when v.Major == 9 && v.Minor == 0: return GetPlatformSpecific(Core90); - case Version v when v.Major == 10 && v.Minor == 0: return GetPlatformSpecific(Core10_0); - default: - return CreateForNewVersion($"net{version.Major}.{version.Minor}", $".NET {version.Major}.{version.Minor}"); - } - } + { Major: 2, Minor: 0 } => Core20, + { Major: 2, Minor: 1 } => Core21, + { Major: 2, Minor: 2 } => Core22, + { Major: 3, Minor: 0 } => Core30, + { Major: 3, Minor: 1 } => Core31, + { Major: 5 } => GetPlatformSpecific(Core50, assembly), + { Major: 6 } => GetPlatformSpecific(Core60, assembly), + { Major: 7 } => GetPlatformSpecific(Core70, assembly), + { Major: 8 } => GetPlatformSpecific(Core80, assembly), + { Major: 9 } => GetPlatformSpecific(Core90, assembly), + { Major: 10 } => GetPlatformSpecific(Core10_0, assembly), + _ => CreateForNewVersion($"net{version.Major}.{version.Minor}", $".NET {version.Major}.{version.Minor}"), + }; internal static bool TryGetVersion(out Version? version) { @@ -220,29 +225,36 @@ internal static bool TryGetVersionFromFrameworkName(string frameworkName, out Ve // Version.TryParse does not handle thing like 3.0.0-WORD internal static string GetParsableVersionPart(string fullVersionName) => new string(fullVersionName.TakeWhile(c => char.IsDigit(c) || c == '.').ToArray()); - private static CoreRuntime GetPlatformSpecific(CoreRuntime fallback) + private static CoreRuntime GetPlatformSpecific(CoreRuntime fallback, Assembly? assembly) + => TryGetTargetPlatform(assembly ?? Assembly.GetEntryAssembly(), out var platform) + ? new CoreRuntime(fallback.RuntimeMoniker, $"{fallback.MsBuildMoniker}-{platform}", fallback.Name) + : fallback; + + internal static bool TryGetTargetPlatform(Assembly? assembly, [NotNullWhen(true)] out string? platform) { - // TargetPlatformAttribute is not part of .NET Standard 2.0 so as usuall we have to use some reflection hacks... + platform = null; + + if (assembly is null) + return false; + + // TargetPlatformAttribute is not part of .NET Standard 2.0 so as usual we have to use some reflection hacks. var targetPlatformAttributeType = typeof(object).Assembly.GetType("System.Runtime.Versioning.TargetPlatformAttribute", throwOnError: false); if (targetPlatformAttributeType is null) // an old preview version of .NET 5 - return fallback; - - var exe = Assembly.GetEntryAssembly(); - if (exe is null) - return fallback; + return false; - var attributeInstance = exe.GetCustomAttribute(targetPlatformAttributeType); + var attributeInstance = assembly.GetCustomAttribute(targetPlatformAttributeType); if (attributeInstance is null) - return fallback; + return false; var platformNameProperty = targetPlatformAttributeType.GetProperty("PlatformName"); if (platformNameProperty is null) - return fallback; + return false; - if (!(platformNameProperty.GetValue(attributeInstance) is string platformName)) - return fallback; + if (platformNameProperty.GetValue(attributeInstance) is not string platformName) + return false; - return new CoreRuntime(fallback.RuntimeMoniker, $"{fallback.MsBuildMoniker}-{platformName}", fallback.Name); + platform = platformName; + return true; } } } diff --git a/src/BenchmarkDotNet/Helpers/FrameworkVersionHelper.cs b/src/BenchmarkDotNet/Helpers/FrameworkVersionHelper.cs index 09558d4d74..0205c857ad 100644 --- a/src/BenchmarkDotNet/Helpers/FrameworkVersionHelper.cs +++ b/src/BenchmarkDotNet/Helpers/FrameworkVersionHelper.cs @@ -1,8 +1,10 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Versioning; +using BenchmarkDotNet.Environments; using Microsoft.Win32; namespace BenchmarkDotNet.Helpers @@ -23,28 +25,38 @@ private static readonly (int minReleaseNumber, string version)[] FrameworkVersio ]; internal static string? GetTargetFrameworkVersion(Assembly? assembly) - { - if (assembly is null) + // Look for a TargetFrameworkAttribute with a supported Framework version. + => assembly?.GetCustomAttribute()?.FrameworkName switch { - return null; - } + ".NETFramework,Version=v4.6.1" => "4.6.1", + ".NETFramework,Version=v4.6.2" => "4.6.2", + ".NETFramework,Version=v4.7" => "4.7", + ".NETFramework,Version=v4.7.1" => "4.7.1", + ".NETFramework,Version=v4.7.2" => "4.7.2", + ".NETFramework,Version=v4.8" => "4.8", + ".NETFramework,Version=v4.8.1" => "4.8.1", + // Null assembly, or TargetFrameworkAttribute not found, or the assembly targeted a version older than we support, + // or the assembly targeted a non-framework tfm (like netstandard2.0). + _ => null, + }; + + internal static Version? GetTargetCoreVersion(Assembly? assembly) + { + //.NETCoreApp,Version=vX.Y + const string FrameworkPrefix = ".NETCoreApp,Version=v"; // Look for a TargetFrameworkAttribute with a supported Framework version. - foreach (var attribute in assembly.GetCustomAttributes()) + string? framework = assembly?.GetCustomAttribute()?.FrameworkName; + if (framework?.StartsWith(FrameworkPrefix) == true + && Version.TryParse(framework[FrameworkPrefix.Length..], out var version) + // We don't support netcoreapp1.X + && version.Major >= 2) { - switch (attribute.FrameworkName) - { - case ".NETFramework,Version=v4.6.1": return "4.6.1"; - case ".NETFramework,Version=v4.6.2": return "4.6.2"; - case ".NETFramework,Version=v4.7": return "4.7"; - case ".NETFramework,Version=v4.7.1": return "4.7.1"; - case ".NETFramework,Version=v4.7.2": return "4.7.2"; - case ".NETFramework,Version=v4.8": return "4.8"; - case ".NETFramework,Version=v4.8.1": return "4.8.1"; - } + return version; } - // TargetFrameworkAttribute not found, or the assembly targeted a version older than we support. + // Null assembly, or TargetFrameworkAttribute not found, or the assembly targeted a version older than we support, + // or the assembly targeted a non-core tfm (like netstandard2.0). return null; } @@ -112,5 +124,44 @@ private static bool IsDeveloperPackInstalled(string version) => Directory.Exists Environment.Is64BitOperatingSystem ? Environment.SpecialFolder.ProgramFilesX86 : Environment.SpecialFolder.ProgramFiles); + + internal static string? GetTfm(Assembly assembly) + { + // We don't support exotic frameworks like Silverlight, WindowsPhone, Xamarin.Mac, etc. + const string CorePrefix = ".NETCoreApp,Version=v"; + const string FrameworkPrefix = ".NETFramework,Version=v"; + const string StandardPrefix = ".NETStandard,Version=v"; + + // Look for a TargetFrameworkAttribute with a supported Framework version. + string? framework = assembly.GetCustomAttribute()?.FrameworkName; + if (TryParseVersion(CorePrefix, out var version)) + { + return version.Major < 5 + ? $"netcoreapp{version.Major}.{version.Minor}" + : CoreRuntime.TryGetTargetPlatform(assembly, out var platform) + ? $"net{version.Major}.{version.Minor}-{platform}" + : $"net{version.Major}.{version.Minor}"; + } + if (TryParseVersion(FrameworkPrefix, out version)) + { + return version.Build > 0 + ? $"net{version.Major}{version.Minor}{version.Build}" + : $"net{version.Major}{version.Minor}"; + } + if (!TryParseVersion(StandardPrefix, out version)) + { + return $"netstandard{version.Major}.{version.Minor}"; + } + + // TargetFrameworkAttribute not found, or the assembly targeted a framework we don't support. + return null; + + bool TryParseVersion(string prefix, [NotNullWhen(true)] out Version? version) + { + version = null; + return framework?.StartsWith(prefix) == true + && Version.TryParse(framework[prefix.Length..], out version); + } + } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/RuntimeInformation.cs b/src/BenchmarkDotNet/Portability/RuntimeInformation.cs index 4e9e3f9dfb..cd9c894a5d 100644 --- a/src/BenchmarkDotNet/Portability/RuntimeInformation.cs +++ b/src/BenchmarkDotNet/Portability/RuntimeInformation.cs @@ -171,9 +171,18 @@ string GetDetailedVersion() } internal static Runtime GetTargetOrCurrentRuntime(Assembly? assembly) - => !IsMono && !IsWasm && IsFullFramework // Match order of checks in GetCurrentRuntime(). - ? ClrRuntime.GetTargetOrCurrentVersion(assembly) - : GetCurrentRuntime(); + { + // Match order of checks in GetCurrentRuntime(). + if (!IsMono && !IsWasm) + { + if (IsFullFramework) + return ClrRuntime.GetTargetOrCurrentVersion(assembly); + // 99% of the time the core runtime is the same as the target framework, but the runtime could roll forward if it's not self-contained. + if (IsNetCore) + return CoreRuntime.GetTargetOrCurrentVersion(assembly); + } + return GetCurrentRuntime(); + } internal static Runtime GetCurrentRuntime() { diff --git a/src/BenchmarkDotNet/Templates/CsProj.txt b/src/BenchmarkDotNet/Templates/CsProj.txt index 99e536b985..d7ae3cae05 100644 --- a/src/BenchmarkDotNet/Templates/CsProj.txt +++ b/src/BenchmarkDotNet/Templates/CsProj.txt @@ -4,7 +4,7 @@ false false $PROGRAMNAME$ - $TFM$ + $TFM$ true $PLATFORM$ $PROGRAMNAME$ diff --git a/src/BenchmarkDotNet/Templates/MonoAOTLLVMCsProj.txt b/src/BenchmarkDotNet/Templates/MonoAOTLLVMCsProj.txt index adcb87389e..e87eee2527 100644 --- a/src/BenchmarkDotNet/Templates/MonoAOTLLVMCsProj.txt +++ b/src/BenchmarkDotNet/Templates/MonoAOTLLVMCsProj.txt @@ -9,7 +9,7 @@ Exe - $TFM$ + $TFM$ $RUNTIMEPACK$ $RUNTIMEIDENTIFIER$ false diff --git a/src/BenchmarkDotNet/Templates/WasmCsProj.txt b/src/BenchmarkDotNet/Templates/WasmCsProj.txt index 3db39f5669..3af58fc514 100644 --- a/src/BenchmarkDotNet/Templates/WasmCsProj.txt +++ b/src/BenchmarkDotNet/Templates/WasmCsProj.txt @@ -13,7 +13,7 @@ Exe Release false - $TFM$ + $TFM$ true $(PublishDir) $PROGRAMNAME$ diff --git a/src/BenchmarkDotNet/Toolchains/ArtifactsPaths.cs b/src/BenchmarkDotNet/Toolchains/ArtifactsPaths.cs index a9467494fa..033a531f5a 100644 --- a/src/BenchmarkDotNet/Toolchains/ArtifactsPaths.cs +++ b/src/BenchmarkDotNet/Toolchains/ArtifactsPaths.cs @@ -4,7 +4,7 @@ namespace BenchmarkDotNet.Toolchains { public class ArtifactsPaths { - public static readonly ArtifactsPaths Empty = new ArtifactsPaths("", "", "", "", "", "", "", "", "", "", "", ""); + public static readonly ArtifactsPaths Empty = new("", "", "", "", "", "", "", "", "", "", "", ""); [PublicAPI] public string RootArtifactsFolderPath { get; } [PublicAPI] public string BuildArtifactsDirectoryPath { get; } diff --git a/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunPublisher.cs b/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunPublisher.cs index c77b56b039..930307066b 100644 --- a/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunPublisher.cs +++ b/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunPublisher.cs @@ -9,21 +9,11 @@ namespace BenchmarkDotNet.Toolchains.CoreRun { - public class CoreRunPublisher : IBuilder + public class CoreRunPublisher(string tfm, FileInfo coreRun, FileInfo? customDotNetCliPath = null) : DotNetCliPublisher(tfm, customDotNetCliPath?.FullName) { - public CoreRunPublisher(FileInfo coreRun, FileInfo? customDotNetCliPath = null) + public override BuildResult Build(GenerateResult generateResult, BuildPartition buildPartition, ILogger logger) { - CoreRun = coreRun; - DotNetCliPublisher = new DotNetCliPublisher(customDotNetCliPath?.FullName); - } - - private FileInfo CoreRun { get; } - - private DotNetCliPublisher DotNetCliPublisher { get; } - - public BuildResult Build(GenerateResult generateResult, BuildPartition buildPartition, ILogger logger) - { - var buildResult = DotNetCliPublisher.Build(generateResult, buildPartition, logger); + var buildResult = base.Build(generateResult, buildPartition, logger); if (buildResult.IsBuildSuccess) UpdateDuplicatedDependencies(buildResult.ArtifactsPaths, logger); @@ -37,7 +27,7 @@ public BuildResult Build(GenerateResult generateResult, BuildPartition buildPart private void UpdateDuplicatedDependencies(ArtifactsPaths artifactsPaths, ILogger logger) { var publishedDirectory = new DirectoryInfo(artifactsPaths.BinariesDirectoryPath); - var coreRunDirectory = CoreRun.Directory; + var coreRunDirectory = coreRun.Directory; foreach (var publishedDependency in publishedDirectory .EnumerateFileSystemInfos() diff --git a/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunToolchain.cs b/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunToolchain.cs index 3cbaa80f65..43337cdd4a 100644 --- a/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunToolchain.cs @@ -34,7 +34,7 @@ public CoreRunToolchain(FileInfo coreRun, bool createCopy = true, Name = displayName; Generator = new CoreRunGenerator(SourceCoreRun, CopyCoreRun, targetFrameworkMoniker, customDotNetCliPath?.FullName, restorePath?.FullName); - Builder = new CoreRunPublisher(CopyCoreRun, customDotNetCliPath); + Builder = new CoreRunPublisher(targetFrameworkMoniker, CopyCoreRun, customDotNetCliPath); Executor = new DotNetCliExecutor(customDotNetCliPath: CopyCoreRun.FullName); // instead of executing "dotnet $pathToDll" we do "CoreRun $pathToDll" } diff --git a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs index 5e40a91ba5..0afa8b5092 100644 --- a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs @@ -14,6 +14,7 @@ using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Running; using BenchmarkDotNet.Toolchains.DotNetCli; +using BenchmarkDotNet.Toolchains.Results; using JetBrains.Annotations; namespace BenchmarkDotNet.Toolchains.CsProj @@ -64,6 +65,20 @@ protected override string GetProjectFilePath(string buildArtifactsDirectoryPath) protected override string GetBinariesDirectoryPath(string buildArtifactsDirectoryPath, string configuration) => Path.Combine(buildArtifactsDirectoryPath, "bin", configuration, TargetFrameworkMoniker); + protected override void GenerateBuildScript(BuildPartition buildPartition, ArtifactsPaths artifactsPaths) + { + string projectFilePath = GetProjectFilePath(buildPartition.RepresentativeBenchmarkCase.Descriptor.Type, NullLogger.Instance).FullName; + + var content = new StringBuilder(300) + .AppendLine($"call {CliPath ?? "dotnet"} {DotNetCliCommand.GetRestoreCommand(artifactsPaths, buildPartition, projectFilePath)}") + .AppendLine($"call {CliPath ?? "dotnet"} {DotNetCliCommand.GetPublishCommand(artifactsPaths, buildPartition, projectFilePath, TargetFrameworkMoniker)}") + .AppendLine($"call {CliPath ?? "dotnet"} {DotNetCliCommand.GetRestoreCommand(artifactsPaths, buildPartition, artifactsPaths.ProjectFilePath)}") + .AppendLine($"call {CliPath ?? "dotnet"} {DotNetCliCommand.GetPublishCommand(artifactsPaths, buildPartition, artifactsPaths.ProjectFilePath, TargetFrameworkMoniker)}") + .ToString(); + + File.WriteAllText(artifactsPaths.BuildScriptFilePath, content); + } + [SuppressMessage("ReSharper", "StringLiteralTypo")] // R# complains about $variables$ protected override void GenerateProject(BuildPartition buildPartition, ArtifactsPaths artifactsPaths, ILogger logger) { @@ -86,6 +101,70 @@ protected override void GenerateProject(BuildPartition buildPartition, Artifacts .ToString(); File.WriteAllText(artifactsPaths.ProjectFilePath, content); + + // Integration tests are built without dependencies, so we skip gathering dlls. + if (!buildPartition.ForcedNoDependenciesForIntegrationTests) + { + GatherReferences(projectFile.FullName, buildPartition, artifactsPaths, logger); + } + } + + protected void GatherReferences(string projectFilePath, BuildPartition buildPartition, ArtifactsPaths artifactsPaths, ILogger logger) + { + // Build the original project then reference all of the built dlls. + BuildResult buildResult = BuildProject(TargetFrameworkMoniker); + + // The build could fail because the project doesn't have a tfm that matches the runtime, e.g. netstandard2.0 vs net10.0, + // So we try to get the actual tfm of the assembly and build again. + if (!buildResult.IsBuildSuccess + && FrameworkVersionHelper.GetTfm(buildPartition.RepresentativeBenchmarkCase.Descriptor.Type.Assembly) is { } actualTfm + && actualTfm != TargetFrameworkMoniker) + { + buildResult = BuildProject(actualTfm); + } + + if (!buildResult.IsBuildSuccess) + { + if (!buildResult.TryToExplainFailureReason(out string reason)) + { + reason = buildResult.ErrorMessage; + } + logger.WriteLineWarning($"Failed to build source project to obtain dll references. Moving forward without it. Reason: {reason}"); + return; + } + + var xmlDoc = new XmlDocument(); + xmlDoc.Load(artifactsPaths.ProjectFilePath); + XmlElement projectElement = xmlDoc.DocumentElement; + var itemGroup = xmlDoc.CreateElement("ItemGroup"); + projectElement.AppendChild(itemGroup); + foreach (var assemblyFile in Directory.GetFiles(artifactsPaths.BinariesDirectoryPath, "*.dll")) + { + var referenceElement = xmlDoc.CreateElement("Reference"); + itemGroup.AppendChild(referenceElement); + referenceElement.SetAttribute("Include", Path.GetFileNameWithoutExtension(assemblyFile)); + var hintPath = xmlDoc.CreateElement("HintPath"); + referenceElement.AppendChild(hintPath); + var locationNode = xmlDoc.CreateTextNode(assemblyFile); + hintPath.AppendChild(locationNode); + // TODO: Add Aliases here for extern alias #2289 + } + + xmlDoc.Save(artifactsPaths.ProjectFilePath); + + BuildResult BuildProject(string tfm) + => new DotNetCliCommand( + CliPath, + projectFilePath, + tfm, + null, + GenerateResult.Success(artifactsPaths, []), + logger, + buildPartition, + [], + buildPartition.Timeout + ) + .RestoreThenBuild(); } /// diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliBuilder.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliBuilder.cs index 7a7afc3184..79773c7be0 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliBuilder.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliBuilder.cs @@ -25,16 +25,18 @@ public DotNetCliBuilder(string targetFrameworkMoniker, string? customDotNetCliPa public BuildResult Build(GenerateResult generateResult, BuildPartition buildPartition, ILogger logger) { - BuildResult buildResult = new DotNetCliCommand( - CustomDotNetCliPath, - string.Empty, - generateResult, - logger, - buildPartition, - Array.Empty(), - buildPartition.Timeout, - logOutput: LogOutput) - .RestoreThenBuild(); + var buildResult = new DotNetCliCommand( + CustomDotNetCliPath, + generateResult.ArtifactsPaths.ProjectFilePath, + TargetFrameworkMoniker, + string.Empty, + generateResult, + logger, + buildPartition, + [], + buildPartition.Timeout, + logOutput: LogOutput + ).RestoreThenBuild(); if (buildResult.IsBuildSuccess && buildPartition.RepresentativeBenchmarkCase.Job.Environment.LargeAddressAware) { diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommand.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommand.cs index 12d268561d..5506dfe8c6 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommand.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommand.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.IO; using System.Linq; using System.Text; using BenchmarkDotNet.Characteristics; -using BenchmarkDotNet.Environments; using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Loggers; @@ -20,7 +18,11 @@ public class DotNetCliCommand { [PublicAPI] public string CliPath { get; } - [PublicAPI] public string Arguments { get; } + [PublicAPI] public string FilePath { get; } + + [PublicAPI] public string TargetFrameworkMoniker { get; } + + [PublicAPI] public string? Arguments { get; } [PublicAPI] public GenerateResult GenerateResult { get; } @@ -28,17 +30,19 @@ public class DotNetCliCommand [PublicAPI] public BuildPartition BuildPartition { get; } - [PublicAPI] public IReadOnlyList EnvironmentVariables { get; } + [PublicAPI] public IReadOnlyList? EnvironmentVariables { get; } [PublicAPI] public TimeSpan Timeout { get; } [PublicAPI] public bool LogOutput { get; } - public DotNetCliCommand(string cliPath, string arguments, GenerateResult generateResult, ILogger logger, - BuildPartition buildPartition, IReadOnlyList environmentVariables, TimeSpan timeout, bool logOutput = false) + public DotNetCliCommand(string cliPath, string filePath, string tfm, string? arguments, GenerateResult generateResult, ILogger logger, + BuildPartition buildPartition, IReadOnlyList? environmentVariables, TimeSpan timeout, bool logOutput = false) { CliPath = cliPath ?? DotNetCliCommandExecutor.DefaultDotNetCliPath.Value; Arguments = arguments; + FilePath = filePath; + TargetFrameworkMoniker = tfm; GenerateResult = generateResult; Logger = logger; BuildPartition = buildPartition; @@ -48,10 +52,10 @@ public DotNetCliCommand(string cliPath, string arguments, GenerateResult generat } public DotNetCliCommand WithArguments(string arguments) - => new(CliPath, arguments, GenerateResult, Logger, BuildPartition, EnvironmentVariables, Timeout, logOutput: LogOutput); + => new(CliPath, FilePath, TargetFrameworkMoniker, arguments, GenerateResult, Logger, BuildPartition, EnvironmentVariables, Timeout, LogOutput); public DotNetCliCommand WithCliPath(string cliPath) - => new(cliPath, Arguments, GenerateResult, Logger, BuildPartition, EnvironmentVariables, Timeout, logOutput: LogOutput); + => new(cliPath, FilePath, TargetFrameworkMoniker, Arguments, GenerateResult, Logger, BuildPartition, EnvironmentVariables, Timeout, LogOutput); [PublicAPI] public BuildResult RestoreThenBuild() @@ -69,12 +73,12 @@ public BuildResult RestoreThenBuild() if (BuildPartition.ForcedNoDependenciesForIntegrationTests) { var restoreResult = DotNetCliCommandExecutor.Execute(WithArguments( - GetRestoreCommand(GenerateResult.ArtifactsPaths, BuildPartition, $"{Arguments} --no-dependencies", "restore-no-deps", excludeOutput: true))); + GetRestoreCommand(GenerateResult.ArtifactsPaths, BuildPartition, FilePath, $"{Arguments} --no-dependencies", "restore-no-deps", excludeOutput: true))); if (!restoreResult.IsSuccess) return BuildResult.Failure(GenerateResult, restoreResult.AllInformation); return DotNetCliCommandExecutor.Execute(WithArguments( - GetBuildCommand(GenerateResult.ArtifactsPaths, BuildPartition, $"{Arguments} --no-restore --no-dependencies", "build-no-restore-no-deps", excludeOutput: true))) + GetBuildCommand(GenerateResult.ArtifactsPaths, BuildPartition, FilePath, TargetFrameworkMoniker, $"{Arguments} --no-restore --no-dependencies", "build-no-restore-no-deps", excludeOutput: true))) .ToBuildResult(GenerateResult); } else @@ -110,28 +114,30 @@ public BuildResult RestoreThenBuildThenPublish() public DotNetCliCommandResult Restore() => DotNetCliCommandExecutor.Execute(WithArguments( - GetRestoreCommand(GenerateResult.ArtifactsPaths, BuildPartition, Arguments, "restore"))); + GetRestoreCommand(GenerateResult.ArtifactsPaths, BuildPartition, FilePath, Arguments, "restore"))); public DotNetCliCommandResult Build() => DotNetCliCommandExecutor.Execute(WithArguments( - GetBuildCommand(GenerateResult.ArtifactsPaths, BuildPartition, Arguments, "build"))); + GetBuildCommand(GenerateResult.ArtifactsPaths, BuildPartition, FilePath, TargetFrameworkMoniker, Arguments, "build"))); public DotNetCliCommandResult BuildNoRestore() => DotNetCliCommandExecutor.Execute(WithArguments( - GetBuildCommand(GenerateResult.ArtifactsPaths, BuildPartition, $"{Arguments} --no-restore", "build-no-restore"))); + GetBuildCommand(GenerateResult.ArtifactsPaths, BuildPartition, FilePath, TargetFrameworkMoniker, $"{Arguments} --no-restore", "build-no-restore"))); public DotNetCliCommandResult Publish() => DotNetCliCommandExecutor.Execute(WithArguments( - GetPublishCommand(GenerateResult.ArtifactsPaths, BuildPartition, Arguments, "publish"))); + GetPublishCommand(GenerateResult.ArtifactsPaths, BuildPartition, FilePath, TargetFrameworkMoniker, Arguments, "publish"))); // PublishNoBuildAndNoRestore was removed because we set --output in the build step. We use the implicit build included in the publish command. public DotNetCliCommandResult PublishNoRestore() => DotNetCliCommandExecutor.Execute(WithArguments( - GetPublishCommand(GenerateResult.ArtifactsPaths, BuildPartition, $"{Arguments} --no-restore", "publish-no-restore"))); + GetPublishCommand(GenerateResult.ArtifactsPaths, BuildPartition, FilePath, TargetFrameworkMoniker, $"{Arguments} --no-restore", "publish-no-restore"))); - internal static string GetRestoreCommand(ArtifactsPaths artifactsPaths, BuildPartition buildPartition, string? extraArguments = null, string? binLogSuffix = null, bool excludeOutput = false) + internal static string GetRestoreCommand(ArtifactsPaths artifactsPaths, BuildPartition buildPartition, string filePath, string? extraArguments = null, string? binLogSuffix = null, bool excludeOutput = false) => new StringBuilder() .AppendArgument("restore") + .AppendArgument($"\"{filePath}\"") + // restore doesn't support -f argument. .AppendArgument(artifactsPaths.PackagesDirectoryName.IsBlank() ? string.Empty : $"--packages \"{artifactsPaths.PackagesDirectoryName}\"") .AppendArgument(GetCustomMsBuildArguments(buildPartition.RepresentativeBenchmarkCase, buildPartition.Resolver)) .AppendArgument(extraArguments) @@ -140,9 +146,12 @@ internal static string GetRestoreCommand(ArtifactsPaths artifactsPaths, BuildPar .MaybeAppendOutputPaths(artifactsPaths, true, excludeOutput) .ToString(); - internal static string GetBuildCommand(ArtifactsPaths artifactsPaths, BuildPartition buildPartition, string? extraArguments = null, string? binLogSuffix = null, bool excludeOutput = false) + internal static string GetBuildCommand(ArtifactsPaths artifactsPaths, BuildPartition buildPartition, string filePath, string tfm, string? extraArguments = null, string? binLogSuffix = null, bool excludeOutput = false) => new StringBuilder() - .AppendArgument($"build -c {buildPartition.BuildConfiguration}") // we don't need to specify TFM, our auto-generated project contains always single one + .AppendArgument("build") + .AppendArgument($"\"{filePath}\"") + .AppendArgument($"-f {tfm}") + .AppendArgument($"-c {buildPartition.BuildConfiguration}") .AppendArgument(GetCustomMsBuildArguments(buildPartition.RepresentativeBenchmarkCase, buildPartition.Resolver)) .AppendArgument(extraArguments) .AppendArgument(GetMandatoryMsBuildSettings(buildPartition.BuildConfiguration)) @@ -151,9 +160,12 @@ internal static string GetBuildCommand(ArtifactsPaths artifactsPaths, BuildParti .MaybeAppendOutputPaths(artifactsPaths, excludeOutput: excludeOutput) .ToString(); - internal static string GetPublishCommand(ArtifactsPaths artifactsPaths, BuildPartition buildPartition, string? extraArguments = null, string? binLogSuffix = null) + internal static string GetPublishCommand(ArtifactsPaths artifactsPaths, BuildPartition buildPartition, string filePath, string tfm, string? extraArguments = null, string? binLogSuffix = null) => new StringBuilder() - .AppendArgument($"publish -c {buildPartition.BuildConfiguration}") // we don't need to specify TFM, our auto-generated project contains always single one + .AppendArgument("publish") + .AppendArgument($"\"{filePath}\"") + .AppendArgument($"-f {tfm}") + .AppendArgument($"-c {buildPartition.BuildConfiguration}") .AppendArgument(GetCustomMsBuildArguments(buildPartition.RepresentativeBenchmarkCase, buildPartition.Resolver)) .AppendArgument(extraArguments) .AppendArgument(GetMandatoryMsBuildSettings(buildPartition.BuildConfiguration)) diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommandExecutor.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommandExecutor.cs index 4dfdb317cd..20c988c64e 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommandExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommandExecutor.cs @@ -159,11 +159,13 @@ internal static string GetSdkPath(string cliPath) { DotNetCliCommand cliCommand = new( cliPath: cliPath, + filePath: string.Empty, + tfm: string.Empty, arguments: "--info", generateResult: null, logger: NullLogger.Instance, buildPartition: null, - environmentVariables: Array.Empty(), + environmentVariables: [], timeout: TimeSpan.FromMinutes(1), logOutput: false); diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliGenerator.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliGenerator.cs index aaa2cda154..9d0fae9d57 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliGenerator.cs @@ -103,8 +103,8 @@ protected override void CopyAllRequiredFiles(ArtifactsPaths artifactsPaths) protected override void GenerateBuildScript(BuildPartition buildPartition, ArtifactsPaths artifactsPaths) { var content = new StringBuilder(300) - .AppendLine($"call {CliPath ?? "dotnet"} {DotNetCliCommand.GetRestoreCommand(artifactsPaths, buildPartition)}") - .AppendLine($"call {CliPath ?? "dotnet"} {DotNetCliCommand.GetBuildCommand(artifactsPaths, buildPartition)}") + .AppendLine($"call {CliPath ?? "dotnet"} {DotNetCliCommand.GetRestoreCommand(artifactsPaths, buildPartition, artifactsPaths.ProjectFilePath)}") + .AppendLine($"call {CliPath ?? "dotnet"} {DotNetCliCommand.GetBuildCommand(artifactsPaths, buildPartition, artifactsPaths.ProjectFilePath, TargetFrameworkMoniker)}") .ToString(); File.WriteAllText(artifactsPaths.BuildScriptFilePath, content); diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliPublisher.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliPublisher.cs index 83158e0b1e..e46864b98a 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliPublisher.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliPublisher.cs @@ -4,35 +4,23 @@ using BenchmarkDotNet.Running; using BenchmarkDotNet.Toolchains.Results; -namespace BenchmarkDotNet.Toolchains.DotNetCli -{ - public class DotNetCliPublisher : IBuilder - { - public DotNetCliPublisher( - string? customDotNetCliPath = null, - string? extraArguments = null, - IReadOnlyList? environmentVariables = null) - { - CustomDotNetCliPath = customDotNetCliPath; - ExtraArguments = extraArguments; - EnvironmentVariables = environmentVariables; - } - - private string? CustomDotNetCliPath { get; } +namespace BenchmarkDotNet.Toolchains.DotNetCli; - private string? ExtraArguments { get; } - - private IReadOnlyList? EnvironmentVariables { get; } +public class DotNetCliPublisher(string tfm, string? customDotNetCliPath = null, string? extraArguments = null, IReadOnlyList? environmentVariables = null) : IBuilder +{ + public string TargetFrameworkMoniker { get; } = tfm; + public string CustomDotNetCliPath { get; } = customDotNetCliPath; - public BuildResult Build(GenerateResult generateResult, BuildPartition buildPartition, ILogger logger) - => new DotNetCliCommand( - CustomDotNetCliPath, - ExtraArguments, - generateResult, - logger, - buildPartition, - EnvironmentVariables, - buildPartition.Timeout) - .RestoreThenBuildThenPublish(); - } + public virtual BuildResult Build(GenerateResult generateResult, BuildPartition buildPartition, ILogger logger) + => new DotNetCliCommand( + CustomDotNetCliPath, + generateResult.ArtifactsPaths.ProjectFilePath, + TargetFrameworkMoniker, + extraArguments, + generateResult, + logger, + buildPartition, + environmentVariables, + buildPartition.Timeout + ).RestoreThenBuildThenPublish(); } diff --git a/src/BenchmarkDotNet/Toolchains/Mono/MonoPublisher.cs b/src/BenchmarkDotNet/Toolchains/Mono/MonoPublisher.cs index ff98540cbf..2620f25c86 100644 --- a/src/BenchmarkDotNet/Toolchains/Mono/MonoPublisher.cs +++ b/src/BenchmarkDotNet/Toolchains/Mono/MonoPublisher.cs @@ -5,34 +5,27 @@ using BenchmarkDotNet.Toolchains.DotNetCli; using BenchmarkDotNet.Toolchains.Results; -namespace BenchmarkDotNet.Toolchains.Mono -{ - public class MonoPublisher : IBuilder - { - public MonoPublisher(string customDotNetCliPath) - { - CustomDotNetCliPath = customDotNetCliPath; - var runtimeIdentifier = CustomDotNetCliToolchainBuilder.GetPortableRuntimeIdentifier(); - - // /p:RuntimeIdentifiers is set explicitly here because --self-contained requires it, see https://github.com/dotnet/sdk/issues/10566 - ExtraArguments = $"--self-contained -r {runtimeIdentifier} /p:UseMonoRuntime=true /p:RuntimeIdentifiers={runtimeIdentifier}"; - } - - private string CustomDotNetCliPath { get; } +namespace BenchmarkDotNet.Toolchains.Mono; - private string ExtraArguments { get; } - - private IReadOnlyList EnvironmentVariables { get; } +public class MonoPublisher(string tfm, string customDotNetCliPath) : DotNetCliPublisher(tfm, customDotNetCliPath) +{ + public override BuildResult Build(GenerateResult generateResult, BuildPartition buildPartition, ILogger logger) + => new DotNetCliCommand( + CustomDotNetCliPath, + generateResult.ArtifactsPaths.ProjectFilePath, + TargetFrameworkMoniker, + GetExtraArguments(), + generateResult, + logger, + buildPartition, + [], + buildPartition.Timeout + ).Publish().ToBuildResult(generateResult); - public BuildResult Build(GenerateResult generateResult, BuildPartition buildPartition, ILogger logger) - => new DotNetCliCommand( - CustomDotNetCliPath, - ExtraArguments, - generateResult, - logger, - buildPartition, - EnvironmentVariables, - buildPartition.Timeout) - .Publish().ToBuildResult(generateResult); + private static string GetExtraArguments() + { + var runtimeIdentifier = CustomDotNetCliToolchainBuilder.GetPortableRuntimeIdentifier(); + // /p:RuntimeIdentifiers is set explicitly here because --self-contained requires it, see https://github.com/dotnet/sdk/issues/10566 + return $"--self-contained -r {runtimeIdentifier} /p:UseMonoRuntime=true /p:RuntimeIdentifiers={runtimeIdentifier}"; } } diff --git a/src/BenchmarkDotNet/Toolchains/Mono/MonoToolchain.cs b/src/BenchmarkDotNet/Toolchains/Mono/MonoToolchain.cs index 222eb2111b..a705034463 100644 --- a/src/BenchmarkDotNet/Toolchains/Mono/MonoToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/Mono/MonoToolchain.cs @@ -21,13 +21,11 @@ private MonoToolchain(string name, IGenerator generator, IBuilder builder, IExec [PublicAPI] public static new IToolchain From(NetCoreAppSettings settings) - { - return new MonoToolchain(settings.Name, - new MonoGenerator(settings.TargetFrameworkMoniker, settings.CustomDotNetCliPath, settings.PackagesPath, settings.RuntimeFrameworkVersion), - new MonoPublisher(settings.CustomDotNetCliPath), - new DotNetCliExecutor(settings.CustomDotNetCliPath), - settings.CustomDotNetCliPath); - } + => new MonoToolchain(settings.Name, + new MonoGenerator(settings.TargetFrameworkMoniker, settings.CustomDotNetCliPath, settings.PackagesPath, settings.RuntimeFrameworkVersion), + new MonoPublisher(settings.TargetFrameworkMoniker, settings.CustomDotNetCliPath), + new DotNetCliExecutor(settings.CustomDotNetCliPath), + settings.CustomDotNetCliPath); public override bool Equals(object obj) => obj is MonoToolchain typed && Equals(typed); diff --git a/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMGenerator.cs b/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMGenerator.cs index 6ba5968617..242d5d4269 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMGenerator.cs @@ -51,6 +51,8 @@ protected override void GenerateProject(BuildPartition buildPartition, Artifacts .ToString(); File.WriteAllText(artifactsPaths.ProjectFilePath, content); + + GatherReferences(projectFile.FullName, buildPartition, artifactsPaths, logger); } protected override string GetPublishDirectoryPath(string buildArtifactsDirectoryPath, string configuration) diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs index f213277880..e340e994f3 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs @@ -62,6 +62,8 @@ protected void GenerateProjectFile(BuildPartition buildPartition, ArtifactsPaths .ToString(); File.WriteAllText(artifactsPaths.ProjectFilePath, content); + + GatherReferences(projectFile.FullName, buildPartition, artifactsPaths, logger); } protected void GenerateLinkerDescriptionFile(ArtifactsPaths artifactsPaths) diff --git a/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs b/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs index fdc5b2f817..76dda258a4 100644 --- a/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs +++ b/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs @@ -71,11 +71,14 @@ protected override string GetBinariesDirectoryPath(string buildArtifactsDirector protected override void GenerateBuildScript(BuildPartition buildPartition, ArtifactsPaths artifactsPaths) { + string projectFilePath = GetProjectFilePath(buildPartition.RepresentativeBenchmarkCase.Descriptor.Type, NullLogger.Instance).FullName; string extraArguments = NativeAotToolchain.GetExtraArguments(runtimeIdentifier); var content = new StringBuilder(300) - .AppendLine($"call {CliPath ?? "dotnet"} {DotNetCliCommand.GetRestoreCommand(artifactsPaths, buildPartition, extraArguments)}") - .AppendLine($"call {CliPath ?? "dotnet"} {DotNetCliCommand.GetPublishCommand(artifactsPaths, buildPartition, extraArguments)}") + .AppendLine($"call {CliPath ?? "dotnet"} {DotNetCliCommand.GetRestoreCommand(artifactsPaths, buildPartition, projectFilePath, extraArguments)}") + .AppendLine($"call {CliPath ?? "dotnet"} {DotNetCliCommand.GetPublishCommand(artifactsPaths, buildPartition, projectFilePath, TargetFrameworkMoniker, extraArguments)}") + .AppendLine($"call {CliPath ?? "dotnet"} {DotNetCliCommand.GetRestoreCommand(artifactsPaths, buildPartition, artifactsPaths.ProjectFilePath, extraArguments)}") + .AppendLine($"call {CliPath ?? "dotnet"} {DotNetCliCommand.GetPublishCommand(artifactsPaths, buildPartition, artifactsPaths.ProjectFilePath, TargetFrameworkMoniker, extraArguments)}") .ToString(); File.WriteAllText(artifactsPaths.BuildScriptFilePath, content); @@ -112,17 +115,21 @@ protected override void GenerateNuGetConfig(ArtifactsPaths artifactsPaths) protected override void GenerateProject(BuildPartition buildPartition, ArtifactsPaths artifactsPaths, ILogger logger) { - File.WriteAllText(artifactsPaths.ProjectFilePath, GenerateProjectForNuGetBuild(buildPartition, artifactsPaths, logger)); + var projectFile = GetProjectFilePath(buildPartition.RepresentativeBenchmarkCase.Descriptor.Type, logger).FullName; + + File.WriteAllText(artifactsPaths.ProjectFilePath, GenerateProjectForNuGetBuild(projectFile, buildPartition, artifactsPaths, logger)); + + GatherReferences(projectFile, buildPartition, artifactsPaths, logger); GenerateReflectionFile(artifactsPaths); } - private string GenerateProjectForNuGetBuild(BuildPartition buildPartition, ArtifactsPaths artifactsPaths, ILogger logger) => $@" + private string GenerateProjectForNuGetBuild(string projectFilePath, BuildPartition buildPartition, ArtifactsPaths artifactsPaths, ILogger logger) => $@" false false Exe - {TargetFrameworkMoniker} + {TargetFrameworkMoniker} {runtimeIdentifier} {RuntimeFrameworkVersion} {artifactsPaths.ProgramName} @@ -152,7 +159,7 @@ private string GenerateProjectForNuGetBuild(BuildPartition buildPartition, Artif {GetILCompilerPackageReference()} - + {string.Join(Environment.NewLine, GetRdXmlFiles(buildPartition.RepresentativeBenchmarkCase.Descriptor.Type, logger).Select(file => $""))} diff --git a/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchain.cs b/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchain.cs index 9d476bf0e0..33da4613ed 100644 --- a/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchain.cs @@ -60,7 +60,7 @@ internal NativeAotToolchain(string displayName, runtimeIdentifier, feeds, useNuGetClearTag, useTempFolderForRestore, packagesRestorePath, rootAllApplicationAssemblies, ilcGenerateCompleteTypeMetadata, ilcGenerateStackTraceData, ilcOptimizationPreference, ilcInstructionSet), - new DotNetCliPublisher(customDotNetCliPath, GetExtraArguments(runtimeIdentifier)), + new DotNetCliPublisher(targetFrameworkMoniker, customDotNetCliPath, GetExtraArguments(runtimeIdentifier)), new Executor()) { CustomDotNetCliPath = customDotNetCliPath;