diff --git a/.gitignore b/.gitignore index 2be928e40089..41f78907d051 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,6 @@ cmake/ # MSBuild Logs **/MSBuild_Logs/MSBuild_pid-*.failure.txt + +# Test results +**/*.trx diff --git a/src/Cli/dotnet/CommonOptions.cs b/src/Cli/dotnet/CommonOptions.cs index 6525e27816d6..95a86ec9984b 100644 --- a/src/Cli/dotnet/CommonOptions.cs +++ b/src/Cli/dotnet/CommonOptions.cs @@ -129,7 +129,7 @@ public static CliArgument DefaultToCurrentDirectory(this CliArgument new string[] { "-p:UseRazorBuildServer=false", "-p:UseSharedCompilation=false", "/nodeReuse:false" }); + .ForwardAsMany(_ => ["--property:UseRazorBuildServer=false", "--property:UseSharedCompilation=false", "/nodeReuse:false"]); public static CliOption ArchitectureOption = new ForwardedOption("--arch", "-a") diff --git a/src/Cli/dotnet/OptionForwardingExtensions.cs b/src/Cli/dotnet/OptionForwardingExtensions.cs index 3864aaa26f71..d402aa8c3031 100644 --- a/src/Cli/dotnet/OptionForwardingExtensions.cs +++ b/src/Cli/dotnet/OptionForwardingExtensions.cs @@ -44,7 +44,7 @@ public static ForwardedOption ForwardAsProperty(this ForwardedOption optionVals .SelectMany(Utils.MSBuildPropertyParser.ParseProperties) - .Select(keyValue => $"{option.Name}:{keyValue.key}={keyValue.value}") + .Select(keyValue => keyValue.value == "" ? $"{option.Name}:{keyValue.key}" : $"{option.Name}:{keyValue.key}={keyValue.value}") ); public static CliOption ForwardAsMany(this ForwardedOption option, Func> format) => option.SetForwardingFunction(format); diff --git a/src/Cli/dotnet/ParseResultExtensions.cs b/src/Cli/dotnet/ParseResultExtensions.cs index 0aa61914a318..faac5cb2ed1f 100644 --- a/src/Cli/dotnet/ParseResultExtensions.cs +++ b/src/Cli/dotnet/ParseResultExtensions.cs @@ -54,7 +54,7 @@ public static void ShowHelpOrErrorIfAppropriate(this ParseResult parseResult) } } - ///Splits a .NET format string by the format placeholders (the {N} parts) to get an array of the literal parts, to be used in message-checking + ///Splits a .NET format string by the format placeholders (the {N} parts) to get an array of the literal parts, to be used in message-checking static string[] DistinctFormatStringParts(string formatString) { return Regex.Split(formatString, @"{[0-9]+}"); // match the literal '{', followed by any of 0-9 one or more times, followed by the literal '}' @@ -173,8 +173,8 @@ public static bool BothArchAndOsOptionsSpecified(this ParseResult parseResult) = internal static string GetCommandLineRuntimeIdentifier(this ParseResult parseResult) { - return parseResult.HasOption(RunCommandParser.RuntimeOption) ? - parseResult.GetValue(RunCommandParser.RuntimeOption) : + return parseResult.HasOption(CommonOptions.RuntimeOption) ? + parseResult.GetValue(CommonOptions.RuntimeOption) : parseResult.HasOption(CommonOptions.OperatingSystemOption) || parseResult.HasOption(CommonOptions.ArchitectureOption) || parseResult.HasOption(CommonOptions.LongFormArchitectureOption) ? diff --git a/src/Cli/dotnet/commands/dotnet-run/LocalizableStrings.resx b/src/Cli/dotnet/commands/dotnet-run/LocalizableStrings.resx index e7c2e21b59f7..ca6c5bee84ad 100644 --- a/src/Cli/dotnet/commands/dotnet-run/LocalizableStrings.resx +++ b/src/Cli/dotnet/commands/dotnet-run/LocalizableStrings.resx @@ -1,17 +1,17 @@  - @@ -180,6 +180,10 @@ The current {1} is '{2}'. The launch profile "{0}" could not be applied. {1} + + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + {0} is the name of an MSBuild target + (Default) @@ -206,7 +210,7 @@ The current {1} is '{2}'. An error was encountered when reading launchSettings.json. {0} - + '{0}' is not a valid project file. diff --git a/src/Cli/dotnet/commands/dotnet-run/Program.cs b/src/Cli/dotnet/commands/dotnet-run/Program.cs index 9f5614abe958..8399465d5ced 100644 --- a/src/Cli/dotnet/commands/dotnet-run/Program.cs +++ b/src/Cli/dotnet/commands/dotnet-run/Program.cs @@ -18,24 +18,21 @@ public static RunCommand FromArgs(string[] args) public static RunCommand FromParseResult(ParseResult parseResult) { - var project = parseResult.GetValue(RunCommandParser.ProjectOption); if (parseResult.UsingRunCommandShorthandProjectOption()) { Reporter.Output.WriteLine(LocalizableStrings.RunCommandProjectAbbreviationDeprecated.Yellow()); - project = parseResult.GetRunCommandShorthandProjectValues().FirstOrDefault(); + parseResult = ModifyParseResultForShorthandProjectOption(parseResult); } var command = new RunCommand( - configuration: parseResult.GetValue(RunCommandParser.ConfigurationOption), - framework: parseResult.GetValue(RunCommandParser.FrameworkOption), - runtime: parseResult.GetCommandLineRuntimeIdentifier(), noBuild: parseResult.HasOption(RunCommandParser.NoBuildOption), - project: project, + projectFileOrDirectory: parseResult.GetValue(RunCommandParser.ProjectOption), launchProfile: parseResult.GetValue(RunCommandParser.LaunchProfileOption), noLaunchProfile: parseResult.HasOption(RunCommandParser.NoLaunchProfileOption), noRestore: parseResult.HasOption(RunCommandParser.NoRestoreOption) || parseResult.HasOption(RunCommandParser.NoBuildOption), interactive: parseResult.HasOption(RunCommandParser.InteractiveOption), - restoreArgs: parseResult.OptionValuesToBeForwarded(RunCommandParser.GetCommand()), + verbosity: parseResult.HasOption(CommonOptions.VerbosityOption) ? parseResult.GetValue(CommonOptions.VerbosityOption) : null, + restoreArgs: parseResult.OptionValuesToBeForwarded(RunCommandParser.GetCommand()).ToArray(), args: parseResult.GetValue(RunCommandParser.ApplicationArguments) ); @@ -48,5 +45,54 @@ public static int Run(ParseResult parseResult) return FromParseResult(parseResult).Execute(); } + + public static ParseResult ModifyParseResultForShorthandProjectOption(ParseResult parseResult) + { + // we know the project is going to be one of the following forms: + // -p:project + // -p project + // so try to find those and filter them out of the arguments array + var possibleProject = parseResult.GetRunCommandShorthandProjectValues().FirstOrDefault()!; + var tokensMinusProject = new List(); + var nextTokenMayBeProject = false; + foreach (var token in parseResult.Tokens) + { + if (token.Value == "-p") + { + // skip this token, if the next token _is_ the project then we'll skip that too + // if the next token _isn't_ the project then we'll backfill + nextTokenMayBeProject = true; + continue; + } + else if (token.Value == possibleProject && nextTokenMayBeProject) + { + // skip, we've successfully stripped this option and value entirely + nextTokenMayBeProject = false; + continue; + } + else if (token.Value.StartsWith("-p") && token.Value.EndsWith(possibleProject)) + { + // both option and value in the same token, skip and carry on + } + else + { + if (nextTokenMayBeProject) + { + //we skipped a -p, so backfill it + tokensMinusProject.Add("-p"); + } + nextTokenMayBeProject = false; + } + + tokensMinusProject.Add(token.Value); + } + + tokensMinusProject.Add("--project"); + tokensMinusProject.Add(possibleProject); + + var tokensToParse = tokensMinusProject.ToArray(); + var newParseResult = Parser.Instance.Parse(tokensToParse); + return newParseResult; + } } } diff --git a/src/Cli/dotnet/commands/dotnet-run/RunCommand.cs b/src/Cli/dotnet/commands/dotnet-run/RunCommand.cs index 2085fa156687..e7b154267041 100644 --- a/src/Cli/dotnet/commands/dotnet-run/RunCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-run/RunCommand.cs @@ -1,8 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#nullable enable + +using System.Reflection; using Microsoft.Build.Exceptions; using Microsoft.Build.Execution; +using Microsoft.Build.Framework; +using Microsoft.Build.Logging; using Microsoft.DotNet.Cli; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.CommandFactory; @@ -12,30 +17,46 @@ namespace Microsoft.DotNet.Tools.Run { public partial class RunCommand { - public string Configuration { get; private set; } - public string Framework { get; private set; } - public string Runtime { get; private set; } + private record RunProperties(string? RunCommand, string? RunArguments, string? RunWorkingDirectory); + public bool NoBuild { get; private set; } - public string Project { get; private set; } - public IEnumerable Args { get; set; } + public string ProjectFileFullPath { get; private set; } + public string[] Args { get; set; } public bool NoRestore { get; private set; } + public VerbosityOptions? Verbosity { get; } public bool Interactive { get; private set; } - public IEnumerable RestoreArgs { get; private set; } + public string[] RestoreArgs { get; private set; } private bool ShouldBuild => !NoBuild; - private bool HasQuietVerbosity => - RestoreArgs.All(arg => !arg.StartsWith("-verbosity:", StringComparison.Ordinal) || - arg.Equals("-verbosity:q", StringComparison.Ordinal) || - arg.Equals("-verbosity:quiet", StringComparison.Ordinal)); public string LaunchProfile { get; private set; } public bool NoLaunchProfile { get; private set; } private bool UseLaunchProfile => !NoLaunchProfile; - public int Execute() + public RunCommand( + bool noBuild, + string? projectFileOrDirectory, + string launchProfile, + bool noLaunchProfile, + bool noRestore, + bool interactive, + VerbosityOptions? verbosity, + string[] restoreArgs, + string[] args) { - Initialize(); + NoBuild = noBuild; + ProjectFileFullPath = DiscoverProjectFilePath(projectFileOrDirectory); + LaunchProfile = launchProfile; + NoLaunchProfile = noLaunchProfile; + Args = args; + Interactive = interactive; + NoRestore = noRestore; + Verbosity = verbosity; + RestoreArgs = GetRestoreArguments(restoreArgs); + } + public int Execute() + { if (!TryGetLaunchProfileSettingsIfNeeded(out var launchSettings)) { return 1; @@ -54,66 +75,45 @@ public int Execute() try { ICommand targetCommand = GetTargetCommand(); - if (launchSettings != null) - { - if (!string.IsNullOrEmpty(launchSettings.ApplicationUrl)) - { - targetCommand.EnvironmentVariable("ASPNETCORE_URLS", launchSettings.ApplicationUrl); - } - - targetCommand.EnvironmentVariable("DOTNET_LAUNCH_PROFILE", launchSettings.LaunchProfileName); - - foreach (var entry in launchSettings.EnvironmentVariables) - { - string value = Environment.ExpandEnvironmentVariables(entry.Value); - //NOTE: MSBuild variables are not expanded like they are in VS - targetCommand.EnvironmentVariable(entry.Key, value); - } - if (string.IsNullOrEmpty(targetCommand.CommandArgs) && launchSettings.CommandLineArgs != null) - { - targetCommand.SetCommandArgs(launchSettings.CommandLineArgs); - } - } - + var launchSettingsCommand = ApplyLaunchSettingsProfileToCommand(targetCommand, launchSettings); // Ignore Ctrl-C for the remainder of the command's execution Console.CancelKeyPress += (sender, e) => { e.Cancel = true; }; - - return targetCommand.Execute().ExitCode; + return launchSettingsCommand.Execute().ExitCode; } catch (InvalidProjectFileException e) { throw new GracefulException( - string.Format(LocalizableStrings.RunCommandSpecifiecFileIsNotAValidProject, Project), + string.Format(LocalizableStrings.RunCommandSpecifiedFileIsNotAValidProject, ProjectFileFullPath), e); } } - public RunCommand(string configuration, - string framework, - string runtime, - bool noBuild, - string project, - string launchProfile, - bool noLaunchProfile, - bool noRestore, - bool interactive, - IEnumerable restoreArgs, - IEnumerable args) + private ICommand ApplyLaunchSettingsProfileToCommand(ICommand targetCommand, ProjectLaunchSettingsModel? launchSettings) { - Configuration = configuration; - Framework = framework; - Runtime = runtime; - NoBuild = noBuild; - Project = project; - LaunchProfile = launchProfile; - NoLaunchProfile = noLaunchProfile; - Args = args; - RestoreArgs = restoreArgs; - NoRestore = noRestore; - Interactive = interactive; + if (launchSettings != null) + { + if (!string.IsNullOrEmpty(launchSettings.ApplicationUrl)) + { + targetCommand.EnvironmentVariable("ASPNETCORE_URLS", launchSettings.ApplicationUrl); + } + + targetCommand.EnvironmentVariable("DOTNET_LAUNCH_PROFILE", launchSettings.LaunchProfileName); + + foreach (var entry in launchSettings.EnvironmentVariables) + { + string value = Environment.ExpandEnvironmentVariables(entry.Value); + //NOTE: MSBuild variables are not expanded like they are in VS + targetCommand.EnvironmentVariable(entry.Key, value); + } + if (string.IsNullOrEmpty(targetCommand.CommandArgs) && launchSettings.CommandLineArgs != null) + { + targetCommand.SetCommandArgs(launchSettings.CommandLineArgs); + } + } + return targetCommand; } - private bool TryGetLaunchProfileSettingsIfNeeded(out ProjectLaunchSettingsModel launchSettingsModel) + private bool TryGetLaunchProfileSettingsIfNeeded(out ProjectLaunchSettingsModel? launchSettingsModel) { launchSettingsModel = default; if (!UseLaunchProfile) @@ -121,66 +121,77 @@ private bool TryGetLaunchProfileSettingsIfNeeded(out ProjectLaunchSettingsModel return true; } - var buildPathContainer = File.Exists(Project) ? Path.GetDirectoryName(Project) : Project; - string propsDirectory; + var launchSettingsPath = TryFindLaunchSettings(ProjectFileFullPath); + if (!File.Exists(launchSettingsPath)) + { + if (!string.IsNullOrEmpty(LaunchProfile)) + { + Reporter.Error.WriteLine(string.Format(LocalizableStrings.RunCommandExceptionCouldNotLocateALaunchSettingsFile, launchSettingsPath).Bold().Red()); + } + return true; + } - // VB.NET projects store the launch settings file in the - // "My Project" directory instead of a "Properties" directory. - if (string.Equals(Path.GetExtension(Project), ".vbproj", StringComparison.OrdinalIgnoreCase)) + if (Verbosity?.IsQuiet() != true) { - propsDirectory = "My Project"; + Reporter.Output.WriteLine(string.Format(LocalizableStrings.UsingLaunchSettingsFromMessage, launchSettingsPath)); } - else + + string profileName = string.IsNullOrEmpty(LaunchProfile) ? LocalizableStrings.DefaultLaunchProfileDisplayName : LaunchProfile; + + try { - propsDirectory = "Properties"; + var launchSettingsFileContents = File.ReadAllText(launchSettingsPath); + var applyResult = LaunchSettingsManager.TryApplyLaunchSettings(launchSettingsFileContents, LaunchProfile); + if (!applyResult.Success) + { + Reporter.Error.WriteLine(string.Format(LocalizableStrings.RunCommandExceptionCouldNotApplyLaunchSettings, profileName, applyResult.FailureReason).Bold().Red()); + } + else + { + launchSettingsModel = applyResult.LaunchSettings; + } + } + catch (IOException ex) + { + Reporter.Error.WriteLine(string.Format(LocalizableStrings.RunCommandExceptionCouldNotApplyLaunchSettings, profileName).Bold().Red()); + Reporter.Error.WriteLine(ex.Message.Bold().Red()); + return false; } - var launchSettingsPath = Path.Combine(buildPathContainer, propsDirectory, "launchSettings.json"); + return true; - if (File.Exists(launchSettingsPath)) + static string? TryFindLaunchSettings(string projectFilePath) { - if (!HasQuietVerbosity) + var buildPathContainer = File.Exists(projectFilePath) ? Path.GetDirectoryName(projectFilePath) : projectFilePath; + if (buildPathContainer is null) { - Reporter.Output.WriteLine(string.Format(LocalizableStrings.UsingLaunchSettingsFromMessage, launchSettingsPath)); + return null; } - string profileName = string.IsNullOrEmpty(LaunchProfile) ? LocalizableStrings.DefaultLaunchProfileDisplayName : LaunchProfile; + string propsDirectory; - try + // VB.NET projects store the launch settings file in the + // "My Project" directory instead of a "Properties" directory. + // TODO: use the `AppDesignerFolder` MSBuild property instead, which captures this logic already + if (string.Equals(Path.GetExtension(projectFilePath), ".vbproj", StringComparison.OrdinalIgnoreCase)) { - var launchSettingsFileContents = File.ReadAllText(launchSettingsPath); - var applyResult = LaunchSettingsManager.TryApplyLaunchSettings(launchSettingsFileContents, LaunchProfile); - if (!applyResult.Success) - { - Reporter.Error.WriteLine(string.Format(LocalizableStrings.RunCommandExceptionCouldNotApplyLaunchSettings, profileName, applyResult.FailureReason).Bold().Red()); - } - else - { - launchSettingsModel = applyResult.LaunchSettings; - } + propsDirectory = "My Project"; } - catch (IOException ex) + else { - Reporter.Error.WriteLine(string.Format(LocalizableStrings.RunCommandExceptionCouldNotApplyLaunchSettings, profileName).Bold().Red()); - Reporter.Error.WriteLine(ex.Message.Bold().Red()); - return false; + propsDirectory = "Properties"; } - } - else if (!string.IsNullOrEmpty(LaunchProfile)) - { - Reporter.Error.WriteLine(string.Format(LocalizableStrings.RunCommandExceptionCouldNotLocateALaunchSettingsFile, launchSettingsPath).Bold().Red()); - } - return true; + var launchSettingsPath = Path.Combine(buildPathContainer, propsDirectory, "launchSettings.json"); + return launchSettingsPath; + } } private void EnsureProjectIsBuilt() { - var restoreArgs = GetRestoreArguments(); - var buildResult = new RestoringCommand( - restoreArgs.Prepend(Project), + RestoreArgs.Prepend(ProjectFileFullPath), NoRestore, advertiseWorkloadUpdates: false ).Execute(); @@ -192,7 +203,7 @@ private void EnsureProjectIsBuilt() } } - private List GetRestoreArguments() + private string[] GetRestoreArguments(IEnumerable cliRestoreArgs) { List args = new() { @@ -201,77 +212,175 @@ private List GetRestoreArguments() // --interactive need to output guide for auth. It cannot be // completely "quiet" - if (!RestoreArgs.Any(a => a.StartsWith("-verbosity:"))) + if (Verbosity is null) { var defaultVerbosity = Interactive ? "minimal" : "quiet"; args.Add($"-verbosity:{defaultVerbosity}"); } - args.AddRange(RestoreArgs); + args.AddRange(cliRestoreArgs); - return args; + return args.ToArray(); } private ICommand GetTargetCommand() { - var globalProperties = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - // This property disables default item globbing to improve performance - // This should be safe because we are not evaluating items, only properties - { Constants.EnableDefaultItems, "false" }, - { Constants.MSBuildExtensionsPath, AppContext.BaseDirectory } - }; + // TODO for MSBuild usage here: need to sync loggers (primarily binlog) used with this evaluation + var project = EvaluateProject(ProjectFileFullPath, RestoreArgs); + ValidatePreconditions(project); + InvokeRunArgumentsTarget(project, RestoreArgs, Verbosity); + var runProperties = ReadRunPropertiesFromProject(project, Args); + var command = CreateCommandFromRunProperties(project, runProperties); + return command; - if (!string.IsNullOrWhiteSpace(Configuration)) + static ProjectInstance EvaluateProject(string projectFilePath, string[] restoreArgs) { - globalProperties.Add("Configuration", Configuration); + var globalProperties = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + // This property disables default item globbing to improve performance + // This should be safe because we are not evaluating items, only properties + { Constants.EnableDefaultItems, "false" }, + { Constants.MSBuildExtensionsPath, AppContext.BaseDirectory } + }; + + var userPassedProperties = DeriveUserPassedProperties(restoreArgs); + if (userPassedProperties is not null) + { + foreach (var (key, values) in userPassedProperties) + { + globalProperties[key] = string.Join(";", values); + } + } + var project = new ProjectInstance(projectFilePath, globalProperties, null); + return project; } - if (!string.IsNullOrWhiteSpace(Framework)) + static void ValidatePreconditions(ProjectInstance project) { - globalProperties.Add("TargetFramework", Framework); + if (string.IsNullOrWhiteSpace(project.GetPropertyValue("TargetFramework"))) + { + ThrowUnableToRunError(project); + } } - if (!string.IsNullOrWhiteSpace(Runtime)) + static Dictionary>? DeriveUserPassedProperties(string[] args) { - globalProperties.Add("RuntimeIdentifier", Runtime); - } + var fakeCommand = new System.CommandLine.CliCommand("dotnet") { CommonOptions.PropertiesOption }; + var propertyParsingConfiguration = new System.CommandLine.CliConfiguration(fakeCommand); + var propertyParseResult = propertyParsingConfiguration.Parse(args); + var propertyValues = propertyParseResult.GetValue(CommonOptions.PropertiesOption); - var project = new ProjectInstance(Project, globalProperties, null); + if (propertyValues != null) + { + var userPassedProperties = new Dictionary>(propertyValues.Length, StringComparer.OrdinalIgnoreCase); + foreach (var property in propertyValues) + { + foreach (var (key, value) in MSBuildPropertyParser.ParseProperties(property)) + { + if (userPassedProperties.TryGetValue(key, out var existingValues)) + { + existingValues.Add(value); + } + else + { + userPassedProperties[key] = [value]; + } + } + } + return userPassedProperties; + } + return null; + } - string runProgram = project.GetPropertyValue("RunCommand"); - if (string.IsNullOrEmpty(runProgram)) + static RunProperties ReadRunPropertiesFromProject(ProjectInstance project, string[] applicationArgs) { - ThrowUnableToRunError(project); - } + string runProgram = project.GetPropertyValue("RunCommand"); + if (string.IsNullOrEmpty(runProgram)) + { + ThrowUnableToRunError(project); + } - string runArguments = project.GetPropertyValue("RunArguments"); - string runWorkingDirectory = project.GetPropertyValue("RunWorkingDirectory"); + string runArguments = project.GetPropertyValue("RunArguments"); + string runWorkingDirectory = project.GetPropertyValue("RunWorkingDirectory"); - if (Args.Any()) - { - runArguments += " " + ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(Args); + if (applicationArgs.Any()) + { + runArguments += " " + ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(applicationArgs); + } + return new(runProgram, runArguments, runWorkingDirectory); } - CommandSpec commandSpec = new(runProgram, runArguments); + static ICommand CreateCommandFromRunProperties(ProjectInstance project, RunProperties runProperties) + { + CommandSpec commandSpec = new(runProperties.RunCommand, runProperties.RunArguments); - var command = CommandFactoryUsingResolver.Create(commandSpec) - .WorkingDirectory(runWorkingDirectory); + var command = CommandFactoryUsingResolver.Create(commandSpec) + .WorkingDirectory(runProperties.RunWorkingDirectory); - var rootVariableName = EnvironmentVariableNames.TryGetDotNetRootVariableName( - project.GetPropertyValue("RuntimeIdentifier"), - project.GetPropertyValue("DefaultAppHostRuntimeIdentifier"), - project.GetPropertyValue("TargetFrameworkVersion")); + var rootVariableName = EnvironmentVariableNames.TryGetDotNetRootVariableName( + project.GetPropertyValue("RuntimeIdentifier"), + project.GetPropertyValue("DefaultAppHostRuntimeIdentifier"), + project.GetPropertyValue("TargetFrameworkVersion")); - if (rootVariableName != null && Environment.GetEnvironmentVariable(rootVariableName) == null) + if (rootVariableName != null && Environment.GetEnvironmentVariable(rootVariableName) == null) + { + command.EnvironmentVariable(rootVariableName, Path.GetDirectoryName(new Muxer().MuxerPath)); + } + return command; + } + + static void InvokeRunArgumentsTarget(ProjectInstance project, string[] restoreArgs, VerbosityOptions? verbosity) { - command.EnvironmentVariable(rootVariableName, Path.GetDirectoryName(new Muxer().MuxerPath)); + // if the restoreArgs contain a `-bl` then let's probe it + List loggersForBuild = [ + MakeTerminalLogger(verbosity) + ]; + if (restoreArgs.FirstOrDefault(arg => arg.StartsWith("-bl", StringComparison.OrdinalIgnoreCase)) is string blArg) + { + if (blArg.Contains(':')) + { + // split and forward args + var split = blArg.Split(':', 2); + loggersForBuild.Add(new BinaryLogger { Parameters = split[1] }); + } + else + { + // just the defaults + loggersForBuild.Add(new BinaryLogger { Parameters = "{}.binlog" }); + } + }; + + if (!project.Build([ComputeRunArgumentsTarget], loggers: loggersForBuild, remoteLoggers: null, out var _targetOutputs)) + { + throw new GracefulException(LocalizableStrings.RunCommandEvaluationExceptionBuildFailed, ComputeRunArgumentsTarget); + } } + } - return command; + static ILogger MakeTerminalLogger(VerbosityOptions? verbosity) + { + var msbuildVerbosity = ToLoggerVerbosity(verbosity); + var thing = Assembly.Load("MSBuild").GetType("Microsoft.Build.Logging.TerminalLogger.TerminalLogger")!.GetConstructor([typeof(LoggerVerbosity)])!.Invoke([msbuildVerbosity]) as ILogger; + return thing!; + } + + static string ComputeRunArgumentsTarget = "ComputeRunArguments"; + + private static LoggerVerbosity ToLoggerVerbosity(VerbosityOptions? verbosity) + { + // map all cases of VerbosityOptions enum to the matching LoggerVerbosity enum + return verbosity switch + { + VerbosityOptions.quiet | VerbosityOptions.q => LoggerVerbosity.Quiet, + VerbosityOptions.minimal | VerbosityOptions.m => LoggerVerbosity.Minimal, + VerbosityOptions.normal | VerbosityOptions.n => LoggerVerbosity.Normal, + VerbosityOptions.detailed | VerbosityOptions.d => LoggerVerbosity.Detailed, + VerbosityOptions.diagnostic | VerbosityOptions.diag => LoggerVerbosity.Diagnostic, + _ => LoggerVerbosity.Quiet // default to quiet because run should be invisible if possible + }; } - private void ThrowUnableToRunError(ProjectInstance project) + private static void ThrowUnableToRunError(ProjectInstance project) { string targetFrameworks = project.GetPropertyValue("TargetFrameworks"); if (!string.IsNullOrEmpty(targetFrameworks)) @@ -291,20 +400,21 @@ private void ThrowUnableToRunError(ProjectInstance project) project.GetPropertyValue("OutputType"))); } - private void Initialize() + private string DiscoverProjectFilePath(string? projectFileOrDirectoryPath) { - if (string.IsNullOrWhiteSpace(Project)) + if (string.IsNullOrWhiteSpace(projectFileOrDirectoryPath)) { - Project = Directory.GetCurrentDirectory(); + projectFileOrDirectoryPath = Directory.GetCurrentDirectory(); } - if (Directory.Exists(Project)) + if (Directory.Exists(projectFileOrDirectoryPath)) { - Project = FindSingleProjectInDirectory(Project); + projectFileOrDirectoryPath = FindSingleProjectInDirectory(projectFileOrDirectoryPath); } + return projectFileOrDirectoryPath; } - private static string FindSingleProjectInDirectory(string directory) + public static string FindSingleProjectInDirectory(string directory) { string[] projectFiles = Directory.GetFiles(directory, "*.*proj"); diff --git a/src/Cli/dotnet/commands/dotnet-run/RunCommandParser.cs b/src/Cli/dotnet/commands/dotnet-run/RunCommandParser.cs index 137599163bf9..f2fff28a7e69 100644 --- a/src/Cli/dotnet/commands/dotnet-run/RunCommandParser.cs +++ b/src/Cli/dotnet/commands/dotnet-run/RunCommandParser.cs @@ -22,10 +22,7 @@ internal static class RunCommandParser Description = LocalizableStrings.CommandOptionProjectDescription }; - public static readonly CliOption> PropertyOption = new ForwardedOption>("--property", "-p") - { - Description = LocalizableStrings.PropertyOptionDescription - }.SetForwardingFunction((values, parseResult) => parseResult.GetRunCommandPropertyValues().Select(value => $"-p:{value}")); + public static readonly CliOption PropertyOption = CommonOptions.PropertiesOption; public static readonly CliOption LaunchProfileOption = new("--launch-profile", "-lp") { @@ -50,7 +47,7 @@ internal static class RunCommandParser public static readonly CliOption NoSelfContainedOption = CommonOptions.NoSelfContainedOption; - public static readonly CliArgument> ApplicationArguments = new("applicationArguments") + public static readonly CliArgument ApplicationArguments = new("applicationArguments") { DefaultValueFactory = _ => Array.Empty(), Description = "Arguments passed to the application that is being run." diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.cs.xlf index 0007a96c5bd7..9ab582c64a7c 100644 --- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.cs.xlf +++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.cs.xlf @@ -61,6 +61,11 @@ Nastavte odlišné názvy profilů. Sestavování... + + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + {0} is the name of an MSBuild target + The build failed. Fix the build errors and run again. Sestavení se nepovedlo. Opravte v sestavení chyby a spusťte ho znovu. @@ -136,6 +141,11 @@ Aktuální {1} je {2}. Upozornění NETSDK1174: Zkratka -p pro --project je zastaralá. Použijte prosím --project. {Locked="--project"} + + '{0}' is not a valid project file. + '{0}' is not a valid project file. + + Using launch settings from {0}... Použití nastavení spuštění z {0}... @@ -175,11 +185,6 @@ Aktuální {1} je {2}. {0} - - '{0}' is not a valid project file. - {0} není platný soubor projektu. - - The configuration to run for. The default for most projects is 'Debug'. Konfigurace pro spuštění. Výchozí možností pro většinu projektů je Debug. diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.de.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.de.xlf index bd37f82bca09..6e710ab89166 100644 --- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.de.xlf +++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.de.xlf @@ -61,6 +61,11 @@ Erstellen Sie eindeutige Profilnamen. Buildvorgang wird ausgeführt... + + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + {0} is the name of an MSBuild target + The build failed. Fix the build errors and run again. Fehler beim Buildvorgang. Beheben Sie die Buildfehler, und versuchen Sie es anschließend noch mal. @@ -136,6 +141,11 @@ Ein ausführbares Projekt muss ein ausführbares TFM (z. B. net5.0) und den Outp Warnung NETSDK1174: Die Abkürzung von „-p“ für „--project“ ist veraltet. Verwenden Sie „--project“. {Locked="--project"} + + '{0}' is not a valid project file. + '{0}' is not a valid project file. + + Using launch settings from {0}... Die Starteinstellungen von {0} werden verwendet… @@ -175,11 +185,6 @@ Ein ausführbares Projekt muss ein ausführbares TFM (z. B. net5.0) und den Outp {0} - - '{0}' is not a valid project file. - "{0}" ist keine gültige Projektdatei. - - The configuration to run for. The default for most projects is 'Debug'. Die Konfiguration für die Ausführung. Der Standardwert für die meisten Projekte ist "Debug". diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.es.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.es.xlf index 693e1b942f21..80b519c89ce5 100644 --- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.es.xlf +++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.es.xlf @@ -61,6 +61,11 @@ Defina nombres de perfiles distintos. Compilando... + + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + {0} is the name of an MSBuild target + The build failed. Fix the build errors and run again. No se pudo llevar a cabo la compilación. Corrija los errores de compilación y vuelva a ejecutar el proyecto. @@ -136,6 +141,11 @@ El valor actual de {1} es "{2}". Advertencia NETSDK1174: La abreviatura de -p para --project está en desuso. Use --project. {Locked="--project"} + + '{0}' is not a valid project file. + '{0}' is not a valid project file. + + Using launch settings from {0}... Usando la configuración de inicio de {0}... @@ -175,11 +185,6 @@ El valor actual de {1} es "{2}". {0} - - '{0}' is not a valid project file. - "{0}" no es un archivo de proyecto válido. - - The configuration to run for. The default for most projects is 'Debug'. La configuración para la que se ejecuta. El valor predeterminado para la mayoría de los proyectos es "Debug". diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.fr.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.fr.xlf index 9fa1cab25ad5..5e12596d8240 100644 --- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.fr.xlf +++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.fr.xlf @@ -61,6 +61,11 @@ faites en sorte que les noms de profil soient distincts. Génération... + + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + {0} is the name of an MSBuild target + The build failed. Fix the build errors and run again. La build a échoué. Corrigez les erreurs de la build et réexécutez-la. @@ -136,6 +141,11 @@ Le {1} actuel est '{2}'. AVERTISSEMENT NETSDK1174 : l’abréviation de-p pour--Project est déconseillée. Veuillez utiliser--Project. {Locked="--project"} + + '{0}' is not a valid project file. + '{0}' is not a valid project file. + + Using launch settings from {0}... Utilisation des paramètres de lancement à partir de {0}... @@ -175,11 +185,6 @@ Le {1} actuel est '{2}'. {0} - - '{0}' is not a valid project file. - '{0}' n'est pas un fichier projet valide. - - The configuration to run for. The default for most projects is 'Debug'. Configuration pour laquelle l'exécution est effectuée. La valeur par défaut pour la plupart des projets est 'Debug'. diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.it.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.it.xlf index a819e30cb114..f098036356c3 100644 --- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.it.xlf +++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.it.xlf @@ -61,6 +61,11 @@ Rendi distinti i nomi dei profili. Compilazione... + + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + {0} is the name of an MSBuild target + The build failed. Fix the build errors and run again. La compilazione non è riuscita. Correggere gli errori di compilazione e ripetere l'esecuzione. @@ -136,6 +141,11 @@ Il valore corrente di {1} è '{2}'. Avviso NETSDK1174: l'abbreviazione di -p per --project è deprecata. Usare --project. {Locked="--project"} + + '{0}' is not a valid project file. + '{0}' is not a valid project file. + + Using launch settings from {0}... Uso delle impostazioni di avvio di {0}... @@ -175,11 +185,6 @@ Il valore corrente di {1} è '{2}'. {0} - - '{0}' is not a valid project file. - '{0}' non è un file di progetto valido. - - The configuration to run for. The default for most projects is 'Debug'. Configurazione da usare per l'esecuzione. L'impostazione predefinita per la maggior parte dei progetti è 'Debug'. diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ja.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ja.xlf index 54cdf3003ca9..e58247512275 100644 --- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ja.xlf +++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ja.xlf @@ -61,6 +61,11 @@ Make the profile names distinct. ビルドしています... + + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + {0} is the name of an MSBuild target + The build failed. Fix the build errors and run again. ビルドに失敗しました。ビルド エラーを修正して、もう一度実行してください。 @@ -136,6 +141,11 @@ The current {1} is '{2}'. 警告 NETSDK1174: --project の省略形である -p は推奨されていません。--Project を使用してください。 {Locked="--project"} + + '{0}' is not a valid project file. + '{0}' is not a valid project file. + + Using launch settings from {0}... {0} からの起動設定を使用中... @@ -175,11 +185,6 @@ The current {1} is '{2}'. {0} - - '{0}' is not a valid project file. - '{0}' は有効なプロジェクト ファイルではありません。 - - The configuration to run for. The default for most projects is 'Debug'. 実行する対象の構成。大部分のプロジェクトで、既定値は 'Debug' です。 diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ko.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ko.xlf index 9c05e2f95094..19ee14db99dc 100644 --- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ko.xlf +++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ko.xlf @@ -61,6 +61,11 @@ Make the profile names distinct. 빌드하는 중... + + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + {0} is the name of an MSBuild target + The build failed. Fix the build errors and run again. 빌드하지 못했습니다. 빌드 오류를 수정하고 다시 실행하세요. @@ -136,6 +141,11 @@ The current {1} is '{2}'. 경고 NETSDK1174: --project에 대한 약어 -p는 더 이상 사용되지 않습니다. --project를 사용하세요. {Locked="--project"} + + '{0}' is not a valid project file. + '{0}' is not a valid project file. + + Using launch settings from {0}... {0}의 시작 설정을 사용하는 중... @@ -175,11 +185,6 @@ The current {1} is '{2}'. {0} - - '{0}' is not a valid project file. - '{0}'은(는) 유효한 프로젝트 파일이 아닙니다. - - The configuration to run for. The default for most projects is 'Debug'. 실행할 구성입니다. 대부분의 프로젝트에서 기본값은 'Debug'입니다. diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.pl.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.pl.xlf index cd2cd5fa63c4..a41c6159ed63 100644 --- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.pl.xlf +++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.pl.xlf @@ -61,6 +61,11 @@ Rozróżnij nazwy profilów. Trwa kompilowanie... + + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + {0} is the name of an MSBuild target + The build failed. Fix the build errors and run again. Kompilacja nie powiodła się. Napraw błędy kompilacji i uruchom ją ponownie. @@ -136,6 +141,11 @@ Bieżący element {1}: „{2}”. Ostrzeżenie NETSDK1174: Skrót -p dla polecenia --project jest przestarzały. Użyj polecenia --project. {Locked="--project"} + + '{0}' is not a valid project file. + '{0}' is not a valid project file. + + Using launch settings from {0}... Używanie ustawień uruchamiania z profilu {0}... @@ -175,11 +185,6 @@ Bieżący element {1}: „{2}”. {0} - - '{0}' is not a valid project file. - „{0}” nie jest prawidłowym plikiem projektu. - - The configuration to run for. The default for most projects is 'Debug'. Konfiguracja, którą należy uruchomić. W przypadku większości projektów ustawienie domyślne to „Debugowanie”. diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.pt-BR.xlf index 141238d8d7cb..f4e1a2f86387 100644 --- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.pt-BR.xlf +++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.pt-BR.xlf @@ -61,6 +61,11 @@ Diferencie os nomes dos perfis. Compilando... + + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + {0} is the name of an MSBuild target + The build failed. Fix the build errors and run again. Ocorreu uma falha no build. Corrija os erros de build e execute novamente. @@ -136,6 +141,11 @@ O {1} atual é '{2}'. Aviso NETSDK1174: a abreviação de-p para--projeto é preterida. Use --projeto. {Locked="--project"} + + '{0}' is not a valid project file. + '{0}' is not a valid project file. + + Using launch settings from {0}... Usando as configurações de inicialização de {0}... @@ -175,11 +185,6 @@ O {1} atual é '{2}'. {0} - - '{0}' is not a valid project file. - '{0}' não é um arquivo de projeto válido. - - The configuration to run for. The default for most projects is 'Debug'. A configuração para a qual a execução ocorrerá. O padrão para a maioria dos projetos é 'Debug'. diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ru.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ru.xlf index 86f0f4baadff..07490e2afdc7 100644 --- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ru.xlf +++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ru.xlf @@ -61,6 +61,11 @@ Make the profile names distinct. Сборка… + + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + {0} is the name of an MSBuild target + The build failed. Fix the build errors and run again. Ошибка сборки. Устраните ошибки сборки и повторите попытку. @@ -136,6 +141,11 @@ The current {1} is '{2}'. Предупреждение NETSDK1174: сокращение "-p" для "--project" не рекомендуется. Используйте "--project". {Locked="--project"} + + '{0}' is not a valid project file. + '{0}' is not a valid project file. + + Using launch settings from {0}... Используются параметры запуска из {0}... @@ -175,11 +185,6 @@ The current {1} is '{2}'. {0} - - '{0}' is not a valid project file. - "{0}" не является допустимым файлом проекта. - - The configuration to run for. The default for most projects is 'Debug'. Конфигурация для запуска. По умолчанию для большинства проектов используется "Debug". diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.tr.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.tr.xlf index 170386671e96..e66e6a668b35 100644 --- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.tr.xlf +++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.tr.xlf @@ -61,6 +61,11 @@ Make the profile names distinct. Derleniyor... + + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + {0} is the name of an MSBuild target + The build failed. Fix the build errors and run again. Derleme başarısız oldu. Derleme hatalarını düzeltip yeniden çalıştırın. @@ -136,6 +141,11 @@ Geçerli {1}: '{2}'. Uyarı NETSDK1174: --project için -p kısaltması kullanımdan kaldırıldı. Lütfen --project kullanın. {Locked="--project"} + + '{0}' is not a valid project file. + '{0}' is not a valid project file. + + Using launch settings from {0}... {0} içindeki başlatma ayarları kullanılıyor... @@ -175,11 +185,6 @@ Geçerli {1}: '{2}'. {0} - - '{0}' is not a valid project file. - '{0}' geçerli bir proje dosyası değil. - - The configuration to run for. The default for most projects is 'Debug'. Çalıştırılacak yapılandırma. Çoğu proje için varsayılan, ‘Hata Ayıklama’ seçeneğidir. diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.zh-Hans.xlf index 677e77d70357..dc440d270860 100644 --- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.zh-Hans.xlf +++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.zh-Hans.xlf @@ -61,6 +61,11 @@ Make the profile names distinct. 正在生成... + + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + {0} is the name of an MSBuild target + The build failed. Fix the build errors and run again. 生成失败。请修复生成错误并重新运行。 @@ -136,6 +141,11 @@ The current {1} is '{2}'. 警告 NETSDK1174: 已弃用使用缩写“-p”来代表“--project”。请使用“--project”。 {Locked="--project"} + + '{0}' is not a valid project file. + '{0}' is not a valid project file. + + Using launch settings from {0}... 从 {0} 使用启动设置... @@ -175,11 +185,6 @@ The current {1} is '{2}'. {0} - - '{0}' is not a valid project file. - “{0}”不是有效的项目文件。 - - The configuration to run for. The default for most projects is 'Debug'. 要运行的配置。大多数项目的默认值是 "Debug"。 diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.zh-Hant.xlf index 7c35f05e8efc..1345ce110fb1 100644 --- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.zh-Hant.xlf +++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.zh-Hant.xlf @@ -61,6 +61,11 @@ Make the profile names distinct. 正在建置... + + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + Running the {0} target to discover run commands failed for this project. Fix the errors and warnings and run again. + {0} is the name of an MSBuild target + The build failed. Fix the build errors and run again. 建置失敗。請修正建置錯誤後,再執行一次。 @@ -136,6 +141,11 @@ The current {1} is '{2}'. 警告 NETSDK1174: --project 已取代縮寫 -p。請使用 --project。 {Locked="--project"} + + '{0}' is not a valid project file. + '{0}' is not a valid project file. + + Using launch settings from {0}... 使用來自 {0} 的啟動設定... @@ -175,11 +185,6 @@ The current {1} is '{2}'. {0} - - '{0}' is not a valid project file. - '{0}' 並非有效的專案名稱。 - - The configuration to run for. The default for most projects is 'Debug'. 要為其執行的組態。大部分的專案預設為「偵錯」。 diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets index e21d8dec9c68..9acbb25f59f7 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets @@ -1108,6 +1108,7 @@ Copyright (c) .NET Foundation. All rights reserved. ============================================================ --> + $(StartWorkingDirectory) @@ -1153,8 +1154,18 @@ Copyright (c) .NET Foundation. All rights reserved. $([System.IO.Path]::GetFullPath($([System.IO.Path]::Combine('$(MSBuildProjectDirectory)', '$(RunWorkingDirectory)')))) + + + + + + + + - + diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToControlGeneratedAssemblyInfo.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToControlGeneratedAssemblyInfo.cs index 31c5fce9569a..98ebc8121ae7 100644 --- a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToControlGeneratedAssemblyInfo.cs +++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToControlGeneratedAssemblyInfo.cs @@ -859,9 +859,7 @@ static void Main(string[] args) .WithWorkingDirectory(Path.Combine(testAsset.Path, testProject.Name)) .Execute(); result.Should().Pass(); - result.StdOut.Should().BeEquivalentTo(expectedFrameworkDisplayName); - + result.StdOut.StripTerminalLoggerProgressIndicators().Should().BeEquivalentTo(expectedFrameworkDisplayName); } - } } diff --git a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs index 43544c3ec309..8eafb99faa46 100644 --- a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs +++ b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAHelloWorldProject.cs @@ -592,35 +592,6 @@ public void PublishRelease_does_not_override_Configuration_property_across_forma Assert.False(File.Exists(releaseAssetPath)); // build will produce a debug asset, need to make sure this doesn't exist either. } - - [Theory] - [InlineData("")] - [InlineData("=")] - public void PublishRelease_does_recognize_undefined_property(string propertySuffix) - { - string tfm = ToolsetInfo.CurrentTargetFramework; - var testProject = new TestProject() - { - IsExe = true, - TargetFrameworks = tfm - }; - - testProject.RecordProperties("SelfContained"); - testProject.RecordProperties("PublishAot"); - - var testAsset = _testAssetsManager.CreateTestProject(testProject); - new DotnetPublishCommand(Log) - .WithWorkingDirectory(Path.Combine(testAsset.TestRoot, MethodBase.GetCurrentMethod().Name)) - .Execute(("-p:SelfContained" + propertySuffix)) - .Should() - .Pass(); - - var properties = testProject.GetPropertyValues(testAsset.TestRoot, configuration: "Release", targetFramework: tfm); - - Assert.Equal("", properties["SelfContained"]); - Assert.Equal("", properties["PublishAot"]); - } - [Theory] [InlineData("true")] [InlineData("false")] diff --git a/test/Microsoft.NET.TestFramework/Utilities/TerminalLoggerStringExtensions.cs b/test/Microsoft.NET.TestFramework/Utilities/TerminalLoggerStringExtensions.cs new file mode 100644 index 000000000000..f14b109155f9 --- /dev/null +++ b/test/Microsoft.NET.TestFramework/Utilities/TerminalLoggerStringExtensions.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.NET.TestFramework.Utilities; + +public static class TerminalLoggerExtensions +{ + /// + /// Strip out progress markers that TerminalLogger writes to stdout (at least on Windows OS's). + /// This is non-visible, but impacts string comparison. + public static string StripTerminalLoggerProgressIndicators(this string stdout) + { + return stdout + .Replace("\x1b]9;4;3;\x1b\\", "") // indeterminate progress start + .Replace("\x1b]9;4;0;\x1b\\", ""); // indeterminate progress end + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/DotnetRunTargetExtension/DotnetRunTargetExtension.csproj b/test/TestAssets/TestProjects/DotnetRunTargetExtension/DotnetRunTargetExtension.csproj new file mode 100644 index 000000000000..8f68da643ffa --- /dev/null +++ b/test/TestAssets/TestProjects/DotnetRunTargetExtension/DotnetRunTargetExtension.csproj @@ -0,0 +1,15 @@ + + + + Exe + $(CurrentTargetFramework) + + + + + $(RunArguments) extended + $(MSBuildThisFileDirectory) + + + + diff --git a/test/TestAssets/TestProjects/DotnetRunTargetExtension/Program.cs b/test/TestAssets/TestProjects/DotnetRunTargetExtension/Program.cs new file mode 100644 index 000000000000..9c7fae3b4e2e --- /dev/null +++ b/test/TestAssets/TestProjects/DotnetRunTargetExtension/Program.cs @@ -0,0 +1,14 @@ +using System; + +namespace ConsoleApplication +{ + public class Program + { + public static void Main(string[] args) + { + Console.WriteLine("Hello World!"); + Console.WriteLine("Args: " + string.Join(", ", args)); + Console.WriteLine("CWD: " + System.IO.Directory.GetCurrentDirectory()); + } + } +} diff --git a/test/TestAssets/TestProjects/DotnetRunTargetExtensionWithError/DotnetRunTargetExtensionWithError.csproj b/test/TestAssets/TestProjects/DotnetRunTargetExtensionWithError/DotnetRunTargetExtensionWithError.csproj new file mode 100644 index 000000000000..6283ebdfccd3 --- /dev/null +++ b/test/TestAssets/TestProjects/DotnetRunTargetExtensionWithError/DotnetRunTargetExtensionWithError.csproj @@ -0,0 +1,13 @@ + + + + Exe + $(CurrentTargetFramework) + + + + + + + + diff --git a/test/TestAssets/TestProjects/DotnetRunTargetExtensionWithError/Program.cs b/test/TestAssets/TestProjects/DotnetRunTargetExtensionWithError/Program.cs new file mode 100644 index 000000000000..9c7fae3b4e2e --- /dev/null +++ b/test/TestAssets/TestProjects/DotnetRunTargetExtensionWithError/Program.cs @@ -0,0 +1,14 @@ +using System; + +namespace ConsoleApplication +{ + public class Program + { + public static void Main(string[] args) + { + Console.WriteLine("Hello World!"); + Console.WriteLine("Args: " + string.Join(", ", args)); + Console.WriteLine("CWD: " + System.IO.Directory.GetCurrentDirectory()); + } + } +} diff --git a/test/dotnet-run.Tests/GivenDotnetRunRunsCsProj.cs b/test/dotnet-run.Tests/GivenDotnetRunBuildsCsProj.cs similarity index 99% rename from test/dotnet-run.Tests/GivenDotnetRunRunsCsProj.cs rename to test/dotnet-run.Tests/GivenDotnetRunBuildsCsProj.cs index 083bb14fdf9a..d083626b91ab 100644 --- a/test/dotnet-run.Tests/GivenDotnetRunRunsCsProj.cs +++ b/test/dotnet-run.Tests/GivenDotnetRunBuildsCsProj.cs @@ -129,7 +129,7 @@ public void ItCanRunAMSBuildProjectWhenSpecifyingAFramework() .WithWorkingDirectory(testProjectDirectory) .Execute("--framework", ToolsetInfo.CurrentTargetFramework) .Should().Pass() - .And.HaveStdOut("Hello World!"); + .And.HaveStdOutContaining("Hello World!"); } [Fact] @@ -402,7 +402,7 @@ public void ItUsesLaunchProfileOfTheSpecifiedName() cmd.StdErr.Should().BeEmpty(); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/sdk/issues/42841")] public void ItDefaultsToTheFirstUsableLaunchProfile() { var testAppName = "AppWithLaunchSettings"; diff --git a/test/dotnet-run.Tests/GivenDotnetRunRunsVbProj.cs b/test/dotnet-run.Tests/GivenDotnetRunBuildsVbProj.cs similarity index 100% rename from test/dotnet-run.Tests/GivenDotnetRunRunsVbProj.cs rename to test/dotnet-run.Tests/GivenDotnetRunBuildsVbProj.cs diff --git a/test/dotnet-run.Tests/GivenDotnetRunIsInterrupted.cs b/test/dotnet-run.Tests/GivenDotnetRunIsInterrupted.cs index 5863caefa0a5..66363869aa6a 100644 --- a/test/dotnet-run.Tests/GivenDotnetRunIsInterrupted.cs +++ b/test/dotnet-run.Tests/GivenDotnetRunIsInterrupted.cs @@ -15,6 +15,7 @@ public GivenDotnetRunIsInterrupted(ITestOutputHelper log) : base(log) { } + // This test is Unix only for the same reason that CoreFX does not test Console.CancelKeyPress on Windows // See https://github.com/dotnet/corefx/blob/a10890f4ffe0fadf090c922578ba0e606ebdd16c/src/System.Console/tests/CancelKeyPress.Unix.cs#L63-L67 [UnixOnlyFact] @@ -23,7 +24,7 @@ public void ItIgnoresSIGINT() var asset = _testAssetsManager.CopyTestAsset("TestAppThatWaits") .WithSource(); - var command = new DotnetCommand(Log, "run") + var command = new DotnetCommand(Log, "run", "-v:q") .WithWorkingDirectory(asset.Path); bool killed = false; @@ -38,16 +39,34 @@ public void ItIgnoresSIGINT() { return; } - - // Simulate a SIGINT sent to a process group (i.e. both `dotnet run` and `TestAppThatWaits`). - // Ideally we would send SIGINT to an actual process group, but the new child process (i.e. `dotnet run`) - // will inherit the current process group from the `dotnet test` process that is running this test. - // We would need to fork(), setpgid(), and then execve() to break out of the current group and that is - // too complex for a simple unit test. - NativeMethods.Posix.kill(testProcess.Id, NativeMethods.Posix.SIGINT).Should().Be(0); // dotnet run - NativeMethods.Posix.kill(Convert.ToInt32(line), NativeMethods.Posix.SIGINT).Should().Be(0); // TestAppThatWaits - - killed = true; + if (line.StartsWith("\x1b]")) + { + line = line.StripTerminalLoggerProgressIndicators(); + } + if (int.TryParse(line, out int pid)) + { + // Simulate a SIGINT sent to a process group (i.e. both `dotnet run` and `TestAppThatWaits`). + // Ideally we would send SIGINT to an actual process group, but the new child process (i.e. `dotnet run`) + // will inherit the current process group from the `dotnet test` process that is running this test. + // We would need to fork(), setpgid(), and then execve() to break out of the current group and that is + // too complex for a simple unit test. + NativeMethods.Posix.kill(testProcess.Id, NativeMethods.Posix.SIGINT).Should().Be(0); // dotnet run + try + { + NativeMethods.Posix.kill(Convert.ToInt32(line), NativeMethods.Posix.SIGINT).Should().Be(0); // TestAppThatWaits + } + catch (Exception e) + { + Log.WriteLine($"Error while sending SIGINT to child process: {e}"); + Assert.Fail($"Failed to send SIGINT to child process: {line}"); + } + + killed = true; + } + else + { + Log.WriteLine($"Got line {line} but was unable to interpret it as a process id - skipping"); + } }; command @@ -81,11 +100,28 @@ public void ItPassesSIGTERMToChild() { return; } - - child = Process.GetProcessById(Convert.ToInt32(line)); - NativeMethods.Posix.kill(testProcess.Id, NativeMethods.Posix.SIGTERM).Should().Be(0); - - killed = true; + if (line.StartsWith("\x1b]")) + { + line = line.StripTerminalLoggerProgressIndicators(); + } + if (int.TryParse(line, out int pid)) + { + try + { + child = Process.GetProcessById(pid); + } + catch (Exception e) + { + Log.WriteLine($"Error while getting child process Id: {e}"); + Assert.Fail($"Failed to get to child process Id: {line}"); + } + NativeMethods.Posix.kill(testProcess.Id, NativeMethods.Posix.SIGTERM).Should().Be(0); + killed = true; + } + else + { + Log.WriteLine($"Got line {line} but was unable to interpret it as a process id - skipping"); + } }; command @@ -125,10 +161,28 @@ public void ItTerminatesTheChildWhenKilled() return; } - child = Process.GetProcessById(Convert.ToInt32(line)); - testProcess.Kill(); - - killed = true; + if (line.StartsWith("\x1b]")) + { + line = line.StripTerminalLoggerProgressIndicators(); + } + if (int.TryParse(line, out int pid)) + { + try + { + child = Process.GetProcessById(pid); + } + catch (Exception e) + { + Log.WriteLine($"Error while getting child process Id: {e}"); + Assert.Fail($"Failed to get to child process Id: {line}"); + } + testProcess.Kill(); + killed = true; + } + else + { + Log.WriteLine($"Got line {line} but was unable to interpret it as a process id - skipping"); + } }; // As of porting these tests to dotnet/sdk, it's unclear if the below is still needed diff --git a/test/dotnet-run.Tests/GivenDotnetRunUsesTargetExtension.cs b/test/dotnet-run.Tests/GivenDotnetRunUsesTargetExtension.cs new file mode 100644 index 000000000000..df57b33821ec --- /dev/null +++ b/test/dotnet-run.Tests/GivenDotnetRunUsesTargetExtension.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using FluentAssertions.Execution; + +namespace Microsoft.DotNet.Cli.Run.Tests; + +/// +/// These tests cover the behavior of dotnet run when invoking the new ComputeRunArguments target. +/// +public class GivenDotnetRunUsesTargetExtension : SdkTest +{ + + public GivenDotnetRunUsesTargetExtension(ITestOutputHelper log) : base(log) + { + } + + [Fact] + public void ItInvokesTheTargetAndRunsCustomLogic() + { + var testAppName = "DotnetRunTargetExtension"; + var testInstance = _testAssetsManager.CopyTestAsset(testAppName) + .WithSource(); + var testProjectDirectory = testInstance.Path; + + var runResult = new DotnetCommand(Log, "run") + .WithWorkingDirectory(testProjectDirectory) + .Execute(); + + using var scope = new AssertionScope("run outputs"); + + // the run command should run the app in the test project directory, + // so we should both check args and working directory + runResult.Should() + .Pass(); + + runResult.Should() + .HaveStdOutContaining("Args: extended"); + + runResult.Should() + .HaveStdOutContaining($"CWD: {testProjectDirectory}"); + } + + [Fact] + public void ItShowsErrorsDuringCustomLogicExecution() + { + var testAppName = "DotnetRunTargetExtensionWithError"; + var testInstance = _testAssetsManager.CopyTestAsset(testAppName) + .WithSource(); + var testProjectDirectory = testInstance.Path; + + var runResult = new DotnetCommand(Log, "run") + .WithWorkingDirectory(testProjectDirectory) + .Execute(); + + using var scope = new AssertionScope("run outputs"); + + // the run command should run the app in the test project directory, + // so we should both check args and working directory + runResult.Should() + .Fail(); + + runResult.Should() + .HaveStdOutContaining("MYAPP001"); + + runResult.Should() + .HaveStdOutContaining($"MYAPP002"); + + } +} diff --git a/test/dotnet-run.Tests/GivenThatWeCanPassNonProjectFilesToDotnetRun.cs b/test/dotnet-run.Tests/GivenThatWeCanPassNonProjectFilesToDotnetRun.cs index e35ce9fc9dc6..799091eaf0ea 100644 --- a/test/dotnet-run.Tests/GivenThatWeCanPassNonProjectFilesToDotnetRun.cs +++ b/test/dotnet-run.Tests/GivenThatWeCanPassNonProjectFilesToDotnetRun.cs @@ -24,7 +24,7 @@ public void ItFailsWithAnAppropriateErrorMessage() .Should().Fail() .And.HaveStdErrContaining( string.Format( - Tools.Run.LocalizableStrings.RunCommandSpecifiecFileIsNotAValidProject, + Tools.Run.LocalizableStrings.RunCommandSpecifiedFileIsNotAValidProject, slnFullPath)); } } diff --git a/test/dotnet-watch.Tests/Utilities/AwaitableProcess.cs b/test/dotnet-watch.Tests/Utilities/AwaitableProcess.cs index 7784ef3de067..fb79e922dae8 100644 --- a/test/dotnet-watch.Tests/Utilities/AwaitableProcess.cs +++ b/test/dotnet-watch.Tests/Utilities/AwaitableProcess.cs @@ -135,6 +135,11 @@ public async Task> GetAllOutputLinesAsync(CancellationToken cancel private void OnData(object sender, DataReceivedEventArgs args) { var line = args.Data ?? string.Empty; + if (line.StartsWith("\x1b]")) + { + // strip terminal logger progress indicators from line + line = line.StripTerminalLoggerProgressIndicators(); + } WriteTestOutput($"{DateTime.Now}: post: '{line}'"); _source.Post(line); diff --git a/test/dotnet-watch.Tests/Watch/GlobbingAppTests.cs b/test/dotnet-watch.Tests/Watch/GlobbingAppTests.cs index be135de7a984..fbacecc4d6b4 100644 --- a/test/dotnet-watch.Tests/Watch/GlobbingAppTests.cs +++ b/test/dotnet-watch.Tests/Watch/GlobbingAppTests.cs @@ -53,7 +53,7 @@ public async Task DeleteCompiledFile() await AssertCompiledAppDefinedTypes(expected: 1); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/sdk/issues/42921")] public async Task DeleteSourceFolder() { var testAsset = TestAssets.CopyTestAsset(AppName) @@ -70,7 +70,7 @@ public async Task DeleteSourceFolder() await AssertCompiledAppDefinedTypes(expected: 1); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/sdk/issues/42921")] public async Task RenameCompiledFile() { var testAsset = TestAssets.CopyTestAsset(AppName) @@ -87,7 +87,7 @@ public async Task RenameCompiledFile() await App.AssertStarted(); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/sdk/issues/42921")] public async Task ChangeExcludedFile() { var testAsset = TestAssets.CopyTestAsset(AppName) diff --git a/test/dotnet.Tests/ParserTests/RunParserTests.cs b/test/dotnet.Tests/ParserTests/RunParserTests.cs index cc0b4fb33c70..9a039229ce55 100644 --- a/test/dotnet.Tests/ParserTests/RunParserTests.cs +++ b/test/dotnet.Tests/ParserTests/RunParserTests.cs @@ -17,7 +17,7 @@ public RunParserTests(ITestOutputHelper output) [Fact] public void RunParserCanGetArgumentFromDoubleDash() { - var runCommand = RunCommand.FromArgs(new[] { "--", "foo" }); + var runCommand = RunCommand.FromArgs(new[] { "--project", "foo.csproj", "--", "foo" }); runCommand.Args.Single().Should().Be("foo"); } } diff --git a/test/dotnet.Tests/dotnet-msbuild/GivenDotnetBuildInvocation.cs b/test/dotnet.Tests/dotnet-msbuild/GivenDotnetBuildInvocation.cs index 18523ca0bb6b..6580191bc869 100644 --- a/test/dotnet.Tests/dotnet-msbuild/GivenDotnetBuildInvocation.cs +++ b/test/dotnet.Tests/dotnet-msbuild/GivenDotnetBuildInvocation.cs @@ -35,7 +35,7 @@ public class GivenDotnetBuildInvocation : IClassFixturemyoutput -property:_CommandLineDefinedOutputPath=true /ArbitrarySwitchForMSBuild")] [InlineData(new string[] { "/t:CustomTarget" }, "/t:CustomTarget")] - [InlineData(new string[] { "--disable-build-servers" }, "-p:UseRazorBuildServer=false -p:UseSharedCompilation=false /nodeReuse:false")] + [InlineData(new string[] { "--disable-build-servers" }, "--property:UseRazorBuildServer=false --property:UseSharedCompilation=false /nodeReuse:false")] public void MsbuildInvocationIsCorrect(string[] args, string expectedAdditionalArgs) { CommandDirectoryContext.PerformActionWithBasePath(WorkingDirectory, () => diff --git a/test/dotnet.Tests/dotnet-msbuild/GivenDotnetCleanInvocation.cs b/test/dotnet.Tests/dotnet-msbuild/GivenDotnetCleanInvocation.cs index d21f015adf1f..81f788ec00f1 100644 --- a/test/dotnet.Tests/dotnet-msbuild/GivenDotnetCleanInvocation.cs +++ b/test/dotnet.Tests/dotnet-msbuild/GivenDotnetCleanInvocation.cs @@ -32,7 +32,7 @@ public void ItAddsProjectToMsbuildInvocation() [InlineData(new string[] { "--configuration", "" }, "-property:Configuration=")] [InlineData(new string[] { "-v", "diag" }, "-verbosity:diag")] [InlineData(new string[] { "--verbosity", "diag" }, "-verbosity:diag")] - [InlineData(new string[] { "--disable-build-servers" }, "-p:UseRazorBuildServer=false -p:UseSharedCompilation=false /nodeReuse:false")] + [InlineData(new string[] { "--disable-build-servers" }, "--property:UseRazorBuildServer=false --property:UseSharedCompilation=false /nodeReuse:false")] public void MsbuildInvocationIsCorrect(string[] args, string expectedAdditionalArgs) { diff --git a/test/dotnet.Tests/dotnet-msbuild/GivenDotnetMSBuildInvocation.cs b/test/dotnet.Tests/dotnet-msbuild/GivenDotnetMSBuildInvocation.cs index 92a38d40fe13..0d42b37cc48f 100644 --- a/test/dotnet.Tests/dotnet-msbuild/GivenDotnetMSBuildInvocation.cs +++ b/test/dotnet.Tests/dotnet-msbuild/GivenDotnetMSBuildInvocation.cs @@ -12,7 +12,7 @@ public class GivenDotnetMSBuildInvocation : IClassFixture diff --git a/test/dotnet.Tests/dotnet-msbuild/GivenDotnetPackInvocation.cs b/test/dotnet.Tests/dotnet-msbuild/GivenDotnetPackInvocation.cs index caa02df8ca8f..34524348d5d3 100644 --- a/test/dotnet.Tests/dotnet-msbuild/GivenDotnetPackInvocation.cs +++ b/test/dotnet.Tests/dotnet-msbuild/GivenDotnetPackInvocation.cs @@ -31,7 +31,7 @@ public class GivenDotnetPackInvocation : IClassFixture" }, "")] - [InlineData(new string[] { "--disable-build-servers" }, "-p:UseRazorBuildServer=false -p:UseSharedCompilation=false /nodeReuse:false")] + [InlineData(new string[] { "--disable-build-servers" }, "--property:UseRazorBuildServer=false --property:UseSharedCompilation=false /nodeReuse:false")] public void MsbuildInvocationIsCorrect(string[] args, string expectedAdditionalArgs) { diff --git a/test/dotnet.Tests/dotnet-msbuild/GivenDotnetPublishInvocation.cs b/test/dotnet.Tests/dotnet-msbuild/GivenDotnetPublishInvocation.cs index e881abbf6adc..e1009968ddc4 100644 --- a/test/dotnet.Tests/dotnet-msbuild/GivenDotnetPublishInvocation.cs +++ b/test/dotnet.Tests/dotnet-msbuild/GivenDotnetPublishInvocation.cs @@ -38,7 +38,7 @@ public GivenDotnetPublishInvocation(ITestOutputHelper output) [InlineData(new string[] { "--verbosity", "minimal" }, "-verbosity:minimal")] [InlineData(new string[] { "" }, "")] [InlineData(new string[] { "", "" }, " ")] - [InlineData(new string[] { "--disable-build-servers" }, "-p:UseRazorBuildServer=false -p:UseSharedCompilation=false /nodeReuse:false")] + [InlineData(new string[] { "--disable-build-servers" }, "--property:UseRazorBuildServer=false --property:UseSharedCompilation=false /nodeReuse:false")] public void MsbuildInvocationIsCorrect(string[] args, string expectedAdditionalArgs) { CommandDirectoryContext.PerformActionWithBasePath(WorkingDirectory, () => diff --git a/test/dotnet.Tests/dotnet-msbuild/GivenDotnetRestoreInvocation.cs b/test/dotnet.Tests/dotnet-msbuild/GivenDotnetRestoreInvocation.cs index 4795b39886de..7268ad2a633c 100644 --- a/test/dotnet.Tests/dotnet-msbuild/GivenDotnetRestoreInvocation.cs +++ b/test/dotnet.Tests/dotnet-msbuild/GivenDotnetRestoreInvocation.cs @@ -35,7 +35,7 @@ public class GivenDotnetRestoreInvocation : IClassFixture" }, "-property:NuGetLockFilePath=")] - [InlineData(new string[] { "--disable-build-servers" }, "-p:UseRazorBuildServer=false -p:UseSharedCompilation=false /nodeReuse:false")] + [InlineData(new string[] { "--disable-build-servers" }, "--property:UseRazorBuildServer=false --property:UseSharedCompilation=false /nodeReuse:false")] public void MsbuildInvocationIsCorrect(string[] args, string expectedAdditionalArgs) { CommandDirectoryContext.PerformActionWithBasePath(WorkingDirectory, () => diff --git a/test/dotnet.Tests/dotnet-msbuild/GivenDotnetRunInvocation.cs b/test/dotnet.Tests/dotnet-msbuild/GivenDotnetRunInvocation.cs index 7436fa7c7aac..b9ff0569909f 100644 --- a/test/dotnet.Tests/dotnet-msbuild/GivenDotnetRunInvocation.cs +++ b/test/dotnet.Tests/dotnet-msbuild/GivenDotnetRunInvocation.cs @@ -8,29 +8,48 @@ namespace Microsoft.DotNet.Cli.MSBuild.Tests [Collection(TestConstants.UsesStaticTelemetryState)] public class GivenDotnetRunInvocation : IClassFixture { - private static readonly string WorkingDirectory = - TestPathUtilities.FormatAbsolutePath(nameof(GivenDotnetRunInvocation)); + public ITestOutputHelper Log { get; } + + public GivenDotnetRunInvocation(ITestOutputHelper log) + { + Log = log; + } [Theory] - [InlineData(new string[] { "-p:prop1=true" }, new string[] { "-p:prop1=true" })] - [InlineData(new string[] { "--property:prop1=true" }, new string[] { "-p:prop1=true" })] - [InlineData(new string[] { "--property", "prop1=true" }, new string[] { "-p:prop1=true" })] - [InlineData(new string[] { "-p", "prop1=true" }, new string[] { "-p:prop1=true" })] - [InlineData(new string[] { "-p", "prop1=true", "-p", "prop2=false" }, new string[] { "-p:prop1=true", "-p:prop2=false" })] - [InlineData(new string[] { "-p:prop1=true;prop2=false" }, new string[] { "-p:prop1=true;prop2=false" })] - [InlineData(new string[] { "-p", "MyProject.csproj", "-p:prop1=true" }, new string[] { "-p:prop1=true" })] + [InlineData(new string[] { "-p:prop1=true" }, new string[] { "--property:prop1=true" })] + [InlineData(new string[] { "--property:prop1=true" }, new string[] { "--property:prop1=true" })] + [InlineData(new string[] { "--property", "prop1=true" }, new string[] { "--property:prop1=true" })] + [InlineData(new string[] { "-p", "prop1=true" }, new string[] { "--property:prop1=true" })] + [InlineData(new string[] { "-p", "prop1=true", "-p", "prop2=false" }, new string[] { "--property:prop1=true", "--property:prop2=false" })] + [InlineData(new string[] { "-p:prop1=true;prop2=false" }, new string[] { "--property:prop1=true", "--property:prop2=false" })] + [InlineData(new string[] { "-p", "MyProject.csproj", "-p:prop1=true" }, new string[] { "--property:prop1=true" })] // The longhand --property option should never be treated as a project - [InlineData(new string[] { "--property", "MyProject.csproj", "-p:prop1=true" }, new string[] { "-p:MyProject.csproj", "-p:prop1=true" })] - [InlineData(new string[] { "--disable-build-servers" }, new string[] { "-p:UseRazorBuildServer=false", "-p:UseSharedCompilation=false", "/nodeReuse:false" })] + [InlineData(new string[] { "--property", "MyProject.csproj", "-p:prop1=true" }, new string[] { "--property:MyProject.csproj", "--property:prop1=true" })] + [InlineData(new string[] { "--disable-build-servers" }, new string[] { "--property:UseRazorBuildServer=false", "--property:UseSharedCompilation=false", "/nodeReuse:false" })] public void MsbuildInvocationIsCorrect(string[] args, string[] expectedArgs) { - CommandDirectoryContext.PerformActionWithBasePath(WorkingDirectory, () => + + string[] constantRestoreArgs = ["-nologo", "-verbosity:quiet"]; + string[] fullExpectedArgs = constantRestoreArgs.Concat(expectedArgs).ToArray(); + var tam = new TestAssetsManager(Log); + var oldWorkingDirectory = Directory.GetCurrentDirectory(); + var newWorkingDir = tam.CopyTestAsset("HelloWorld", identifier: $"{nameof(MsbuildInvocationIsCorrect)}_{args.GetHashCode()}_{expectedArgs.GetHashCode()}").WithSource().Path; + try + { + Directory.SetCurrentDirectory(newWorkingDir); + + CommandDirectoryContext.PerformActionWithBasePath(newWorkingDir, () => + { + var command = RunCommand.FromArgs(args); + command.RestoreArgs + .Should() + .BeEquivalentTo(fullExpectedArgs); + }); + } + finally { - var command = RunCommand.FromArgs(args); - command.RestoreArgs - .Should() - .BeEquivalentTo(expectedArgs); - }); + Directory.SetCurrentDirectory(oldWorkingDirectory); + } } } } diff --git a/test/dotnet.Tests/dotnet-msbuild/GivenDotnetStoreInvocation.cs b/test/dotnet.Tests/dotnet-msbuild/GivenDotnetStoreInvocation.cs index b5b02a19050f..bec0850f9c75 100644 --- a/test/dotnet.Tests/dotnet-msbuild/GivenDotnetStoreInvocation.cs +++ b/test/dotnet.Tests/dotnet-msbuild/GivenDotnetStoreInvocation.cs @@ -33,7 +33,7 @@ public void ItAddsProjectToMsbuildInvocation(string optionName) [InlineData(new string[] { "--use-current-runtime" }, "-property:UseCurrentRuntimeIdentifier=True")] [InlineData(new string[] { "--ucr" }, "-property:UseCurrentRuntimeIdentifier=True")] [InlineData(new string[] { "--manifest", "one.xml", "--manifest", "two.xml", "--manifest", "three.xml" }, @"-property:AdditionalProjects=one.xml%3Btwo.xml%3Bthree.xml")] - [InlineData(new string[] { "--disable-build-servers" }, "-p:UseRazorBuildServer=false -p:UseSharedCompilation=false /nodeReuse:false")] + [InlineData(new string[] { "--disable-build-servers" }, "--property:UseRazorBuildServer=false --property:UseSharedCompilation=false /nodeReuse:false")] public void MsbuildInvocationIsCorrect(string[] args, string expectedAdditionalArgs) { CommandDirectoryContext.PerformActionWithBasePath(WorkingDirectory, () => diff --git a/test/dotnet.Tests/dotnet-msbuild/GivenDotnetTestInvocation.cs b/test/dotnet.Tests/dotnet-msbuild/GivenDotnetTestInvocation.cs index adcfa610a1a4..875c4a2c360b 100644 --- a/test/dotnet.Tests/dotnet-msbuild/GivenDotnetTestInvocation.cs +++ b/test/dotnet.Tests/dotnet-msbuild/GivenDotnetTestInvocation.cs @@ -12,7 +12,7 @@ public class GivenDotnetTestInvocation : IClassFixture")] + [InlineData(new string[] { "--disable-build-servers" }, "--property:UseRazorBuildServer=false --property:UseSharedCompilation=false /nodeReuse:false -property:VSTestArtifactsProcessingMode=collect -property:VSTestSessionCorrelationId=")] public void MsbuildInvocationIsCorrect(string[] args, string expectedAdditionalArgs) { CommandDirectoryContext.PerformActionWithBasePath(WorkingDirectory, () => diff --git a/test/dotnet.Tests/dotnet.Tests.csproj b/test/dotnet.Tests/dotnet.Tests.csproj index 1a9a46c82936..76d95d0b1c39 100644 --- a/test/dotnet.Tests/dotnet.Tests.csproj +++ b/test/dotnet.Tests/dotnet.Tests.csproj @@ -14,7 +14,7 @@ true MicrosoftAspNetCore false - + $(ArtifactsBinDir)redist\$(Configuration) @@ -28,7 +28,6 @@ - @@ -42,16 +41,17 @@ + - - - + + +