diff --git a/src/Build/Logging/BaseConsoleLogger.cs b/src/Build/Logging/BaseConsoleLogger.cs index 96942d03d55..7dc448e6fb5 100644 --- a/src/Build/Logging/BaseConsoleLogger.cs +++ b/src/Build/Logging/BaseConsoleLogger.cs @@ -12,6 +12,7 @@ using System.Threading; using Microsoft.Build.Evaluation; using Microsoft.Build.Framework; +using Microsoft.Build.Framework.Logging; using Microsoft.Build.Internal; using Microsoft.Build.Shared; using ColorResetter = Microsoft.Build.Logging.ColorResetter; @@ -75,20 +76,9 @@ internal abstract class BaseConsoleLogger : INodeLogger, IStringBuilderProvider /// public void ParseParameters() { - if (Parameters == null) + foreach (var parameter in LoggerParametersHelper.ParseParameters(Parameters)) { - return; - } - - foreach (string parameter in Parameters.Split(parameterDelimiters)) - { - if (string.IsNullOrWhiteSpace(parameter)) - { - continue; - } - - string[] parameterAndValue = parameter.Split(s_parameterValueSplitCharacter); - ApplyParameter(parameterAndValue[0], parameterAndValue.Length > 1 ? parameterAndValue[1] : null); + ApplyParameter(parameter.Item1, parameter.Item2); } } @@ -1038,33 +1028,17 @@ internal virtual bool ApplyParameter(string parameterName, string parameterValue /// private bool ApplyVerbosityParameter(string parameterValue) { - switch (parameterValue.ToUpperInvariant()) + if (LoggerParametersHelper.TryParseVerbosityParameter(parameterValue, out LoggerVerbosity? verbosity)) { - case "Q": - case "QUIET": - Verbosity = LoggerVerbosity.Quiet; - return true; - case "M": - case "MINIMAL": - Verbosity = LoggerVerbosity.Minimal; - return true; - case "N": - case "NORMAL": - Verbosity = LoggerVerbosity.Normal; - return true; - case "D": - case "DETAILED": - Verbosity = LoggerVerbosity.Detailed; - return true; - case "DIAG": - case "DIAGNOSTIC": - Verbosity = LoggerVerbosity.Diagnostic; - return true; - default: - string errorCode; - string helpKeyword; - string message = ResourceUtilities.FormatResourceStringStripCodeAndKeyword(out errorCode, out helpKeyword, "InvalidVerbosity", parameterValue); - throw new LoggerException(message, null, errorCode, helpKeyword); + Verbosity = (LoggerVerbosity)verbosity; + return true; + } + else + { + string errorCode; + string helpKeyword; + string message = ResourceUtilities.FormatResourceStringStripCodeAndKeyword(out errorCode, out helpKeyword, "InvalidVerbosity", parameterValue); + throw new LoggerException(message, null, errorCode, helpKeyword); } } @@ -1135,16 +1109,6 @@ private bool ApplyVerbosityParameter(string parameterValue) internal const string projectSeparatorLine = "__________________________________________________"; - /// - /// Console logger parameters delimiters. - /// - internal static readonly char[] parameterDelimiters = MSBuildConstants.SemicolonChar; - - /// - /// Console logger parameter value split character. - /// - private static readonly char[] s_parameterValueSplitCharacter = MSBuildConstants.EqualsChar; - /// /// When true, accumulate performance numbers. /// diff --git a/src/Build/Logging/ConsoleLogger.cs b/src/Build/Logging/ConsoleLogger.cs index 35886babcca..41bfaf94f50 100644 --- a/src/Build/Logging/ConsoleLogger.cs +++ b/src/Build/Logging/ConsoleLogger.cs @@ -4,6 +4,7 @@ using System; using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Framework; +using Microsoft.Build.Framework.Logging; using Microsoft.Build.Framework.Telemetry; using Microsoft.Build.Shared; using BaseConsoleLogger = Microsoft.Build.BackEnd.Logging.BaseConsoleLogger; @@ -113,7 +114,7 @@ private void InitializeBaseConsoleLogger() bool preferConsoleColor = false; if (!string.IsNullOrEmpty(_parameters)) { - string[] parameterComponents = _parameters.Split(BaseConsoleLogger.parameterDelimiters); + string[] parameterComponents = _parameters.Split(LoggerParametersHelper.s_parameterDelimiters); foreach (string param in parameterComponents) { if (param.Length <= 0) diff --git a/src/Framework/Logging/LoggerParametersHelper.cs b/src/Framework/Logging/LoggerParametersHelper.cs new file mode 100644 index 00000000000..b4f7a843d4f --- /dev/null +++ b/src/Framework/Logging/LoggerParametersHelper.cs @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Build.Shared; + +namespace Microsoft.Build.Framework.Logging +{ + internal static class LoggerParametersHelper + { + // Logger parameters delimiters. + public static readonly char[] s_parameterDelimiters = MSBuildConstants.SemicolonChar; + + // Logger parameter value split character. + public static readonly char[] s_parameterValueSplitCharacter = MSBuildConstants.EqualsChar; + + public static bool TryParseVerbosityParameter(string parameterValue, [NotNullWhen(true)] out LoggerVerbosity? verbosity) + { + switch (parameterValue.ToUpperInvariant()) + { + case "Q": + case "QUIET": + verbosity = LoggerVerbosity.Quiet; + return true; + case "M": + case "MINIMAL": + verbosity = LoggerVerbosity.Minimal; + return true; + case "N": + case "NORMAL": + verbosity = LoggerVerbosity.Normal; + return true; + case "D": + case "DETAILED": + verbosity = LoggerVerbosity.Detailed; + return true; + case "DIAG": + case "DIAGNOSTIC": + verbosity = LoggerVerbosity.Diagnostic; + return true; + default: + verbosity = null; + return false; + } + } + + public static IEnumerable> ParseParameters(string? parametersString) + { + if (parametersString is not null) + { + foreach (string parameter in parametersString.Split(s_parameterDelimiters)) + { + if (string.IsNullOrWhiteSpace(parameter)) + { + continue; + } + + string[] parameterAndValue = parameter.Split(s_parameterValueSplitCharacter); + yield return new Tuple(parameterAndValue[0], parameterAndValue.Length > 1 ? parameterAndValue[1] : null); + } + } + } + } +} diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryDetailedVerbosity_FailedWithErrors.Linux.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryDetailedVerbosity_FailedWithErrors.Linux.verified.txt new file mode 100644 index 00000000000..2a011fd87a4 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryDetailedVerbosity_FailedWithErrors.Linux.verified.txt @@ -0,0 +1,9 @@ +]9;4;3;\The plugin credential provider could not acquire credentials.Authentication may require manual action. Consider re-running the command with --interactive for `dotnet`, /p:NuGetInteractive="true" for MSBuild or removing the -NonInteractive switch for `NuGet` + project failed with 1 error(s) and 1 warning(s) (0.2s) + High importance message! + directory/file(1,2,3,4): warning AA0000: Warning! + directory/file(1,2,3,4): error AA0000: Error! +[?25l +[?25h +Build failed with 1 error(s) and 1 warning(s) in 5.0s +]9;4;0;\ \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryDetailedVerbosity_FailedWithErrors.OSX.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryDetailedVerbosity_FailedWithErrors.OSX.verified.txt new file mode 100644 index 00000000000..94c8f8666eb --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryDetailedVerbosity_FailedWithErrors.OSX.verified.txt @@ -0,0 +1,8 @@ +The plugin credential provider could not acquire credentials.Authentication may require manual action. Consider re-running the command with --interactive for `dotnet`, /p:NuGetInteractive="true" for MSBuild or removing the -NonInteractive switch for `NuGet` + project failed with 1 error(s) and 1 warning(s) (0.2s) + High importance message! + directory/file(1,2,3,4): warning AA0000: Warning! + directory/file(1,2,3,4): error AA0000: Error! +[?25l +[?25h +Build failed with 1 error(s) and 1 warning(s) in 5.0s diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryDetailedVerbosity_FailedWithErrors.Windows.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryDetailedVerbosity_FailedWithErrors.Windows.verified.txt new file mode 100644 index 00000000000..2a011fd87a4 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryDetailedVerbosity_FailedWithErrors.Windows.verified.txt @@ -0,0 +1,9 @@ +]9;4;3;\The plugin credential provider could not acquire credentials.Authentication may require manual action. Consider re-running the command with --interactive for `dotnet`, /p:NuGetInteractive="true" for MSBuild or removing the -NonInteractive switch for `NuGet` + project failed with 1 error(s) and 1 warning(s) (0.2s) + High importance message! + directory/file(1,2,3,4): warning AA0000: Warning! + directory/file(1,2,3,4): error AA0000: Error! +[?25l +[?25h +Build failed with 1 error(s) and 1 warning(s) in 5.0s +]9;4;0;\ \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryDiagnosticVerbosity_FailedWithErrors.Linux.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryDiagnosticVerbosity_FailedWithErrors.Linux.verified.txt new file mode 100644 index 00000000000..2a011fd87a4 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryDiagnosticVerbosity_FailedWithErrors.Linux.verified.txt @@ -0,0 +1,9 @@ +]9;4;3;\The plugin credential provider could not acquire credentials.Authentication may require manual action. Consider re-running the command with --interactive for `dotnet`, /p:NuGetInteractive="true" for MSBuild or removing the -NonInteractive switch for `NuGet` + project failed with 1 error(s) and 1 warning(s) (0.2s) + High importance message! + directory/file(1,2,3,4): warning AA0000: Warning! + directory/file(1,2,3,4): error AA0000: Error! +[?25l +[?25h +Build failed with 1 error(s) and 1 warning(s) in 5.0s +]9;4;0;\ \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryDiagnosticVerbosity_FailedWithErrors.OSX.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryDiagnosticVerbosity_FailedWithErrors.OSX.verified.txt new file mode 100644 index 00000000000..94c8f8666eb --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryDiagnosticVerbosity_FailedWithErrors.OSX.verified.txt @@ -0,0 +1,8 @@ +The plugin credential provider could not acquire credentials.Authentication may require manual action. Consider re-running the command with --interactive for `dotnet`, /p:NuGetInteractive="true" for MSBuild or removing the -NonInteractive switch for `NuGet` + project failed with 1 error(s) and 1 warning(s) (0.2s) + High importance message! + directory/file(1,2,3,4): warning AA0000: Warning! + directory/file(1,2,3,4): error AA0000: Error! +[?25l +[?25h +Build failed with 1 error(s) and 1 warning(s) in 5.0s diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryDiagnosticVerbosity_FailedWithErrors.Windows.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryDiagnosticVerbosity_FailedWithErrors.Windows.verified.txt new file mode 100644 index 00000000000..2a011fd87a4 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryDiagnosticVerbosity_FailedWithErrors.Windows.verified.txt @@ -0,0 +1,9 @@ +]9;4;3;\The plugin credential provider could not acquire credentials.Authentication may require manual action. Consider re-running the command with --interactive for `dotnet`, /p:NuGetInteractive="true" for MSBuild or removing the -NonInteractive switch for `NuGet` + project failed with 1 error(s) and 1 warning(s) (0.2s) + High importance message! + directory/file(1,2,3,4): warning AA0000: Warning! + directory/file(1,2,3,4): error AA0000: Error! +[?25l +[?25h +Build failed with 1 error(s) and 1 warning(s) in 5.0s +]9;4;0;\ \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryMinimalVerbosity_FailedWithErrors.Linux.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryMinimalVerbosity_FailedWithErrors.Linux.verified.txt new file mode 100644 index 00000000000..c0b5f816a67 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryMinimalVerbosity_FailedWithErrors.Linux.verified.txt @@ -0,0 +1,8 @@ +]9;4;3;\The plugin credential provider could not acquire credentials.Authentication may require manual action. Consider re-running the command with --interactive for `dotnet`, /p:NuGetInteractive="true" for MSBuild or removing the -NonInteractive switch for `NuGet` + project failed with 1 error(s) and 1 warning(s) (0.2s) + directory/file(1,2,3,4): warning AA0000: Warning! + directory/file(1,2,3,4): error AA0000: Error! +[?25l +[?25h +Build failed with 1 error(s) and 1 warning(s) in 5.0s +]9;4;0;\ \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryMinimalVerbosity_FailedWithErrors.OSX.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryMinimalVerbosity_FailedWithErrors.OSX.verified.txt new file mode 100644 index 00000000000..f2c3daca5c7 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryMinimalVerbosity_FailedWithErrors.OSX.verified.txt @@ -0,0 +1,7 @@ +The plugin credential provider could not acquire credentials.Authentication may require manual action. Consider re-running the command with --interactive for `dotnet`, /p:NuGetInteractive="true" for MSBuild or removing the -NonInteractive switch for `NuGet` + project failed with 1 error(s) and 1 warning(s) (0.2s) + directory/file(1,2,3,4): warning AA0000: Warning! + directory/file(1,2,3,4): error AA0000: Error! +[?25l +[?25h +Build failed with 1 error(s) and 1 warning(s) in 5.0s diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryMinimalVerbosity_FailedWithErrors.Windows.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryMinimalVerbosity_FailedWithErrors.Windows.verified.txt new file mode 100644 index 00000000000..c0b5f816a67 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryMinimalVerbosity_FailedWithErrors.Windows.verified.txt @@ -0,0 +1,8 @@ +]9;4;3;\The plugin credential provider could not acquire credentials.Authentication may require manual action. Consider re-running the command with --interactive for `dotnet`, /p:NuGetInteractive="true" for MSBuild or removing the -NonInteractive switch for `NuGet` + project failed with 1 error(s) and 1 warning(s) (0.2s) + directory/file(1,2,3,4): warning AA0000: Warning! + directory/file(1,2,3,4): error AA0000: Error! +[?25l +[?25h +Build failed with 1 error(s) and 1 warning(s) in 5.0s +]9;4;0;\ \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryNormalVerbosity_FailedWithErrors.Linux.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryNormalVerbosity_FailedWithErrors.Linux.verified.txt new file mode 100644 index 00000000000..c0b5f816a67 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryNormalVerbosity_FailedWithErrors.Linux.verified.txt @@ -0,0 +1,8 @@ +]9;4;3;\The plugin credential provider could not acquire credentials.Authentication may require manual action. Consider re-running the command with --interactive for `dotnet`, /p:NuGetInteractive="true" for MSBuild or removing the -NonInteractive switch for `NuGet` + project failed with 1 error(s) and 1 warning(s) (0.2s) + directory/file(1,2,3,4): warning AA0000: Warning! + directory/file(1,2,3,4): error AA0000: Error! +[?25l +[?25h +Build failed with 1 error(s) and 1 warning(s) in 5.0s +]9;4;0;\ \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryNormalVerbosity_FailedWithErrors.OSX.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryNormalVerbosity_FailedWithErrors.OSX.verified.txt new file mode 100644 index 00000000000..f2c3daca5c7 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryNormalVerbosity_FailedWithErrors.OSX.verified.txt @@ -0,0 +1,7 @@ +The plugin credential provider could not acquire credentials.Authentication may require manual action. Consider re-running the command with --interactive for `dotnet`, /p:NuGetInteractive="true" for MSBuild or removing the -NonInteractive switch for `NuGet` + project failed with 1 error(s) and 1 warning(s) (0.2s) + directory/file(1,2,3,4): warning AA0000: Warning! + directory/file(1,2,3,4): error AA0000: Error! +[?25l +[?25h +Build failed with 1 error(s) and 1 warning(s) in 5.0s diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryNormalVerbosity_FailedWithErrors.Windows.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryNormalVerbosity_FailedWithErrors.Windows.verified.txt new file mode 100644 index 00000000000..c0b5f816a67 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryNormalVerbosity_FailedWithErrors.Windows.verified.txt @@ -0,0 +1,8 @@ +]9;4;3;\The plugin credential provider could not acquire credentials.Authentication may require manual action. Consider re-running the command with --interactive for `dotnet`, /p:NuGetInteractive="true" for MSBuild or removing the -NonInteractive switch for `NuGet` + project failed with 1 error(s) and 1 warning(s) (0.2s) + directory/file(1,2,3,4): warning AA0000: Warning! + directory/file(1,2,3,4): error AA0000: Error! +[?25l +[?25h +Build failed with 1 error(s) and 1 warning(s) in 5.0s +]9;4;0;\ \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryQuietVerbosity_FailedWithErrors.Linux.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryQuietVerbosity_FailedWithErrors.Linux.verified.txt new file mode 100644 index 00000000000..ed7d3ade9de --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryQuietVerbosity_FailedWithErrors.Linux.verified.txt @@ -0,0 +1,3 @@ +]9;4;3;\directory/file(1,2,3,4): warning AA0000: Warning! +directory/file(1,2,3,4): error AA0000: Error! +]9;4;0;\ \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryQuietVerbosity_FailedWithErrors.OSX.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryQuietVerbosity_FailedWithErrors.OSX.verified.txt new file mode 100644 index 00000000000..b52d4bf957f --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryQuietVerbosity_FailedWithErrors.OSX.verified.txt @@ -0,0 +1,2 @@ +directory/file(1,2,3,4): warning AA0000: Warning! +directory/file(1,2,3,4): error AA0000: Error! diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryQuietVerbosity_FailedWithErrors.Windows.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryQuietVerbosity_FailedWithErrors.Windows.verified.txt new file mode 100644 index 00000000000..ed7d3ade9de --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintBuildSummaryQuietVerbosity_FailedWithErrors.Windows.verified.txt @@ -0,0 +1,3 @@ +]9;4;3;\directory/file(1,2,3,4): warning AA0000: Warning! +directory/file(1,2,3,4): error AA0000: Error! +]9;4;0;\ \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithOverwrittenVerbosity_FailedWithErrors.Linux.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithOverwrittenVerbosity_FailedWithErrors.Linux.verified.txt new file mode 100644 index 00000000000..2a011fd87a4 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithOverwrittenVerbosity_FailedWithErrors.Linux.verified.txt @@ -0,0 +1,9 @@ +]9;4;3;\The plugin credential provider could not acquire credentials.Authentication may require manual action. Consider re-running the command with --interactive for `dotnet`, /p:NuGetInteractive="true" for MSBuild or removing the -NonInteractive switch for `NuGet` + project failed with 1 error(s) and 1 warning(s) (0.2s) + High importance message! + directory/file(1,2,3,4): warning AA0000: Warning! + directory/file(1,2,3,4): error AA0000: Error! +[?25l +[?25h +Build failed with 1 error(s) and 1 warning(s) in 5.0s +]9;4;0;\ \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithOverwrittenVerbosity_FailedWithErrors.OSX.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithOverwrittenVerbosity_FailedWithErrors.OSX.verified.txt new file mode 100644 index 00000000000..94c8f8666eb --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithOverwrittenVerbosity_FailedWithErrors.OSX.verified.txt @@ -0,0 +1,8 @@ +The plugin credential provider could not acquire credentials.Authentication may require manual action. Consider re-running the command with --interactive for `dotnet`, /p:NuGetInteractive="true" for MSBuild or removing the -NonInteractive switch for `NuGet` + project failed with 1 error(s) and 1 warning(s) (0.2s) + High importance message! + directory/file(1,2,3,4): warning AA0000: Warning! + directory/file(1,2,3,4): error AA0000: Error! +[?25l +[?25h +Build failed with 1 error(s) and 1 warning(s) in 5.0s diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithOverwrittenVerbosity_FailedWithErrors.Windows.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithOverwrittenVerbosity_FailedWithErrors.Windows.verified.txt new file mode 100644 index 00000000000..2a011fd87a4 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithOverwrittenVerbosity_FailedWithErrors.Windows.verified.txt @@ -0,0 +1,9 @@ +]9;4;3;\The plugin credential provider could not acquire credentials.Authentication may require manual action. Consider re-running the command with --interactive for `dotnet`, /p:NuGetInteractive="true" for MSBuild or removing the -NonInteractive switch for `NuGet` + project failed with 1 error(s) and 1 warning(s) (0.2s) + High importance message! + directory/file(1,2,3,4): warning AA0000: Warning! + directory/file(1,2,3,4): error AA0000: Error! +[?25l +[?25h +Build failed with 1 error(s) and 1 warning(s) in 5.0s +]9;4;0;\ \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithTaskCommandLineEventArgs_Succeeded.Linux.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithTaskCommandLineEventArgs_Succeeded.Linux.verified.txt new file mode 100644 index 00000000000..ea19717537f --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithTaskCommandLineEventArgs_Succeeded.Linux.verified.txt @@ -0,0 +1,6 @@ +]9;4;3;\ project succeeded (0.2s) + Task Command Line. +[?25l +[?25h +Build succeeded in 5.0s +]9;4;0;\ \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithTaskCommandLineEventArgs_Succeeded.OSX.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithTaskCommandLineEventArgs_Succeeded.OSX.verified.txt new file mode 100644 index 00000000000..809a4f0a0eb --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithTaskCommandLineEventArgs_Succeeded.OSX.verified.txt @@ -0,0 +1,5 @@ + project succeeded (0.2s) + Task Command Line. +[?25l +[?25h +Build succeeded in 5.0s diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithTaskCommandLineEventArgs_Succeeded.Windows.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithTaskCommandLineEventArgs_Succeeded.Windows.verified.txt new file mode 100644 index 00000000000..ea19717537f --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithTaskCommandLineEventArgs_Succeeded.Windows.verified.txt @@ -0,0 +1,6 @@ +]9;4;3;\ project succeeded (0.2s) + Task Command Line. +[?25l +[?25h +Build succeeded in 5.0s +]9;4;0;\ \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithoutTaskCommandLineEventArgs_Succeeded.Linux.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithoutTaskCommandLineEventArgs_Succeeded.Linux.verified.txt new file mode 100644 index 00000000000..d5e6b72e894 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithoutTaskCommandLineEventArgs_Succeeded.Linux.verified.txt @@ -0,0 +1,4 @@ +]9;4;3;\[?25l +[?25h +Build succeeded in 5.0s +]9;4;0;\ \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithoutTaskCommandLineEventArgs_Succeeded.OSX.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithoutTaskCommandLineEventArgs_Succeeded.OSX.verified.txt new file mode 100644 index 00000000000..4d414bf90bf --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithoutTaskCommandLineEventArgs_Succeeded.OSX.verified.txt @@ -0,0 +1,3 @@ +[?25l +[?25h +Build succeeded in 5.0s diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithoutTaskCommandLineEventArgs_Succeeded.Windows.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithoutTaskCommandLineEventArgs_Succeeded.Windows.verified.txt new file mode 100644 index 00000000000..d5e6b72e894 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintSummaryWithoutTaskCommandLineEventArgs_Succeeded.Windows.verified.txt @@ -0,0 +1,4 @@ +]9;4;3;\[?25l +[?25h +Build succeeded in 5.0s +]9;4;0;\ \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintTestSummaryNormalVerbosity_Succeeded.Linux.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintTestSummaryNormalVerbosity_Succeeded.Linux.verified.txt new file mode 100644 index 00000000000..34897dfd415 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintTestSummaryNormalVerbosity_Succeeded.Linux.verified.txt @@ -0,0 +1,6 @@ +]9;4;3;\ project test succeeded (0.2s) +[?25l +[?25h +Build succeeded in 5.0s +Test run failed. Total: 10 Failed: 1 Passed: 7 Skipped: 2, Duration: 1.0s +]9;4;0;\ \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintTestSummaryNormalVerbosity_Succeeded.OSX.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintTestSummaryNormalVerbosity_Succeeded.OSX.verified.txt new file mode 100644 index 00000000000..6615c0644e7 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintTestSummaryNormalVerbosity_Succeeded.OSX.verified.txt @@ -0,0 +1,5 @@ + project test succeeded (0.2s) +[?25l +[?25h +Build succeeded in 5.0s +Test run failed. Total: 10 Failed: 1 Passed: 7 Skipped: 2, Duration: 1.0s diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintTestSummaryNormalVerbosity_Succeeded.Windows.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintTestSummaryNormalVerbosity_Succeeded.Windows.verified.txt new file mode 100644 index 00000000000..34897dfd415 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintTestSummaryNormalVerbosity_Succeeded.Windows.verified.txt @@ -0,0 +1,6 @@ +]9;4;3;\ project test succeeded (0.2s) +[?25l +[?25h +Build succeeded in 5.0s +Test run failed. Total: 10 Failed: 1 Passed: 7 Skipped: 2, Duration: 1.0s +]9;4;0;\ \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintTestSummaryQuietVerbosity_Succeeded.Linux.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintTestSummaryQuietVerbosity_Succeeded.Linux.verified.txt new file mode 100644 index 00000000000..1631c824d25 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintTestSummaryQuietVerbosity_Succeeded.Linux.verified.txt @@ -0,0 +1 @@ +]9;4;3;\]9;4;0;\ \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintTestSummaryQuietVerbosity_Succeeded.OSX.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintTestSummaryQuietVerbosity_Succeeded.OSX.verified.txt new file mode 100644 index 00000000000..c1b8d743e34 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintTestSummaryQuietVerbosity_Succeeded.OSX.verified.txt @@ -0,0 +1 @@ +emptyString \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintTestSummaryQuietVerbosity_Succeeded.Windows.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintTestSummaryQuietVerbosity_Succeeded.Windows.verified.txt new file mode 100644 index 00000000000..1631c824d25 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.PrintTestSummaryQuietVerbosity_Succeeded.Windows.verified.txt @@ -0,0 +1 @@ +]9;4;3;\]9;4;0;\ \ No newline at end of file diff --git a/src/MSBuild.UnitTests/TerminalLogger_Tests.cs b/src/MSBuild.UnitTests/TerminalLogger_Tests.cs index d983a3a5596..955610abad2 100644 --- a/src/MSBuild.UnitTests/TerminalLogger_Tests.cs +++ b/src/MSBuild.UnitTests/TerminalLogger_Tests.cs @@ -30,6 +30,12 @@ public class TerminalLogger_Tests : IEventSource, IDisposable { private const int _nodeCount = 8; private const string _eventSender = "Test"; + + private const string _immediateMessageString = + "The plugin credential provider could not acquire credentials." + + "Authentication may require manual action. Consider re-running the command with --interactive for `dotnet`, " + + "/p:NuGetInteractive=\"true\" for MSBuild or removing the -NonInteractive switch for `NuGet`"; + private readonly string _projectFile = NativeMethods.IsUnixLike ? "/src/project.proj" : @"C:\src\project.proj"; private readonly string _projectFile2 = NativeMethods.IsUnixLike ? "/src/project2.proj" : @"C:\src\project2.proj"; @@ -39,8 +45,11 @@ public class TerminalLogger_Tests : IEventSource, IDisposable private readonly TerminalLogger _terminallogger; private readonly DateTime _buildStartTime = new DateTime(2023, 3, 30, 16, 30, 0); + private readonly DateTime _targetStartTime = new DateTime(2023, 3, 30, 16, 30, 1); + private readonly DateTime _messageTime = new DateTime(2023, 3, 30, 16, 30, 2); private readonly DateTime _buildFinishTime = new DateTime(2023, 3, 30, 16, 30, 5); + private VerifySettings _settings = new(); private readonly CultureInfo _originalCulture = Thread.CurrentThread.CurrentCulture; @@ -139,7 +148,7 @@ private ProjectFinishedEventArgs MakeProjectFinishedEventArgs(string projectFile private TargetStartedEventArgs MakeTargetStartedEventArgs(string projectFile, string targetName) { - return new TargetStartedEventArgs("", "", targetName, projectFile, targetFile: projectFile) + return new TargetStartedEventArgs("", "", targetName, projectFile, targetFile: projectFile, String.Empty, TargetBuiltReason.None, _targetStartTime) { BuildEventContext = MakeBuildEventContext(), }; @@ -177,14 +186,31 @@ private BuildWarningEventArgs MakeWarningEventArgs(string warning) }; } - private BuildMessageEventArgs MakeMessageEventArgs(string message) + private BuildMessageEventArgs MakeMessageEventArgs(string message, MessageImportance importance) + { + return new BuildMessageEventArgs(message, "keyword", null, importance) + { + BuildEventContext = MakeBuildEventContext(), + }; + } + + private BuildMessageEventArgs MakeTaskCommandLineEventArgs(string message, MessageImportance importance) { - return new BuildMessageEventArgs(message, "keyword", null, MessageImportance.High) + return new TaskCommandLineEventArgs(message, "Task", importance) { BuildEventContext = MakeBuildEventContext(), }; } + private BuildMessageEventArgs MakeExtendedMessageEventArgs(string message, MessageImportance importance, string extendedType, Dictionary? extendedMetadata) + { + return new ExtendedBuildMessageEventArgs(extendedType, message, "keyword", null, importance, _messageTime) + { + BuildEventContext = MakeBuildEventContext(), + ExtendedMetadata = extendedMetadata + }; + } + private BuildErrorEventArgs MakeErrorEventArgs(string error) { return new BuildErrorEventArgs("", "AA0000", "directory/file", 1, 2, 3, 4, error, null, null) @@ -214,6 +240,24 @@ private void InvokeLoggerCallbacksForSimpleProject(bool succeeded, Action additi BuildFinished?.Invoke(_eventSender, MakeBuildFinishedEventArgs(succeeded)); } + private void InvokeLoggerCallbacksForTestProject(bool succeeded, Action additionalCallbacks) + { + BuildStarted?.Invoke(_eventSender, MakeBuildStartedEventArgs()); + ProjectStarted?.Invoke(_eventSender, MakeProjectStartedEventArgs(_projectFile)); + + TargetStarted?.Invoke(_eventSender, MakeTargetStartedEventArgs(_projectFile, "_TestRunStart")); + TaskStarted?.Invoke(_eventSender, MakeTaskStartedEventArgs(_projectFile, "Task")); + + additionalCallbacks(); + + TaskFinished?.Invoke(_eventSender, MakeTaskFinishedEventArgs(_projectFile, "Task", succeeded)); + TargetFinished?.Invoke(_eventSender, MakeTargetFinishedEventArgs(_projectFile, "_TestRunStart", succeeded)); + + ProjectFinished?.Invoke(_eventSender, MakeProjectFinishedEventArgs(_projectFile, succeeded)); + + BuildFinished?.Invoke(_eventSender, MakeBuildFinishedEventArgs(succeeded)); + } + private void InvokeLoggerCallbacksForTwoProjects(bool succeeded, Action additionalCallbacks, Action additionalCallbacks2) { BuildStarted?.Invoke(_eventSender, MakeBuildStartedEventArgs()); @@ -281,10 +325,7 @@ public Task PrintImmediateMessage_Success() { InvokeLoggerCallbacksForSimpleProject(succeeded: true, () => { - MessageRaised?.Invoke(_eventSender, MakeMessageEventArgs( - "The plugin credential provider could not acquire credentials." + - "Authentication may require manual action. Consider re-running the command with --interactive for `dotnet`, " + - "/p:NuGetInteractive=\"true\" for MSBuild or removing the -NonInteractive switch for `NuGet`")); + MessageRaised?.Invoke(_eventSender, MakeMessageEventArgs(_immediateMessageString, MessageImportance.High)); }); return Verify(_outputWriter.ToString(), _settings).UniqueForOSPlatform(); @@ -295,7 +336,7 @@ public Task PrintImmediateMessage_Skipped() { InvokeLoggerCallbacksForSimpleProject(succeeded: true, () => { - MessageRaised?.Invoke(_eventSender, MakeMessageEventArgs("--anycustomarg")); + MessageRaised?.Invoke(_eventSender, MakeMessageEventArgs("--anycustomarg", MessageImportance.High)); }); return Verify(_outputWriter.ToString(), _settings).UniqueForOSPlatform(); @@ -388,6 +429,142 @@ public Task PrintBuildSummary_2Projects_FailedWithErrorsAndWarnings() #endregion + private void CallAllTypesOfMessagesWarningAndError() + { + MessageRaised?.Invoke(_eventSender, MakeMessageEventArgs(_immediateMessageString, MessageImportance.High)); + MessageRaised?.Invoke(_eventSender, MakeMessageEventArgs("High importance message!", MessageImportance.High)); + MessageRaised?.Invoke(_eventSender, MakeMessageEventArgs("Normal importance message!", MessageImportance.Normal)); + MessageRaised?.Invoke(_eventSender, MakeMessageEventArgs("Low importance message!", MessageImportance.Low)); + WarningRaised?.Invoke(_eventSender, MakeWarningEventArgs("Warning!")); + ErrorRaised?.Invoke(_eventSender, MakeErrorEventArgs("Error!")); + } + + private void CallAllTypesOfTestMessages() + { + MessageRaised?.Invoke(_eventSender, MakeExtendedMessageEventArgs( + "Test passed.", + MessageImportance.High, + "TLTESTPASSED", + new Dictionary() { { "displayName", "testName1" }, { "localizedResult", "passed" } })); + MessageRaised?.Invoke(_eventSender, MakeExtendedMessageEventArgs( + "Test skipped.", + MessageImportance.High, + "TLTESTSKIPPED", + new Dictionary() { { "displayName", "testName2" }, { "localizedResult", "skipped" } })); + MessageRaised?.Invoke(_eventSender, MakeExtendedMessageEventArgs( + "Test results.", + MessageImportance.High, + "TLTESTFINISH", + new Dictionary() { { "total", "10" }, { "passed", "7" }, { "skipped", "2" }, { "failed", "1" } })); + } + + [Fact] + public Task PrintBuildSummaryQuietVerbosity_FailedWithErrors() + { + _terminallogger.Verbosity = LoggerVerbosity.Quiet; + InvokeLoggerCallbacksForSimpleProject(succeeded: false, CallAllTypesOfMessagesWarningAndError); + + return Verify(_outputWriter.ToString(), _settings).UniqueForOSPlatform(); + } + + + [Fact] + public Task PrintBuildSummaryMinimalVerbosity_FailedWithErrors() + { + _terminallogger.Verbosity = LoggerVerbosity.Minimal; + InvokeLoggerCallbacksForSimpleProject(succeeded: false, CallAllTypesOfMessagesWarningAndError); + + return Verify(_outputWriter.ToString(), _settings).UniqueForOSPlatform(); + } + + [Fact] + public Task PrintBuildSummaryNormalVerbosity_FailedWithErrors() + { + _terminallogger.Verbosity = LoggerVerbosity.Normal; + InvokeLoggerCallbacksForSimpleProject(succeeded: false, CallAllTypesOfMessagesWarningAndError); + + return Verify(_outputWriter.ToString(), _settings).UniqueForOSPlatform(); + } + + [Fact] + public Task PrintBuildSummaryDetailedVerbosity_FailedWithErrors() + { + _terminallogger.Verbosity = LoggerVerbosity.Detailed; + InvokeLoggerCallbacksForSimpleProject(succeeded: false, CallAllTypesOfMessagesWarningAndError); + + return Verify(_outputWriter.ToString(), _settings).UniqueForOSPlatform(); + } + + + [Fact] + public Task PrintBuildSummaryDiagnosticVerbosity_FailedWithErrors() + { + _terminallogger.Verbosity = LoggerVerbosity.Diagnostic; + InvokeLoggerCallbacksForSimpleProject(succeeded: false, CallAllTypesOfMessagesWarningAndError); + + return Verify(_outputWriter.ToString(), _settings).UniqueForOSPlatform(); + } + + [Fact] + public Task PrintTestSummaryNormalVerbosity_Succeeded() + { + _terminallogger.Verbosity = LoggerVerbosity.Normal; + InvokeLoggerCallbacksForTestProject(succeeded: true, CallAllTypesOfTestMessages); + + return Verify(_outputWriter.ToString(), _settings).UniqueForOSPlatform(); + } + + [Fact] + public Task PrintTestSummaryQuietVerbosity_Succeeded() + { + _terminallogger.Verbosity = LoggerVerbosity.Quiet; + InvokeLoggerCallbacksForTestProject(succeeded: true, CallAllTypesOfTestMessages); + + return Verify(_outputWriter.ToString(), _settings).UniqueForOSPlatform(); + } + + [Fact] + public Task PrintSummaryWithOverwrittenVerbosity_FailedWithErrors() + { + _terminallogger.Verbosity = LoggerVerbosity.Minimal; + _terminallogger.Parameters = "v=diag"; + _terminallogger.ParseParameters(); + + InvokeLoggerCallbacksForSimpleProject(succeeded: false, CallAllTypesOfMessagesWarningAndError); + + return Verify(_outputWriter.ToString(), _settings).UniqueForOSPlatform(); + } + + [Fact] + public Task PrintSummaryWithTaskCommandLineEventArgs_Succeeded() + { + _terminallogger.Verbosity = LoggerVerbosity.Detailed; + _terminallogger.Parameters = "SHOWCOMMANDLINE=on"; + _terminallogger.ParseParameters(); + + InvokeLoggerCallbacksForSimpleProject(succeeded: true, () => + { + MessageRaised?.Invoke(_eventSender, MakeTaskCommandLineEventArgs("Task Command Line.", MessageImportance.High)); + }); + + return Verify(_outputWriter.ToString(), _settings).UniqueForOSPlatform(); + } + + [Fact] + public Task PrintSummaryWithoutTaskCommandLineEventArgs_Succeeded() + { + _terminallogger.Verbosity = LoggerVerbosity.Detailed; + _terminallogger.Parameters = "SHOWCOMMANDLINE=off"; + _terminallogger.ParseParameters(); + + InvokeLoggerCallbacksForSimpleProject(succeeded: true, () => + { + MessageRaised?.Invoke(_eventSender, MakeTaskCommandLineEventArgs("Task Command Line.", MessageImportance.High)); + }); + + return Verify(_outputWriter.ToString(), _settings).UniqueForOSPlatform(); + } + [Fact] public void DisplayNodesShowsCurrent() { diff --git a/src/MSBuild/MSBuild.csproj b/src/MSBuild/MSBuild.csproj index 0415842c6b7..f396ca22cfe 100644 --- a/src/MSBuild/MSBuild.csproj +++ b/src/MSBuild/MSBuild.csproj @@ -128,6 +128,7 @@ + diff --git a/src/MSBuild/TerminalLogger/MessageSeverity.cs b/src/MSBuild/TerminalLogger/MessageSeverity.cs index 07aa9058be3..9f374e292dd 100644 --- a/src/MSBuild/TerminalLogger/MessageSeverity.cs +++ b/src/MSBuild/TerminalLogger/MessageSeverity.cs @@ -6,4 +6,4 @@ namespace Microsoft.Build.Logging.TerminalLogger; /// /// Enumerates the supported message severities. /// -internal enum MessageSeverity { Warning, Error } +internal enum MessageSeverity { Message, Warning, Error } diff --git a/src/MSBuild/TerminalLogger/TerminalLogger.cs b/src/MSBuild/TerminalLogger/TerminalLogger.cs index 8cc93b0b588..dd13fbe28aa 100644 --- a/src/MSBuild/TerminalLogger/TerminalLogger.cs +++ b/src/MSBuild/TerminalLogger/TerminalLogger.cs @@ -10,10 +10,13 @@ using Microsoft.Build.Shared; using System.Text.RegularExpressions; using System.Diagnostics; +using Microsoft.Build.Framework.Logging; #if NET7_0_OR_GREATER using System.Diagnostics.CodeAnalysis; using System.Globalization; + + #endif #if NETFRAMEWORK using Microsoft.IO; @@ -197,6 +200,11 @@ public ProjectContext(BuildEventContext context) /// private DateTime? _testEndTime; + /// + /// Whether to show TaskCommandLineEventArgs high-priority messages. + /// + private bool _showCommandLine = false; + /// /// Default constructor, used by the MSBuild logger infra. /// @@ -205,6 +213,12 @@ public TerminalLogger() Terminal = new Terminal(); } + public TerminalLogger(LoggerVerbosity verbosity) + : this() + { + Verbosity = verbosity; + } + /// /// Internal constructor accepting a custom for testing. /// @@ -217,13 +231,10 @@ internal TerminalLogger(ITerminal terminal) #region INodeLogger implementation /// - public LoggerVerbosity Verbosity { get => LoggerVerbosity.Minimal; set { } } + public LoggerVerbosity Verbosity { get; set; } = LoggerVerbosity.Minimal; /// - public string? Parameters - { - get => ""; set { } - } + public string? Parameters { get; set; } = null; /// public void Initialize(IEventSource eventSource, int nodeCount) @@ -237,6 +248,8 @@ public void Initialize(IEventSource eventSource, int nodeCount) /// public void Initialize(IEventSource eventSource) { + ParseParameters(); + eventSource.BuildStarted += BuildStarted; eventSource.BuildFinished += BuildFinished; eventSource.ProjectStarted += ProjectStarted; @@ -255,6 +268,76 @@ public void Initialize(IEventSource eventSource) } } + /// + /// Parses out the logger parameters from the Parameters string. + /// + public void ParseParameters() + { + foreach (var parameter in LoggerParametersHelper.ParseParameters(Parameters)) + { + ApplyParameter(parameter.Item1, parameter.Item2); + } + } + + /// + /// Apply a terminal logger parameter. + /// parameterValue may be null, if there is no parameter value. + /// + /// + /// If verbosity parameter value is not correct, throws an exception. Other incorrect parameter values are disregarded. + /// + private void ApplyParameter(string parameterName, string? parameterValue) + { + ErrorUtilities.VerifyThrowArgumentNull(parameterName, nameof(parameterName)); + + switch (parameterName.ToUpperInvariant()) + { + case "V": + case "VERBOSITY": + ApplyVerbosityParameter(parameterValue); + break; + case "SHOWCOMMANDLINE": + TryApplyShowCommandLineParameter(parameterValue); + break; + } + } + + /// + /// Apply the verbosity value + /// + private void ApplyVerbosityParameter(string? parameterValue) + { + if (parameterValue is not null && LoggerParametersHelper.TryParseVerbosityParameter(parameterValue, out LoggerVerbosity? verbosity)) + { + Verbosity = (LoggerVerbosity)verbosity; + } + else + { + string errorCode; + string helpKeyword; + string message = ResourceUtilities.FormatResourceStringStripCodeAndKeyword(out errorCode, out helpKeyword, "InvalidVerbosity", parameterValue); + throw new LoggerException(message, null, errorCode, helpKeyword); + } + } + + /// + /// Apply the show command Line value + /// + private bool TryApplyShowCommandLineParameter(string? parameterValue) + { + if (String.IsNullOrEmpty(parameterValue)) + { + _showCommandLine = true; + } + else + { + return ConversionUtilities.TryConvertStringToBool(parameterValue, out _showCommandLine); + } + + return true; + } + + /// public void Shutdown() { @@ -299,43 +382,46 @@ private void BuildFinished(object sender, BuildFinishedEventArgs e) Terminal.BeginUpdate(); try - { - string duration = (e.Timestamp - _buildStartTime).TotalSeconds.ToString("F1"); - string buildResult = RenderBuildResult(e.Succeeded, _buildErrorsCount, _buildWarningsCount); - - Terminal.WriteLine(""); - if (_restoreFailed) + { + if (Verbosity > LoggerVerbosity.Quiet) { - Terminal.WriteLine(ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("RestoreCompleteWithMessage", - buildResult, - duration)); - } - else - { - Terminal.WriteLine(ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("BuildFinished", - buildResult, - duration)); - } + string duration = (e.Timestamp - _buildStartTime).TotalSeconds.ToString("F1"); + string buildResult = RenderBuildResult(e.Succeeded, _buildErrorsCount, _buildWarningsCount); - if (_testRunSummaries.Any()) - { - var total = _testRunSummaries.Sum(t => t.Total); - var failed = _testRunSummaries.Sum(t => t.Failed); - var passed = _testRunSummaries.Sum(t => t.Passed); - var skipped = _testRunSummaries.Sum(t => t.Skipped); - var testDuration = (_testStartTime != null && _testEndTime != null ? (_testEndTime - _testStartTime).Value.TotalSeconds : 0).ToString("F1"); - - var colorizedResult = _testRunSummaries.Any(t => t.Failed > 0) || (_buildErrorsCount > 0) - ? AnsiCodes.Colorize(ResourceUtilities.GetResourceString("BuildResult_Failed"), TerminalColor.Red) - : AnsiCodes.Colorize(ResourceUtilities.GetResourceString("BuildResult_Succeeded"), TerminalColor.Green); - - Terminal.WriteLine(ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("TestSummary", - colorizedResult, - total, - failed, - passed, - skipped, - testDuration)); + Terminal.WriteLine(""); + if (_restoreFailed) + { + Terminal.WriteLine(ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("RestoreCompleteWithMessage", + buildResult, + duration)); + } + else + { + Terminal.WriteLine(ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("BuildFinished", + buildResult, + duration)); + } + + if (_testRunSummaries.Any()) + { + var total = _testRunSummaries.Sum(t => t.Total); + var failed = _testRunSummaries.Sum(t => t.Failed); + var passed = _testRunSummaries.Sum(t => t.Passed); + var skipped = _testRunSummaries.Sum(t => t.Skipped); + var testDuration = (_testStartTime != null && _testEndTime != null ? (_testEndTime - _testStartTime).Value.TotalSeconds : 0).ToString("F1"); + + var colorizedResult = _testRunSummaries.Any(t => t.Failed > 0) || (_buildErrorsCount > 0) + ? AnsiCodes.Colorize(ResourceUtilities.GetResourceString("BuildResult_Failed"), TerminalColor.Red) + : AnsiCodes.Colorize(ResourceUtilities.GetResourceString("BuildResult_Succeeded"), TerminalColor.Green); + + Terminal.WriteLine(ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("TestSummary", + colorizedResult, + total, + failed, + passed, + skipped, + testDuration)); + } } } finally @@ -404,6 +490,12 @@ private void ProjectFinished(object sender, ProjectFinishedEventArgs e) UpdateNodeStatus(buildEventContext, null); } + // Continue execution and add project summary to the static part of the Console only if verbosity is higher than Quiet. + if (Verbosity <= LoggerVerbosity.Quiet) + { + return; + } + ProjectContext c = new(buildEventContext); if (_projects.TryGetValue(c, out Project? project)) @@ -650,6 +742,7 @@ private void MessageRaised(object sender, BuildMessageEventArgs e) if (message is not null && e.Importance == MessageImportance.High) { var hasProject = _projects.TryGetValue(new ProjectContext(buildEventContext), out Project? project); + // Detect project output path by matching high-importance messages against the "$(MSBuildProjectName) -> ..." // pattern used by the CopyFilesToOutputDirectory target. int index = message.IndexOf(FilePathPattern, StringComparison.Ordinal); @@ -661,14 +754,31 @@ private void MessageRaised(object sender, BuildMessageEventArgs e) { ReadOnlyMemory outputPath = e.Message.AsMemory().Slice(index + 4); project!.OutputPath = outputPath; + return; } } - if (IsImmediateMessage(message)) + if (Verbosity > LoggerVerbosity.Quiet) { - RenderImmediateMessage(message); + // Show immediate messages to the user. + if (IsImmediateMessage(message)) + { + RenderImmediateMessage(message); + return; + } + if (e.Code == "NETSDK1057" && !_loggedPreviewMessage) + { + // The SDK will log the high-pri "not-a-warning" message NETSDK1057 + // when it's a preview version up to MaxCPUCount times, but that's + // an implementation detail--the user cares about at most one. + + RenderImmediateMessage(message); + _loggedPreviewMessage = true; + return; + } } - else if (hasProject && project!.IsTestProject) + + if (hasProject && project!.IsTestProject) { var node = _nodes[NodeIndexForContext(buildEventContext)]; @@ -699,30 +809,45 @@ private void MessageRaised(object sender, BuildMessageEventArgs e) case "TLTESTFINISH": { - _ = int.TryParse(extendedMessage.ExtendedMetadata!["total"]!, out int total); - _ = int.TryParse(extendedMessage.ExtendedMetadata!["passed"]!, out int passed); - _ = int.TryParse(extendedMessage.ExtendedMetadata!["skipped"]!, out int skipped); - _ = int.TryParse(extendedMessage.ExtendedMetadata!["failed"]!, out int failed); + // Collect test run summary. + if (Verbosity > LoggerVerbosity.Quiet) + { + _ = int.TryParse(extendedMessage.ExtendedMetadata!["total"]!, out int total); + _ = int.TryParse(extendedMessage.ExtendedMetadata!["passed"]!, out int passed); + _ = int.TryParse(extendedMessage.ExtendedMetadata!["skipped"]!, out int skipped); + _ = int.TryParse(extendedMessage.ExtendedMetadata!["failed"]!, out int failed); - _testRunSummaries.Add(new TestSummary(total, passed, skipped, failed)); + _testRunSummaries.Add(new TestSummary(total, passed, skipped, failed)); - _testEndTime = _testEndTime == null - ? e.Timestamp - : e.Timestamp > _testEndTime - ? e.Timestamp : _testEndTime; + _testEndTime = _testEndTime == null + ? e.Timestamp + : e.Timestamp > _testEndTime + ? e.Timestamp : _testEndTime; + } + break; } } + return; } } - else if (e.Code == "NETSDK1057" && !_loggedPreviewMessage) + + if (Verbosity > LoggerVerbosity.Normal) { - // The SDK will log the high-pri "not-a-warning" message NETSDK1057 - // when it's a preview version up to MaxCPUCount times, but that's - // an implementation detail--the user cares about at most one. + if (e is TaskCommandLineEventArgs && !_showCommandLine) + { + return; + } - RenderImmediateMessage(message); - _loggedPreviewMessage = true; + if (hasProject) + { + project!.AddBuildMessage(MessageSeverity.Message, message); + } + else + { + // Display messages reported by MSBuild, even if it's not tracked in _projects collection. + RenderImmediateMessage(message); + } } } } @@ -744,7 +869,9 @@ private void WarningRaised(object sender, BuildWarningEventArgs e) columnNumber: e.ColumnNumber, endColumnNumber: e.EndColumnNumber); - if (buildEventContext is not null && _projects.TryGetValue(new ProjectContext(buildEventContext), out Project? project)) + if (buildEventContext is not null + && _projects.TryGetValue(new ProjectContext(buildEventContext), out Project? project) + && Verbosity > LoggerVerbosity.Quiet) { if (IsImmediateMessage(message)) { @@ -755,7 +882,7 @@ private void WarningRaised(object sender, BuildWarningEventArgs e) } else { - // It is necessary to display warning messages reported by MSBuild, even if it's not tracked in _projects collection. + // It is necessary to display warning messages reported by MSBuild, even if it's not tracked in _projects collection or the verbosity is Quiet. RenderImmediateMessage(message); _buildWarningsCount++; } @@ -790,13 +917,15 @@ private void ErrorRaised(object sender, BuildErrorEventArgs e) columnNumber: e.ColumnNumber, endColumnNumber: e.EndColumnNumber); - if (buildEventContext is not null && _projects.TryGetValue(new ProjectContext(buildEventContext), out Project? project)) + if (buildEventContext is not null + && _projects.TryGetValue(new ProjectContext(buildEventContext), out Project? project) + && Verbosity > LoggerVerbosity.Quiet) { project.AddBuildMessage(MessageSeverity.Error, message); } else { - // It is necessary to display error messages reported by MSBuild, even if it's not tracked in _projects collection. + // It is necessary to display error messages reported by MSBuild, even if it's not tracked in _projects collection or the verbosity is Quiet. RenderImmediateMessage(message); _buildErrorsCount++; } diff --git a/src/MSBuild/XMake.cs b/src/MSBuild/XMake.cs index ad65e97d06c..213b842ae6a 100644 --- a/src/MSBuild/XMake.cs +++ b/src/MSBuild/XMake.cs @@ -3757,7 +3757,7 @@ private static ILogger[] ProcessLoggingSwitches( } else if (terminalloggerOptIn) { - ProcessTerminalLogger(noConsoleLogger, aggregatedTerminalLoggerParameters, distributedLoggerRecords, cpuCount, loggers); + ProcessTerminalLogger(noConsoleLogger, aggregatedTerminalLoggerParameters, distributedLoggerRecords, verbosity, cpuCount, loggers); } else { @@ -3936,13 +3936,14 @@ internal static void ProcessConsoleLoggerSwitch( private static void ProcessTerminalLogger(bool noConsoleLogger, string aggregatedLoggerParameters, List distributedLoggerRecords, + LoggerVerbosity verbosity, int cpuCount, List loggers) { if (!noConsoleLogger) { // A central logger will be created for both single proc and multiproc. - TerminalLogger logger = new TerminalLogger() + TerminalLogger logger = new TerminalLogger(verbosity) { Parameters = aggregatedLoggerParameters };