From 21f8e47c431777f36a3b7db11fabecd176a09044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Tue, 30 Jan 2024 17:36:15 +0100 Subject: [PATCH 01/19] First take --- src/MSBuild/Resources/Strings.resx | 432 ++++++++++-------- src/MSBuild/Resources/xlf/Strings.cs.xlf | 23 + src/MSBuild/Resources/xlf/Strings.de.xlf | 23 + src/MSBuild/Resources/xlf/Strings.es.xlf | 23 + src/MSBuild/Resources/xlf/Strings.fr.xlf | 23 + src/MSBuild/Resources/xlf/Strings.it.xlf | 23 + src/MSBuild/Resources/xlf/Strings.ja.xlf | 23 + src/MSBuild/Resources/xlf/Strings.ko.xlf | 23 + src/MSBuild/Resources/xlf/Strings.pl.xlf | 23 + src/MSBuild/Resources/xlf/Strings.pt-BR.xlf | 23 + src/MSBuild/Resources/xlf/Strings.ru.xlf | 23 + src/MSBuild/Resources/xlf/Strings.tr.xlf | 23 + src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf | 23 + src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf | 23 + src/MSBuild/TerminalLogger/Project.cs | 2 + .../TerminalLogger/StaticTimeStopwatch.cs | 19 + src/MSBuild/TerminalLogger/TerminalLogger.cs | 172 ++++++- src/MSBuild/TerminalLogger/TestSummary.cs | 16 + 18 files changed, 728 insertions(+), 212 deletions(-) create mode 100644 src/MSBuild/TerminalLogger/StaticTimeStopwatch.cs create mode 100644 src/MSBuild/TerminalLogger/TestSummary.cs diff --git a/src/MSBuild/Resources/Strings.resx b/src/MSBuild/Resources/Strings.resx index 0daf54d6adf..9cf90fa9ae4 100644 --- a/src/MSBuild/Resources/Strings.resx +++ b/src/MSBuild/Resources/Strings.resx @@ -1,6 +1,66 @@  + + @@ -9,9 +69,16 @@ - + + + + + + + + @@ -20,11 +87,10 @@ - - - - - + + + + @@ -38,13 +104,6 @@ - - - - - - - text/microsoft-resx @@ -53,19 +112,19 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + MSBUILD : error MSB1011: Specify which project or solution file to use because this folder contains more than one project or solution file. {StrBegin="MSBUILD : error MSB1011: "}UE: If no project or solution file is explicitly specified on the MSBuild.exe command-line, then the engine searches for a project or solution file in the current directory by looking for *.*PROJ and *.SLN. If more than one file is found that matches this wildcard, we fire this error. LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1050: Specify which project or solution file to use because the folder "{0}" contains more than one project or solution file. {StrBegin="MSBUILD : error MSB1050: "}UE: If no project or solution file is explicitly specified on the MSBuild.exe command-line, then the engine searches for a @@ -74,36 +133,35 @@ LOCALIZATION: The prefix "MSB1050 : error MSBxxxx:" should not be localized. - - + MSBUILD : Configuration error {0}: {1} {SubString="Configuration"}UE: This prefixes any error from reading the toolset definitions in msbuild.exe.config or the registry. There's no error code because one was included in the error message. LOCALIZATION: The word "Configuration" should be localized, the words "MSBuild" and "error" should NOT be localized. - + MSBUILD : error MSB1027: The -noAutoResponse switch cannot be specified in the MSBuild.rsp auto-response file, nor in any response file that is referenced by the auto-response file. {StrBegin="MSBUILD : error MSB1027: "}LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:", "-noAutoResponse" and "MSBuild.rsp" should not be localized. - + MSBuild version {0} for {1} LOCALIZATION: {0} contains the DLL version number. {1} contains the name of a runtime, like ".NET Framework", ".NET Core", or "Mono" - + MSBUILD : error MSB1008: Only one project can be specified. {StrBegin="MSBUILD : error MSB1008: "}UE: This happens if the user does something like "msbuild.exe myapp.proj myapp2.proj". This is not allowed. MSBuild.exe will only build a single project. The help topic may link to an article about how to author an MSBuild project that itself launches MSBuild on a number of other projects. LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1025: An internal failure occurred while running MSBuild. {StrBegin="MSBUILD : error MSB1025: "}UE: This message is shown when the application has to terminate either because of a bug in the code, or because some FX/CLR method threw an unexpected exception. LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" and "MSBuild" should not be localized. - + Syntax: MSBuild.exe [options] [project file | directory] @@ -115,7 +173,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + Description: Builds the specified targets in the project file. If a project file is not specified, MSBuild searches the current working directory for a file that has a file @@ -132,7 +190,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + Switches: Note that you can specify switches using "-switch", "/switch" and "--switch". @@ -145,7 +203,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -help Display this usage message. (Short form: -? or -h) @@ -157,7 +215,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -noLogo Do not display the startup banner and copyright message. @@ -169,7 +227,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -version Display version information only. (Short form: -ver) @@ -181,7 +239,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + @<file> Insert command-line settings from a text file. To specify multiple response files, specify each response file separately. @@ -200,7 +258,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -noAutoResponse Do not auto-include any MSBuild.rsp files. (Short form: -noAutoRsp) @@ -213,7 +271,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -target:<targets> Build these targets in this project. Use a semicolon or a comma to separate multiple targets, or specify each target separately. (Short form: -t) @@ -229,7 +287,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -property:<n>=<v> Set or override these project-level properties. <n> is the property name, and <v> is the property value. Use a semicolon or a comma to separate multiple properties, or @@ -246,7 +304,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -logger:<logger> Use this logger to log events from MSBuild. To specify multiple loggers, specify each logger separately. The <logger> syntax is: @@ -271,7 +329,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -verbosity:<level> Display this amount of information in the event log. The available verbosity levels are: q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic]. (Short form: -v) @@ -291,7 +349,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -consoleLoggerParameters:<parameters> Parameters to console logger. (Short form: -clp) The available parameters are: @@ -338,7 +396,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -noConsoleLogger Disable the default console logger and do not log events to the console. (Short form: -noConLog) @@ -351,7 +409,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -validate Validate the project against the default schema. (Short form: -val) @@ -369,19 +427,19 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - -maxCpuCount[:n] Specifies the maximum number of concurrent processes to + + -maxCpuCount[:n] Specifies the maximum number of concurrent processes to build with. If the switch is not used, the default value used is 1. If the switch is used without a value MSBuild will use up to the number of processors on the computer. (Short form: -m[:n]) - + LOCALIZATION: "maxCpuCount" should not be localized. LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + Examples: MSBuild MyApp.sln -t:Rebuild -p:Configuration=Release @@ -397,14 +455,12 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + For switch syntax, type "MSBuild -help" UE: this message is shown when the user makes a syntax error on the command-line for a switch. LOCALIZATION: "MSBuild -help" should not be localized. - - - + -distributedLogger:<central logger>*<forwarding logger> Use this logger to log events from MSBuild, attaching a different logger instance to each node. To specify @@ -432,7 +488,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg chars. - + -ignoreProjectExtensions:<extensions> List of extensions to ignore when determining which project file to build. Use a semicolon or a comma @@ -450,9 +506,8 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - -toolsVersion:<version> + + -toolsVersion:<version> The version of the MSBuild Toolset (tasks, targets, etc.) to use during build. This version will override the versions specified by individual projects. (Short form: @@ -469,9 +524,8 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - -outputResultsCache:[cacheFile] + + -outputResultsCache:[cacheFile] Output cache file where MSBuild will write the contents of its build result caches at the end of the build. If -isolateProjects is set to False, this sets it to True. @@ -482,9 +536,8 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - -inputResultsCaches:<cacheFile>... + + -inputResultsCaches:<cacheFile>... Semicolon separated list of input cache files that MSBuild will read build results from. If -isolateProjects is set to False, this sets it to True. (short form: -irc) @@ -494,8 +547,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - + -fileLogger[n] Logs the build output to a file. By default the file is in the current directory and named "msbuild[n].log". Events from all nodes are combined into @@ -514,7 +566,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -distributedFileLogger Logs the build output to multiple log files, one log file per MSBuild node. The initial location for these files is @@ -537,7 +589,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -fileLoggerParameters[n]:<parameters> Provides any extra parameters for file loggers. The presence of this switch implies the @@ -576,7 +628,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -nodeReuse:<parameters> Enables or Disables the reuse of MSBuild nodes. The parameters are: @@ -588,7 +640,7 @@ -nr:true - + -preprocess[:file] Creates a single, aggregated project file by inlining all the files that would be imported during a @@ -603,7 +655,7 @@ -pp:out.txt - + -detailedSummary[:True|False] Shows detailed information at the end of the build about the configurations built and how they were @@ -615,7 +667,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -warnAsError[:code[;code2]] List of warning codes to treats as errors. Use a semicolon or a comma to separate multiple warning codes. To treat all @@ -634,7 +686,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -warnAsMessage[:code[;code2]] List of warning codes to treats as low importance messages. Use a semicolon or a comma to separate @@ -649,7 +701,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -binaryLogger[:[LogFile=]output.binlog[;ProjectImports={None,Embed,ZipFile}]] Serializes all build events to a compressed binary file. By default the file is in the current directory and named @@ -701,7 +753,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -restore[:True|False] Runs a target named Restore prior to building other targets and ensures the build for these @@ -726,7 +778,7 @@ format. Otherwise, a tab separated file is produced. - + -restoreProperty:<n>=<v> Set or override these project-level properties only during restore and do not use properties specified @@ -743,7 +795,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -interactive[:True|False] Indicates that actions in the build are allowed to interact with the user. Do not use this argument @@ -758,7 +810,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -isolateProjects[:True|MessageUponIsolationViolation|False] Causes MSBuild to build each project in isolation. @@ -784,7 +836,7 @@ LOCALIZATION: "-isolateProjects" should not be localized. LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -graphBuild[:True|False] Causes MSBuild to construct and build a project graph. @@ -803,10 +855,10 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + For more detailed information, see https://aka.ms/msbuild/docs - + -targets[:file] Prints a list of available targets without executing the actual build process. By default the output is written to @@ -822,7 +874,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -lowPriority[:True|False] Causes MSBuild to run at low process priority. @@ -836,7 +888,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -warnNotAsError[:code[;code2]] List of warning codes to treats not treat as errors. Use a semicolon or a comma to separate @@ -851,7 +903,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -question (Experimental) Question whether there is any build work. MSBuild will error out when it detects a target or task @@ -865,7 +917,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + -reportFileAccesses[:True|False] Causes MSBuild to report file accesses to any configured project cache plugins. @@ -877,7 +929,7 @@ LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - + MSBUILD : Configuration error MSB1043: The application could not start. {0} {StrBegin="MSBUILD : Configuration error MSB1043: "} @@ -885,7 +937,7 @@ LOCALIZATION: The prefix "MSBUILD : Configuration error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1019: Logger switch was not correctly formed. {StrBegin="MSBUILD : error MSB1019: "}UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. This error is shown when a user does any of the following: @@ -896,7 +948,7 @@ parameters are optional). LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1030: Maximum CPU count is not valid. {0} {StrBegin="MSBUILD : error MSB1030: "} @@ -905,15 +957,15 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1032: Maximum CPU count is not valid. Value must be an integer greater than zero and no more than 1024. {StrBegin="MSBUILD : error MSB1032: "} UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. This error is shown when a user specifies a CPU value that is zero or less. For example, -m:0 instead of -m:2. LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - + + MSBUILD : error MSB1033: Node number is not valid. {0}. {StrBegin="MSBUILD : error MSB1033: "} @@ -922,7 +974,7 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1034: Node number is not valid. Value must be an integer greater than zero. {StrBegin="MSBUILD : error MSB1034: "} UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. @@ -930,7 +982,7 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1006: Property is not valid. {StrBegin="MSBUILD : error MSB1006: "}UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. @@ -941,12 +993,12 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : MSB1046: The schema "{0}" is not valid. {1} {StrBegin="MSBUILD : MSB1046: "}UE: This message is shown when the schema file provided for the validation of a project is itself not valid. LOCALIZATION: "{0}" is the schema file path. "{1}" is a message from an FX exception that describes why the schema file is bad. - + Switch: {0} UE: This is attached to error messages caused by an invalid switch. This message indicates what the invalid arg was. @@ -956,7 +1008,7 @@ LOCALIZATION: {0} contains the invalid switch text. - + MSBUILD : error MSB1040: ToolsVersion is not valid. {0} {StrBegin="MSBUILD : error MSB1040: "} @@ -965,7 +1017,7 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1018: Verbosity level is not valid. {StrBegin="MSBUILD : error MSB1018: "}UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. @@ -974,31 +1026,31 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1028: The logger failed unexpectedly. {StrBegin="MSBUILD : error MSB1028: "} UE: This error is shown when a logger specified with the -logger switch throws an exception while being initialized. This message is followed by the exception text including the stack trace. LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - MSBUILD : Logger error {0}: {1} - UE: This prefixes the error message emitted by a logger, when a logger fails in a controlled way using a LoggerException. + + MSBUILD : Logger error {0}: {1} + UE: This prefixes the error message emitted by a logger, when a logger fails in a controlled way using a LoggerException. For example, the logger is indicating that it could not create its output file. There's no error code because one was supplied by the logger. LOCALIZATION: The word "Logger" should be localized, the words "MSBuild" and "error" should NOT be localized. - - MSBUILD : Logger error MSB1029: {0} - {SubString="Logger", "{0}"}{StrBegin="MSBUILD : "} + + MSBUILD : Logger error MSB1029: {0} + {SubString="Logger", "{0}"}{StrBegin="MSBUILD : "} UE: This prefixes the error message emitted by a logger, when a logger fails in a controlled way using a LoggerException. For example, the logger is indicating that it could not create its output file. This is like LoggerFailurePrefixNoErrorCode, but the logger didn't supply its own error code, so we have to provide one. LOCALIZATION: The word "Logger" should be localized, the words "MSBuild" and "error" should NOT be localized. - + MSBUILD : error MSB1007: Specify a logger. {StrBegin="MSBUILD : error MSB1007: "}UE: This happens if the user does something like "msbuild.exe -logger". The user must pass in an actual logger class @@ -1006,14 +1058,14 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1031: Specify the maximum number of CPUs. {StrBegin="MSBUILD : error MSB1031: "}UE: This happens if the user does something like "msbuild.exe -m". The user must pass in an actual number like -m:4. LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1003: Specify a project or solution file. The current working directory does not contain a project or solution file. {StrBegin="MSBUILD : error MSB1003: "}UE: The user must either specify a project or solution file to build, or there must be a project file in the current directory @@ -1021,7 +1073,7 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1005: Specify a property and its value. {StrBegin="MSBUILD : error MSB1005: "}UE: This happens if the user does something like "msbuild.exe -property". The user must pass in an actual property @@ -1029,7 +1081,7 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1012: Specify a response file. {StrBegin="MSBUILD : error MSB1012: "}UE: This error would occur if the user did something like "msbuild.exe @ foo.proj". The at-sign must be followed by a @@ -1037,7 +1089,7 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1004: Specify the name of the target. {StrBegin="MSBUILD : error MSB1004: "}UE: This happens if the user does something like "msbuild.exe -target". The user must pass in an actual target name @@ -1045,7 +1097,7 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1010: Must provide a property name for the getProperty switch. {StrBegin="MSBUILD : error MSB1010: "}UE: This happens if the user does something like "msbuild.exe -getProperty". The user must pass in an actual property name @@ -1053,7 +1105,7 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1014: Must provide an item name for the getItem switch. {StrBegin="MSBUILD : error MSB1014: "}UE: This happens if the user does something like "msbuild.exe -getItem". The user must pass in an actual item name @@ -1061,7 +1113,7 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1017: Must provide a target name for the getTargetResult switch. {StrBegin="MSBUILD : error MSB1017: "}UE: This happens if the user does something like "msbuild.exe -getTargetResult". The user must pass in an actual target name @@ -1069,14 +1121,14 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1063: Cannot access properties or items when building solution files or solution filter files. This feature is only available when building individual projects. {StrBegin="MSBUILD : error MSB1063: "}UE: This happens if the user passes in a solution file when trying to access individual properties or items. The user must pass in a project file. LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1039: Specify the version of the toolset. {StrBegin="MSBUILD : error MSB1039: "} @@ -1085,7 +1137,7 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1016: Specify the verbosity level. {StrBegin="MSBUILD : error MSB1016: "}UE: This happens if the user does something like "msbuild.exe -verbosity". The user must pass in a verbosity level @@ -1093,14 +1145,14 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1024: Only one schema can be specified for validation of the project. {StrBegin="MSBUILD : error MSB1024: "}UE: The user did something like msbuild -validate:foo.xsd -validate:bar.xsd. We only allow one schema to be specified. LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + Some command line switches were read from the auto-response file "{0}". To disable this file, use the "-noAutoResponse" switch. UE: This message appears in high verbosity modes when we used some @@ -1108,25 +1160,25 @@ where the switches are coming from. - + MSBUILD : error MSB1009: Project file does not exist. {StrBegin="MSBUILD : error MSB1009: "}UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + Building the projects in this solution one at a time. To enable parallel build, please add the "-m" switch. - + MSBUILD : MSB1045: Stopping because of syntax errors in project file. {StrBegin="MSBUILD : MSB1045: "} - + MSBUILD : error MSB1023: Cannot read the response file. {0} {StrBegin="MSBUILD : error MSB1023: "}UE: This error is shown when the response file cannot be read off disk. LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. {0} contains a localized message explaining why the response file could not be read -- this message comes from the CLR/FX. - + MSBUILD : error MSB1013: The response file was specified twice. A response file can be specified only once. Any files named "msbuild.rsp" in the directory of MSBuild.exe or in the directory of the first project or solution built (which if no project or solution is specified is the current working directory) were automatically used as response files. {StrBegin="MSBUILD : error MSB1013: "}UE: Response files are just text files that contain a bunch of command-line switches to be passed to MSBuild.exe. The purpose is so you don't have to type the same switches over and over again ... you can just pass in the response file instead. @@ -1135,54 +1187,54 @@ exact @ switch that resulted in the duplicate response file. LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1022: Response file does not exist. {StrBegin="MSBUILD : error MSB1022: "}UE: This message would show if the user did something like "msbuild @bogus.rsp" where bogus.rsp doesn't exist. This message does not need in-line parameters because the exception takes care of displaying the invalid arg. LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + Validating project using schema file "{0}". LOCALIZATION: "{0}" is the location of the schema file. - + MSBUILD : MSB1044: Project is not valid. {0} {StrBegin="MSBUILD : MSB1044: "}UE: This error is shown when the user asks his project to be validated against a schema (-val switch for MSBuild.exe), and the project has errors. "{0}" contains a message explaining the problem. LOCALIZATION: "{0}" is a message from the System.XML schema validator and is already localized. - + MSBUILD : error MSB1026: Schema file does not exist. {StrBegin="MSBUILD : error MSB1026: "}UE: This error is shown when the user specifies a schema file using the -validate:<schema> switch, and the file does not exist on disk. This message does not need in-line parameters because the exception takes care of displaying the invalid arg. LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1026: Schema file '{0}' does not exist. {StrBegin="MSBUILD : error MSB1026: "}UE: This error is printed if the default schema does not exist or in the extremely unlikely event that an explicit schema file was passed and existed when the command line parameters were checked but was deleted from disk before this check was made. LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1002: This switch does not take any parameters. {StrBegin="MSBUILD : error MSB1002: "}UE: For example, if somebody types "msbuild.exe -noLogo:1", they would get this error because the -noLogo switch should not be followed by any parameters ... it stands alone. LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1001: Unknown switch. {StrBegin="MSBUILD : error MSB1001: "}UE: This occurs when the user passes in an unrecognized switch on the MSBuild.exe command-line. LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1015: MSBuild does not run on this version of the operating system. It is only supported on Windows 7 and later versions. {StrBegin="MSBUILD : error MSB1015: "}LOCALIZATION: The error prefix "MSBUILD : error MSBxxxx:" should not be localized. - + Forcing load of Microsoft.Build.Engine because MSBUILDOLDOM=1... - + MSBUILD : error MSB1035: Specify the project extensions to ignore. {StrBegin="MSBUILD : error MSB1035: "} UE: This happens if the user does something like "msbuild.exe -ignoreProjectExtensions". The user must pass in one or more @@ -1190,11 +1242,11 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1036: There is an invalid extension in the -ignoreProjectExtensions list. Extensions must start with a period ".", have one or more characters after the period and not contain any invalid path characters or wildcards. {StrBegin="MSBUILD : error MSB1036: "}LOCALIZATION: The error prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1037: Specify one or more parameters for the console logger if using the -consoleLoggerParameters switch {StrBegin="MSBUILD : error MSB1037: "} UE: This happens if the user does something like "msbuild.exe -consoleLoggerParameters:". The user must pass in one or more parameters @@ -1202,7 +1254,7 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1038: Specify one or more parameters for the file logger if using the -fileLoggerParameters switch {StrBegin="MSBUILD : error MSB1038: "} UE: This happens if the user does something like "msbuild.exe -fileLoggerParameters:". The user must pass in one or more parameters @@ -1210,7 +1262,7 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1066: Specify one or more parameters for the terminal logger if using the -terminalLoggerParameters switch {StrBegin="MSBUILD : error MSB1066: "} @@ -1219,14 +1271,14 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1041: Specify one or more parameters for node reuse if using the -nodeReuse switch {StrBegin="MSBUILD : error MSB1041: "} UE: This happens if the user does something like "msbuild.exe -nodeReuse:" without a true or false LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1042: Node reuse value is not valid. {0}. {StrBegin="MSBUILD : error MSB1042: "} UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. @@ -1234,7 +1286,7 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1042: Node reuse value is not valid. This version of MSBuild does not support node reuse. If specified, the node reuse switch value must be false. {StrBegin="MSBUILD : error MSB1042: "} UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. @@ -1242,7 +1294,7 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1052: Restore value is not valid. {0} {StrBegin="MSBUILD : error MSB1052: "} UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. @@ -1250,7 +1302,7 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1055: Interactive value is not valid. {0} {StrBegin="MSBUILD : error MSB1055: "} @@ -1259,7 +1311,7 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1056: Isolate projects value is not valid. {0} {StrBegin="MSBUILD : error MSB1056: "} @@ -1269,7 +1321,7 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1057: Graph build value is not valid. {StrBegin="MSBUILD : error MSB1057: "} @@ -1278,7 +1330,7 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1061: Detailed summary value is not valid. {0} {StrBegin="MSBUILD : error MSB1061: "} @@ -1287,7 +1339,7 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1064: Low priority value is not valid. {0} {StrBegin="MSBUILD : error MSB1064: "} @@ -1296,7 +1348,7 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1065: Terminal logger value is not valid. It should be one of 'auto', 'true', or 'false'. {0} {StrBegin="MSBUILD : error MSB1065: "} @@ -1305,26 +1357,25 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + Attempting to cancel the build... - + MSBUILD : error MSB1047: File to preprocess to is not valid. {0} {StrBegin="MSBUILD : error MSB1047: "} - + MSBUILD : error MSB1059: Targets could not be printed. {0} {StrBegin="MSBUILD : error MSB1059: "} - - + MSBUILD : error MSB1021: Cannot create an instance of the logger. {0} {StrBegin="MSBUILD : error MSB1021: "} UE: This error is shown when a logger cannot be loaded and instantiated from its assembly. LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. {0} contains a message explaining why the logger could not be created -- this message comes from the CLR/FX and is localized. - + MSBUILD : error MSB1020: The logger was not found. Check the following: 1.) The logger name specified is the same as the name of the logger class. 2.) The logger class is "public" and implements the Microsoft.Build.Framework.ILogger interface. 3.) The path to the logger assembly is correct, or the logger can be loaded using only the assembly name provided. {StrBegin="MSBUILD : error MSB1020: "}UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. @@ -1333,22 +1384,22 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB4192: The project file "{0}" is in the ".vcproj" or ".dsp" file format, which MSBuild cannot build directly. Please convert the project by opening it in the Visual Studio IDE or running the conversion tool, or, for ".vcproj", use MSBuild to build the solution file containing the project instead. {StrBegin="MSBUILD : error MSB4192: "} LOC: ".vcproj" and ".dsp" should not be localized - + If MSBuild debugging does not work correctly, please verify that the "Just My Code" feature is enabled in Visual Studio, and that you have selected the managed debugger. - + MSBUILD : error MSB1048: Solution files cannot be debugged directly. Run MSBuild first with an environment variable MSBUILDEMITSOLUTION=1 to create a corresponding ".sln.metaproj" file. Then debug that. {StrBegin="MSBUILD : error MSB1048: "} LOC: ".SLN" should not be localized - + MSBUILD : error MSB1049: The {0} parameter must be specified {StrBegin="MSBUILD : error MSB1049: "} - + MSBUILD : error MSB1063: Report file accesses value is not valid. {0} {StrBegin="MSBUILD : error MSB1063: "} @@ -1357,21 +1408,14 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - - - - - + Build started. - + {0} ({1},{2}) A file location to be embedded in a string. - + MSBUILD : error MSB1051: Specify one or more warning codes to treat as low importance messages when using the -warnAsMessage switch. {StrBegin="MSBUILD : error MSB1051: "} @@ -1379,7 +1423,7 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - + MSBUILD : error MSB1060: Specify one or more warning codes when using the -warnNotAsError switch. {StrBegin="MSBUILD : error MSB1060: "} @@ -1393,11 +1437,11 @@ MSBUILD :error MSB1054: A filename must be specified to generate the profiler result. - + MSBUILD : error MSB1058: Only one output results cache can be specified. {StrBegin="MSBUILD : error MSB1058: "} - + The specified logger "{0}" could not be created and will not be used. {1} UE: This error is shown when a logger cannot be loaded and instantiated from its assembly. @@ -1406,7 +1450,7 @@ logger could not be created -- this message comes from the CLR/FX and is localized. - + MSBUILD : error MSB1060: Undefined environment variable passed in as switch. {StrBegin="MSBUILD : error MSB1060: "} @@ -1414,49 +1458,48 @@ but the environment variable is not defined. - + Process = "{0}" - + MSBuild executable path = "{0}" - + Command line arguments = "{0}" - + Current directory = "{0}" - + MSBuild version = "{0}" - + MSBuild logs and debug information will be at "{0}" - + {0} Full command line: '{1}' Switches appended by response files:{2} - + '{0}' came from '{1}' These are response file switches with the location of the response file on disk. - + MSBUILD : error MSB1062: The -warnnotaserror switch cannot be specified unless the -warnaserror switch is also specified and left empty. {StrBegin="MSBUILD : error MSB1062: "}LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:", "-warnnotaserror" and "-warnaserror" should not be localized. - + The '{0}' switch is not supported for solution files. - + MSBUILD : error MSB5016: The name "{0}" contains an invalid character "{1}". {StrBegin="MSBUILD : error MSB5016: "} - Restore complete ({0}s) @@ -1537,9 +1580,9 @@ {0}: VT100 coded hyperlink to project output directory - - ({0:F1}s) - + + ({0:F1}s) + {0}: duration in seconds with 1 decimal point @@ -1555,12 +1598,25 @@ Terminal Logger was not used because the output is being redirected to a file. - - - + + {0}{1} test {2} ({3}s) + + Project finished summary. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: BuildResult_{X} + {3}: duration in seconds with 1 decimal point + + + + {0}{1} test {2} {3} ({4}s) + + Project finished summary including target framework information. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: target framework + {3}: BuildResult_{X} + {4}: duration in seconds with 1 decimal point + + + \ No newline at end of file diff --git a/src/MSBuild/Resources/xlf/Strings.cs.xlf b/src/MSBuild/Resources/xlf/Strings.cs.xlf index 2ed7d5da6bb..7f92b00795d 100644 --- a/src/MSBuild/Resources/xlf/Strings.cs.xlf +++ b/src/MSBuild/Resources/xlf/Strings.cs.xlf @@ -1654,6 +1654,29 @@ Když se nastaví na MessageUponIsolationViolation (nebo jeho krátký Terminálový protokolovač se nepoužil, protože výstup se přesměrovává do souboru. + + {0}{1} test {2} ({3}s) + {0}{1} test {2} ({3}s) + + Project finished summary. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: BuildResult_{X} + {3}: duration in seconds with 1 decimal point + + + + {0}{1} test {2} {3} ({4}s) + {0}{1} test {2} {3} ({4}s) + + Project finished summary including target framework information. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: target framework + {3}: BuildResult_{X} + {4}: duration in seconds with 1 decimal point + + MSBUILD : error MSB1002: This switch does not take any parameters. MSBUILD : error MSB1002: Pro tento přepínač se nepoužívají žádné parametry. diff --git a/src/MSBuild/Resources/xlf/Strings.de.xlf b/src/MSBuild/Resources/xlf/Strings.de.xlf index d95a1823546..3074a2b73b8 100644 --- a/src/MSBuild/Resources/xlf/Strings.de.xlf +++ b/src/MSBuild/Resources/xlf/Strings.de.xlf @@ -1642,6 +1642,29 @@ Dieses Protokollierungsformat ist standardmäßig aktiviert. Die Terminalprotokollierung wurde nicht verwendet, da die Ausgabe an eine Datei umgeleitet wird. + + {0}{1} test {2} ({3}s) + {0}{1} test {2} ({3}s) + + Project finished summary. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: BuildResult_{X} + {3}: duration in seconds with 1 decimal point + + + + {0}{1} test {2} {3} ({4}s) + {0}{1} test {2} {3} ({4}s) + + Project finished summary including target framework information. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: target framework + {3}: BuildResult_{X} + {4}: duration in seconds with 1 decimal point + + MSBUILD : error MSB1002: This switch does not take any parameters. MSBUILD : error MSB1002: Der Schalter erlaubt keine Parameter. diff --git a/src/MSBuild/Resources/xlf/Strings.es.xlf b/src/MSBuild/Resources/xlf/Strings.es.xlf index 7cd424ea80f..aac77f21df3 100644 --- a/src/MSBuild/Resources/xlf/Strings.es.xlf +++ b/src/MSBuild/Resources/xlf/Strings.es.xlf @@ -1648,6 +1648,29 @@ Esta marca es experimental y puede que no funcione según lo previsto. No se usó el terminal de Logger porque la salida se está redirigiendo a un archivo. + + {0}{1} test {2} ({3}s) + {0}{1} test {2} ({3}s) + + Project finished summary. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: BuildResult_{X} + {3}: duration in seconds with 1 decimal point + + + + {0}{1} test {2} {3} ({4}s) + {0}{1} test {2} {3} ({4}s) + + Project finished summary including target framework information. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: target framework + {3}: BuildResult_{X} + {4}: duration in seconds with 1 decimal point + + MSBUILD : error MSB1002: This switch does not take any parameters. MSBUILD : error MSB1002: Este modificador no tiene ningún parámetro. diff --git a/src/MSBuild/Resources/xlf/Strings.fr.xlf b/src/MSBuild/Resources/xlf/Strings.fr.xlf index 4439fa3e05a..b49a800ef75 100644 --- a/src/MSBuild/Resources/xlf/Strings.fr.xlf +++ b/src/MSBuild/Resources/xlf/Strings.fr.xlf @@ -1641,6 +1641,29 @@ Remarque : verbosité des enregistreurs d’événements de fichiers L’enregistreur d’événements du terminal n’a pas été utilisé car la sortie est redirigée vers un fichier. + + {0}{1} test {2} ({3}s) + {0}{1} test {2} ({3}s) + + Project finished summary. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: BuildResult_{X} + {3}: duration in seconds with 1 decimal point + + + + {0}{1} test {2} {3} ({4}s) + {0}{1} test {2} {3} ({4}s) + + Project finished summary including target framework information. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: target framework + {3}: BuildResult_{X} + {4}: duration in seconds with 1 decimal point + + MSBUILD : error MSB1002: This switch does not take any parameters. MSBUILD : error MSB1002: Ce commutateur n'accepte aucun paramètre. diff --git a/src/MSBuild/Resources/xlf/Strings.it.xlf b/src/MSBuild/Resources/xlf/Strings.it.xlf index d549fbef333..31e215aa734 100644 --- a/src/MSBuild/Resources/xlf/Strings.it.xlf +++ b/src/MSBuild/Resources/xlf/Strings.it.xlf @@ -1652,6 +1652,29 @@ Nota: livello di dettaglio dei logger di file Il logger del terminale non è stato usato perché l'output viene reindirizzato a un file. + + {0}{1} test {2} ({3}s) + {0}{1} test {2} ({3}s) + + Project finished summary. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: BuildResult_{X} + {3}: duration in seconds with 1 decimal point + + + + {0}{1} test {2} {3} ({4}s) + {0}{1} test {2} {3} ({4}s) + + Project finished summary including target framework information. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: target framework + {3}: BuildResult_{X} + {4}: duration in seconds with 1 decimal point + + MSBUILD : error MSB1002: This switch does not take any parameters. MSBUILD : error MSB1002: questa opzione non accetta parametri. diff --git a/src/MSBuild/Resources/xlf/Strings.ja.xlf b/src/MSBuild/Resources/xlf/Strings.ja.xlf index bde74466fc0..05c8077165b 100644 --- a/src/MSBuild/Resources/xlf/Strings.ja.xlf +++ b/src/MSBuild/Resources/xlf/Strings.ja.xlf @@ -1641,6 +1641,29 @@ 出力がファイルにリダイレクトされているため、ターミナル ロガーは使用されませんでした。 + + {0}{1} test {2} ({3}s) + {0}{1} test {2} ({3}s) + + Project finished summary. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: BuildResult_{X} + {3}: duration in seconds with 1 decimal point + + + + {0}{1} test {2} {3} ({4}s) + {0}{1} test {2} {3} ({4}s) + + Project finished summary including target framework information. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: target framework + {3}: BuildResult_{X} + {4}: duration in seconds with 1 decimal point + + MSBUILD : error MSB1002: This switch does not take any parameters. MSBUILD : error MSB1002: このスイッチにはパラメーターを指定できません。 diff --git a/src/MSBuild/Resources/xlf/Strings.ko.xlf b/src/MSBuild/Resources/xlf/Strings.ko.xlf index 6b2e1b97d3c..166dc28fb66 100644 --- a/src/MSBuild/Resources/xlf/Strings.ko.xlf +++ b/src/MSBuild/Resources/xlf/Strings.ko.xlf @@ -1641,6 +1641,29 @@ 출력이 파일로 리디렉션되기 때문에 터미널 로거가 사용되지 않았습니다. + + {0}{1} test {2} ({3}s) + {0}{1} test {2} ({3}s) + + Project finished summary. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: BuildResult_{X} + {3}: duration in seconds with 1 decimal point + + + + {0}{1} test {2} {3} ({4}s) + {0}{1} test {2} {3} ({4}s) + + Project finished summary including target framework information. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: target framework + {3}: BuildResult_{X} + {4}: duration in seconds with 1 decimal point + + MSBUILD : error MSB1002: This switch does not take any parameters. MSBUILD : error MSB1002: 이 스위치에는 매개 변수를 지정할 수 없습니다. diff --git a/src/MSBuild/Resources/xlf/Strings.pl.xlf b/src/MSBuild/Resources/xlf/Strings.pl.xlf index 38eafd9dbc1..5c6b4f748f9 100644 --- a/src/MSBuild/Resources/xlf/Strings.pl.xlf +++ b/src/MSBuild/Resources/xlf/Strings.pl.xlf @@ -1652,6 +1652,29 @@ Ta flaga jest eksperymentalna i może nie działać zgodnie z oczekiwaniami. Rejestrator terminali nie został użyty, ponieważ dane wyjściowe są przekierowywane do pliku. + + {0}{1} test {2} ({3}s) + {0}{1} test {2} ({3}s) + + Project finished summary. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: BuildResult_{X} + {3}: duration in seconds with 1 decimal point + + + + {0}{1} test {2} {3} ({4}s) + {0}{1} test {2} {3} ({4}s) + + Project finished summary including target framework information. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: target framework + {3}: BuildResult_{X} + {4}: duration in seconds with 1 decimal point + + MSBUILD : error MSB1002: This switch does not take any parameters. MSBUILD : error MSB1002: ten przełącznik nie ma żadnych parametrów. diff --git a/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf b/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf index 9399e73a222..bb64f66b98f 100644 --- a/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf +++ b/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf @@ -1642,6 +1642,29 @@ arquivo de resposta. O Agente de Terminal não foi usado porque a saída está sendo redirecionada para um arquivo. + + {0}{1} test {2} ({3}s) + {0}{1} test {2} ({3}s) + + Project finished summary. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: BuildResult_{X} + {3}: duration in seconds with 1 decimal point + + + + {0}{1} test {2} {3} ({4}s) + {0}{1} test {2} {3} ({4}s) + + Project finished summary including target framework information. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: target framework + {3}: BuildResult_{X} + {4}: duration in seconds with 1 decimal point + + MSBUILD : error MSB1002: This switch does not take any parameters. MSBUILD : error MSB1002: Esta opção não aceita parâmetros. diff --git a/src/MSBuild/Resources/xlf/Strings.ru.xlf b/src/MSBuild/Resources/xlf/Strings.ru.xlf index 09b513f67b4..d0635052bb3 100644 --- a/src/MSBuild/Resources/xlf/Strings.ru.xlf +++ b/src/MSBuild/Resources/xlf/Strings.ru.xlf @@ -1640,6 +1640,29 @@ Средство ведения журнала терминалов не используется, так как выходные данные перенаправляются в файл. + + {0}{1} test {2} ({3}s) + {0}{1} test {2} ({3}s) + + Project finished summary. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: BuildResult_{X} + {3}: duration in seconds with 1 decimal point + + + + {0}{1} test {2} {3} ({4}s) + {0}{1} test {2} {3} ({4}s) + + Project finished summary including target framework information. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: target framework + {3}: BuildResult_{X} + {4}: duration in seconds with 1 decimal point + + MSBUILD : error MSB1002: This switch does not take any parameters. MSBUILD : error MSB1002: у этого ключа нет параметров. diff --git a/src/MSBuild/Resources/xlf/Strings.tr.xlf b/src/MSBuild/Resources/xlf/Strings.tr.xlf index 37bd0d98a4f..c46582a4f6c 100644 --- a/src/MSBuild/Resources/xlf/Strings.tr.xlf +++ b/src/MSBuild/Resources/xlf/Strings.tr.xlf @@ -1645,6 +1645,29 @@ Çıkış bir dosyaya yeniden yönlendirildiği için Terminal Günlükçüsü kullanılmadı. + + {0}{1} test {2} ({3}s) + {0}{1} test {2} ({3}s) + + Project finished summary. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: BuildResult_{X} + {3}: duration in seconds with 1 decimal point + + + + {0}{1} test {2} {3} ({4}s) + {0}{1} test {2} {3} ({4}s) + + Project finished summary including target framework information. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: target framework + {3}: BuildResult_{X} + {4}: duration in seconds with 1 decimal point + + MSBUILD : error MSB1002: This switch does not take any parameters. MSBUILD : error MSB1002: Bu anahtar parametreyle kullanılmaz. diff --git a/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf b/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf index bb43d7a64f0..261148ad676 100644 --- a/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf @@ -1641,6 +1641,29 @@ 未使用终端记录器,因为正在将输出重定向到文件。 + + {0}{1} test {2} ({3}s) + {0}{1} test {2} ({3}s) + + Project finished summary. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: BuildResult_{X} + {3}: duration in seconds with 1 decimal point + + + + {0}{1} test {2} {3} ({4}s) + {0}{1} test {2} {3} ({4}s) + + Project finished summary including target framework information. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: target framework + {3}: BuildResult_{X} + {4}: duration in seconds with 1 decimal point + + MSBUILD : error MSB1002: This switch does not take any parameters. MSBUILD : error MSB1002: 此开关不采用任何参数。 diff --git a/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf b/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf index 64eb661fc18..6c8728c8730 100644 --- a/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf @@ -1641,6 +1641,29 @@ 因為輸出正重新導向至檔案,所以未使用終端記錄器。 + + {0}{1} test {2} ({3}s) + {0}{1} test {2} ({3}s) + + Project finished summary. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: BuildResult_{X} + {3}: duration in seconds with 1 decimal point + + + + {0}{1} test {2} {3} ({4}s) + {0}{1} test {2} {3} ({4}s) + + Project finished summary including target framework information. + {0}: indentation - few spaces to visually indent row + {1}: project name + {2}: target framework + {3}: BuildResult_{X} + {4}: duration in seconds with 1 decimal point + + MSBUILD : error MSB1002: This switch does not take any parameters. MSBUILD : error MSB1002: 這個參數不使用任何參數。 diff --git a/src/MSBuild/TerminalLogger/Project.cs b/src/MSBuild/TerminalLogger/Project.cs index 3f027249782..fec67e629af 100644 --- a/src/MSBuild/TerminalLogger/Project.cs +++ b/src/MSBuild/TerminalLogger/Project.cs @@ -46,6 +46,8 @@ public Project(string? targetFramework, StopwatchAbstraction? stopwatch) /// public string? TargetFramework { get; } + public bool IsTestProject { get; set; } + /// /// A lazily initialized list of build messages/warnings/errors raised during the build. /// diff --git a/src/MSBuild/TerminalLogger/StaticTimeStopwatch.cs b/src/MSBuild/TerminalLogger/StaticTimeStopwatch.cs new file mode 100644 index 00000000000..fa6a3682893 --- /dev/null +++ b/src/MSBuild/TerminalLogger/StaticTimeStopwatch.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace Microsoft.Build.Logging.TerminalLogger; + +internal sealed class StaticTimeStopwatch : StopwatchAbstraction +{ + public StaticTimeStopwatch(double elapsedMilliseconds) + { + ElapsedSeconds = elapsedMilliseconds / 1000; + } + + public override double ElapsedSeconds { get; } + + public override void Start() => throw new System.NotSupportedException(); + public override void Stop() => throw new System.NotSupportedException(); +} diff --git a/src/MSBuild/TerminalLogger/TerminalLogger.cs b/src/MSBuild/TerminalLogger/TerminalLogger.cs index 10048c7ac19..f179cc11a4a 100644 --- a/src/MSBuild/TerminalLogger/TerminalLogger.cs +++ b/src/MSBuild/TerminalLogger/TerminalLogger.cs @@ -8,6 +8,12 @@ using Microsoft.Build.Framework; using Microsoft.Build.Shared; using System.Text.RegularExpressions; +using System.Diagnostics; +using System.Collections.Concurrent; +using static System.Net.Mime.MediaTypeNames; + + + #if NET7_0_OR_GREATER using System.Diagnostics.CodeAnalysis; #endif @@ -59,6 +65,8 @@ public ProjectContext(BuildEventContext context) internal Func? CreateStopwatch = null; + internal static string[] MessageSeparator = new[] { "||||" }; + /// /// Protects access to state shared between the logger callbacks and the rendering thread. /// @@ -170,6 +178,7 @@ public ProjectContext(BuildEventContext context) /// The two directory separator characters to be passed to methods like . /// private static readonly char[] PathSeparators = { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; + private ConcurrentBag _testRunSummaries = new(); /// /// Default constructor, used by the MSBuild logger infra. @@ -271,6 +280,9 @@ private void BuildFinished(object sender, BuildFinishedEventArgs e) _projects.Clear(); + var testRunSummaries = _testRunSummaries; + _testRunSummaries = new ConcurrentBag(); + Terminal.BeginUpdate(); try { @@ -290,6 +302,26 @@ private void BuildFinished(object sender, BuildFinishedEventArgs e) 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 = testRunSummaries.Aggregate(TimeSpan.Zero,(t, s) => t + s.Duration); + if (testRunSummaries.Any(t => t.Failed > 0) || _buildHasErrors) + { + var testResult = $"Test run {AnsiCodes.Colorize("failed", TerminalColor.Red)}. Total: {total} Failed: {failed} Passed: {passed} Skipped: {skipped}, Duration: {duration}"; + Terminal.WriteLine(testResult); + } + else + { + var testResult = $"Test run {AnsiCodes.Colorize("passed", TerminalColor.Green)}. Total: {total} Failed: {failed} Passed: {passed} Skipped: {skipped}, Duration: {duration}"; + Terminal.WriteLine(testResult); + } + } } finally { @@ -301,6 +333,7 @@ private void BuildFinished(object sender, BuildFinishedEventArgs e) Terminal.EndUpdate(); } + _buildHasErrors = false; _buildHasWarnings = false; _restoreFailed = false; @@ -406,26 +439,48 @@ private void ProjectFinished(object sender, ProjectFinishedEventArgs e) _restoreFinished = true; } // If this was a notable project build, we print it as completed only if it's produced an output or warnings/error. - else if (project.OutputPath is not null || project.BuildMessages is not null) + else if (project.OutputPath is not null || project.BuildMessages is not null || project.IsTestProject) { // Show project build complete and its output - - if (string.IsNullOrEmpty(project.TargetFramework)) + if (project.IsTestProject) { - Terminal.Write(ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("ProjectFinished_NoTF", - Indentation, - projectFile, - buildResult, - duration)); + if (string.IsNullOrEmpty(project.TargetFramework)) + { + Terminal.Write(ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("TestProjectFinished_NoTF", + Indentation, + projectFile, + buildResult, + duration)); + } + else + { + Terminal.Write(ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("TestProjectFinished_WithTF", + Indentation, + projectFile, + AnsiCodes.Colorize(project.TargetFramework, TargetFrameworkColor), + buildResult, + duration)); + } } else { - Terminal.Write(ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("ProjectFinished_WithTF", - Indentation, - projectFile, - AnsiCodes.Colorize(project.TargetFramework, TargetFrameworkColor), - buildResult, - duration)); + if (string.IsNullOrEmpty(project.TargetFramework)) + { + Terminal.Write(ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("ProjectFinished_NoTF", + Indentation, + projectFile, + buildResult, + duration)); + } + else + { + Terminal.Write(ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("ProjectFinished_WithTF", + Indentation, + projectFile, + AnsiCodes.Colorize(project.TargetFramework, TargetFrameworkColor), + buildResult, + duration)); + } } // Print the output path as a link if we have it. @@ -498,12 +553,22 @@ private void ProjectFinished(object sender, ProjectFinishedEventArgs e) private void TargetStarted(object sender, TargetStartedEventArgs e) { var buildEventContext = e.BuildEventContext; + Debug.WriteLine($"Target started {e.TargetName}"); if (_restoreContext is null && buildEventContext is not null && _projects.TryGetValue(new ProjectContext(buildEventContext), out Project? project)) { project.Stopwatch.Start(); string projectFile = Path.GetFileNameWithoutExtension(e.ProjectFile); - NodeStatus nodeStatus = new(projectFile, project.TargetFramework, e.TargetName, project.Stopwatch); + + var isTestTarget = e.TargetName == "_VSTestMSBuild2"; + + var targetName = isTestTarget ? "Testing" : e.TargetName; + if (isTestTarget) + { + project.IsTestProject = true; + } + + NodeStatus nodeStatus = new(projectFile, project.TargetFramework, targetName, project.Stopwatch); UpdateNodeStatus(buildEventContext, nodeStatus); } } @@ -530,14 +595,17 @@ private void TargetFinished(object sender, TargetFinishedEventArgs e) private void TaskStarted(object sender, TaskStartedEventArgs e) { var buildEventContext = e.BuildEventContext; - if (_restoreContext is null && buildEventContext is not null && e.TaskName == "MSBuild") + if (_restoreContext is null && buildEventContext is not null) { - // This will yield the node, so preemptively mark it idle - UpdateNodeStatus(buildEventContext, null); - - if (_projects.TryGetValue(new ProjectContext(buildEventContext), out Project? project)) + if (e.TaskName == "MSBuild") { - project.Stopwatch.Stop(); + // This will yield the node, so preemptively mark it idle + UpdateNodeStatus(buildEventContext, null); + + if (_projects.TryGetValue(new ProjectContext(buildEventContext), out Project? project)) + { + project.Stopwatch.Stop(); + } } } } @@ -556,6 +624,8 @@ private void MessageRaised(object sender, BuildMessageEventArgs e) string? message = e.Message; if (message is not null && e.Importance == MessageImportance.High) { + Debug.WriteLine($"MSBUILD MESSAGE: {message}"); + 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); @@ -563,11 +633,10 @@ private void MessageRaised(object sender, BuildMessageEventArgs e) { var projectFileName = Path.GetFileName(e.ProjectFile.AsSpan()); if (!projectFileName.IsEmpty && - message.AsSpan().StartsWith(Path.GetFileNameWithoutExtension(projectFileName)) && - _projects.TryGetValue(new ProjectContext(buildEventContext), out Project? project)) + message.AsSpan().StartsWith(Path.GetFileNameWithoutExtension(projectFileName)) && hasProject) { ReadOnlyMemory outputPath = e.Message.AsMemory().Slice(index + 4); - project.OutputPath = outputPath; + project!.OutputPath = outputPath; } } @@ -575,6 +644,61 @@ private void MessageRaised(object sender, BuildMessageEventArgs e) { RenderImmediateMessage(message); } + else if (hasProject && project!.IsTestProject) + { + var node = _nodes[NodeIndexForContext(buildEventContext)]; + + if (e.Subcategory == "VSTESTTLPASSED") + { + // 0 - localized result indicator + // 1 - display name + // 2 - duration + var data = message.Split(MessageSeparator, StringSplitOptions.None); + var indicator = data[0]; + var displayName = data[1]; + var duration = data[2]; + + // TODO: Re-enable colorization. I think there is a "bug" in RenderNodeStatus, that does not account for Target containing + // ANSI codes, which will make it render shorter on the screen. So when text is replaces, the duration is seen twice on the screen. + var testResult = $"{AnsiCodes.Colorize(indicator, TerminalColor.Green)} {displayName}"; + var status = new NodeStatus(node!.Project, node.TargetFramework, testResult, project.Stopwatch); + UpdateNodeStatus(buildEventContext, status); + } + else if (e.Subcategory == "VSTESTTLSKIPPED") + { + // 0 - localized result indicator + // 1 - display name + var data = message.Split(MessageSeparator, StringSplitOptions.None); + var indicator = data[0]; + var displayName = data[1]; + var testResult = $"{AnsiCodes.Colorize(indicator, TerminalColor.Yellow)} {displayName}"; + var status = new NodeStatus(node!.Project, node.TargetFramework, testResult, project.Stopwatch); + UpdateNodeStatus(buildEventContext, status); + } + else if (e.Subcategory == "VSTESTTLFINISH") + { + // 0 - total tests + // 1 - passed tests + // 2 - skipped tests + // 3 - failed tests + // 4 - duration + var data = message.Split(MessageSeparator, StringSplitOptions.None); + _ = int.TryParse(data[0], out int total); + _ = int.TryParse(data[1], out int passed); + _ = int.TryParse(data[2], out int skipped); + _ = int.TryParse(data[3], out int failed); + _ = double.TryParse(data[4], out double durationInMs); + + _testRunSummaries.Add(new TestSummary + { + Total = total, + Passed = passed, + Skipped = skipped, + Failed = failed, + Duration = TimeSpan.FromMilliseconds(durationInMs) + }); + } + } else if (e.Code == "NETSDK1057" && !_loggedPreviewMessage) { // The SDK will log the high-pri "not-a-warning" message NETSDK1057 diff --git a/src/MSBuild/TerminalLogger/TestSummary.cs b/src/MSBuild/TerminalLogger/TestSummary.cs new file mode 100644 index 00000000000..d053d885abc --- /dev/null +++ b/src/MSBuild/TerminalLogger/TestSummary.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Build.Logging.TerminalLogger +{ + internal class TestSummary + { + public int Total { get; set; } + public int Passed { get; set; } + public int Skipped { get; set; } + public int Failed { get; set; } + public TimeSpan Duration { get; set; } + } +} From 4ecf48666e54a8f54ffa1ec0dcc43f90fc166c1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Fri, 2 Feb 2024 16:00:46 +0100 Subject: [PATCH 02/19] Fix timing and use extended messages --- src/MSBuild/TerminalLogger/TerminalLogger.cs | 114 ++++++++++--------- src/MSBuild/TerminalLogger/TestSummary.cs | 1 - 2 files changed, 59 insertions(+), 56 deletions(-) diff --git a/src/MSBuild/TerminalLogger/TerminalLogger.cs b/src/MSBuild/TerminalLogger/TerminalLogger.cs index f179cc11a4a..918f5488e51 100644 --- a/src/MSBuild/TerminalLogger/TerminalLogger.cs +++ b/src/MSBuild/TerminalLogger/TerminalLogger.cs @@ -65,8 +65,6 @@ public ProjectContext(BuildEventContext context) internal Func? CreateStopwatch = null; - internal static string[] MessageSeparator = new[] { "||||" }; - /// /// Protects access to state shared between the logger callbacks and the rendering thread. /// @@ -179,6 +177,8 @@ public ProjectContext(BuildEventContext context) /// private static readonly char[] PathSeparators = { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; private ConcurrentBag _testRunSummaries = new(); + private DateTime? _testStartTime; + private DateTime? _testEndTime; /// /// Default constructor, used by the MSBuild logger infra. @@ -305,20 +305,19 @@ private void BuildFinished(object sender, BuildFinishedEventArgs e) 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 = testRunSummaries.Aggregate(TimeSpan.Zero,(t, s) => t + s.Duration); + var testDuration = (_testStartTime != null && _testEndTime != null ? (_testEndTime - _testStartTime).Value.TotalSeconds : 0).ToString("F1"); if (testRunSummaries.Any(t => t.Failed > 0) || _buildHasErrors) { - var testResult = $"Test run {AnsiCodes.Colorize("failed", TerminalColor.Red)}. Total: {total} Failed: {failed} Passed: {passed} Skipped: {skipped}, Duration: {duration}"; + var testResult = $"Test run {AnsiCodes.Colorize("failed", TerminalColor.Red)}. Total: {total} Failed: {failed} Passed: {passed} Skipped: {skipped}, Duration: {testDuration}s"; Terminal.WriteLine(testResult); } else { - var testResult = $"Test run {AnsiCodes.Colorize("passed", TerminalColor.Green)}. Total: {total} Failed: {failed} Passed: {passed} Skipped: {skipped}, Duration: {duration}"; + var testResult = $"Test run {AnsiCodes.Colorize("passed", TerminalColor.Green)}. Total: {total} Failed: {failed} Passed: {passed} Skipped: {skipped}, Duration: {testDuration}s"; Terminal.WriteLine(testResult); } } @@ -553,7 +552,6 @@ private void ProjectFinished(object sender, ProjectFinishedEventArgs e) private void TargetStarted(object sender, TargetStartedEventArgs e) { var buildEventContext = e.BuildEventContext; - Debug.WriteLine($"Target started {e.TargetName}"); if (_restoreContext is null && buildEventContext is not null && _projects.TryGetValue(new ProjectContext(buildEventContext), out Project? project)) { project.Stopwatch.Start(); @@ -565,6 +563,12 @@ private void TargetStarted(object sender, TargetStartedEventArgs e) var targetName = isTestTarget ? "Testing" : e.TargetName; if (isTestTarget) { + // Use the minimal start time, so if we run tests in parallel, we can calculate duration + // as this start time, minus time when tests finished. + _testStartTime = _testStartTime == null + ? e.Timestamp + : e.Timestamp < _testStartTime + ? e.Timestamp : _testStartTime; project.IsTestProject = true; } @@ -587,6 +591,13 @@ private void UpdateNodeStatus(BuildEventContext buildEventContext, NodeStatus? n /// private void TargetFinished(object sender, TargetFinishedEventArgs e) { + if (e.TargetName == "_VSTestMSBuild") + { + _testEndTime = _testEndTime == null + ? e.Timestamp + : e.Timestamp > _testEndTime + ? e.Timestamp : _testEndTime; + } } /// @@ -624,7 +635,6 @@ private void MessageRaised(object sender, BuildMessageEventArgs e) string? message = e.Message; if (message is not null && e.Importance == MessageImportance.High) { - Debug.WriteLine($"MSBUILD MESSAGE: {message}"); 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. @@ -648,55 +658,49 @@ private void MessageRaised(object sender, BuildMessageEventArgs e) { var node = _nodes[NodeIndexForContext(buildEventContext)]; - if (e.Subcategory == "VSTESTTLPASSED") - { - // 0 - localized result indicator - // 1 - display name - // 2 - duration - var data = message.Split(MessageSeparator, StringSplitOptions.None); - var indicator = data[0]; - var displayName = data[1]; - var duration = data[2]; - - // TODO: Re-enable colorization. I think there is a "bug" in RenderNodeStatus, that does not account for Target containing - // ANSI codes, which will make it render shorter on the screen. So when text is replaces, the duration is seen twice on the screen. - var testResult = $"{AnsiCodes.Colorize(indicator, TerminalColor.Green)} {displayName}"; - var status = new NodeStatus(node!.Project, node.TargetFramework, testResult, project.Stopwatch); - UpdateNodeStatus(buildEventContext, status); - } - else if (e.Subcategory == "VSTESTTLSKIPPED") + if (e is IExtendedBuildEventArgs extendedMessage) { - // 0 - localized result indicator - // 1 - display name - var data = message.Split(MessageSeparator, StringSplitOptions.None); - var indicator = data[0]; - var displayName = data[1]; - var testResult = $"{AnsiCodes.Colorize(indicator, TerminalColor.Yellow)} {displayName}"; - var status = new NodeStatus(node!.Project, node.TargetFramework, testResult, project.Stopwatch); - UpdateNodeStatus(buildEventContext, status); - } - else if (e.Subcategory == "VSTESTTLFINISH") - { - // 0 - total tests - // 1 - passed tests - // 2 - skipped tests - // 3 - failed tests - // 4 - duration - var data = message.Split(MessageSeparator, StringSplitOptions.None); - _ = int.TryParse(data[0], out int total); - _ = int.TryParse(data[1], out int passed); - _ = int.TryParse(data[2], out int skipped); - _ = int.TryParse(data[3], out int failed); - _ = double.TryParse(data[4], out double durationInMs); - - _testRunSummaries.Add(new TestSummary + switch (extendedMessage.ExtendedType) { - Total = total, - Passed = passed, - Skipped = skipped, - Failed = failed, - Duration = TimeSpan.FromMilliseconds(durationInMs) - }); + case "VSTESTTLPASSED": + { + var indicator = extendedMessage.ExtendedMetadata!["localizedResult"]!; + var displayName = extendedMessage.ExtendedMetadata!["displayName"]; + + var testResult = $"{AnsiCodes.Colorize(indicator, TerminalColor.Green)} {displayName}"; + var status = new NodeStatus(node!.Project, node.TargetFramework, testResult, project.Stopwatch); + UpdateNodeStatus(buildEventContext, status); + break; + } + + case "VSTESTTLSKIPPED": + { + var indicator = extendedMessage.ExtendedMetadata!["localizedResult"]!; + var displayName = extendedMessage.ExtendedMetadata!["displayName"]; + + var testResult = $"{AnsiCodes.Colorize(indicator, TerminalColor.Yellow)} {displayName}"; + var status = new NodeStatus(node!.Project, node.TargetFramework, testResult, project.Stopwatch); + UpdateNodeStatus(buildEventContext, status); + break; + } + + case "VSTESTTLFINISH": + { + _ = 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 = total, + Passed = passed, + Skipped = skipped, + Failed = failed, + }); + break; + } + } } } else if (e.Code == "NETSDK1057" && !_loggedPreviewMessage) diff --git a/src/MSBuild/TerminalLogger/TestSummary.cs b/src/MSBuild/TerminalLogger/TestSummary.cs index d053d885abc..c7a800d4ad8 100644 --- a/src/MSBuild/TerminalLogger/TestSummary.cs +++ b/src/MSBuild/TerminalLogger/TestSummary.cs @@ -11,6 +11,5 @@ internal class TestSummary public int Passed { get; set; } public int Skipped { get; set; } public int Failed { get; set; } - public TimeSpan Duration { get; set; } } } From 591a51e901ad425c736d0c9e9cfd81009afd5768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Tue, 6 Feb 2024 11:34:15 +0100 Subject: [PATCH 03/19] Remove unused class --- .../TerminalLogger/StaticTimeStopwatch.cs | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 src/MSBuild/TerminalLogger/StaticTimeStopwatch.cs diff --git a/src/MSBuild/TerminalLogger/StaticTimeStopwatch.cs b/src/MSBuild/TerminalLogger/StaticTimeStopwatch.cs deleted file mode 100644 index fa6a3682893..00000000000 --- a/src/MSBuild/TerminalLogger/StaticTimeStopwatch.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; - -namespace Microsoft.Build.Logging.TerminalLogger; - -internal sealed class StaticTimeStopwatch : StopwatchAbstraction -{ - public StaticTimeStopwatch(double elapsedMilliseconds) - { - ElapsedSeconds = elapsedMilliseconds / 1000; - } - - public override double ElapsedSeconds { get; } - - public override void Start() => throw new System.NotSupportedException(); - public override void Stop() => throw new System.NotSupportedException(); -} From 8a34eb886500729009ac995f01c6e47ed8ced922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Wed, 7 Feb 2024 17:49:36 +0100 Subject: [PATCH 04/19] doc and rename messages --- src/MSBuild/TerminalLogger/Project.cs | 3 ++ src/MSBuild/TerminalLogger/TerminalLogger.cs | 39 +++++++++++++++----- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/MSBuild/TerminalLogger/Project.cs b/src/MSBuild/TerminalLogger/Project.cs index fec67e629af..403a66375e7 100644 --- a/src/MSBuild/TerminalLogger/Project.cs +++ b/src/MSBuild/TerminalLogger/Project.cs @@ -46,6 +46,9 @@ public Project(string? targetFramework, StopwatchAbstraction? stopwatch) /// public string? TargetFramework { get; } + /// + /// True when the project has run target with name "_TestRunStart" defined in . + /// public bool IsTestProject { get; set; } /// diff --git a/src/MSBuild/TerminalLogger/TerminalLogger.cs b/src/MSBuild/TerminalLogger/TerminalLogger.cs index 918f5488e51..9df37bf6f3d 100644 --- a/src/MSBuild/TerminalLogger/TerminalLogger.cs +++ b/src/MSBuild/TerminalLogger/TerminalLogger.cs @@ -176,8 +176,25 @@ public ProjectContext(BuildEventContext context) /// The two directory separator characters to be passed to methods like . /// private static readonly char[] PathSeparators = { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; + + /// + /// One summary per finished project test run. + /// private ConcurrentBag _testRunSummaries = new(); + + /// + /// Name of target that identifies a project that has tests. + /// + private static string _testTarget = "_TestRunStart"; + + /// + /// Time of the oldest observed test target start. + /// private DateTime? _testStartTime; + + /// + /// Time of the most recently observed test target finished. + /// private DateTime? _testEndTime; /// @@ -280,7 +297,7 @@ private void BuildFinished(object sender, BuildFinishedEventArgs e) _projects.Clear(); - var testRunSummaries = _testRunSummaries; + var testRunSummaries = _testRunSummaries.ToList(); _testRunSummaries = new ConcurrentBag(); Terminal.BeginUpdate(); @@ -332,7 +349,6 @@ private void BuildFinished(object sender, BuildFinishedEventArgs e) Terminal.EndUpdate(); } - _buildHasErrors = false; _buildHasWarnings = false; _restoreFailed = false; @@ -438,6 +454,8 @@ private void ProjectFinished(object sender, ProjectFinishedEventArgs e) _restoreFinished = true; } // If this was a notable project build, we print it as completed only if it's produced an output or warnings/error. + // If this is a test project, print it always, so user can see either a success or failure, otherwise success is hidden + // and it is hard to see if project finished, or did not run at all. else if (project.OutputPath is not null || project.BuildMessages is not null || project.IsTestProject) { // Show project build complete and its output @@ -558,7 +576,7 @@ private void TargetStarted(object sender, TargetStartedEventArgs e) string projectFile = Path.GetFileNameWithoutExtension(e.ProjectFile); - var isTestTarget = e.TargetName == "_VSTestMSBuild2"; + var isTestTarget = e.TargetName == _testTarget; var targetName = isTestTarget ? "Testing" : e.TargetName; if (isTestTarget) @@ -591,7 +609,7 @@ private void UpdateNodeStatus(BuildEventContext buildEventContext, NodeStatus? n /// private void TargetFinished(object sender, TargetFinishedEventArgs e) { - if (e.TargetName == "_VSTestMSBuild") + if (e.TargetName == _testTarget) { _testEndTime = _testEndTime == null ? e.Timestamp @@ -658,33 +676,34 @@ private void MessageRaised(object sender, BuildMessageEventArgs e) { var node = _nodes[NodeIndexForContext(buildEventContext)]; - if (e is IExtendedBuildEventArgs extendedMessage) + // Consumes test update messages produced by VSTest and MSTest runner. + if (node != null && e is IExtendedBuildEventArgs extendedMessage) { switch (extendedMessage.ExtendedType) { - case "VSTESTTLPASSED": + case "TLTESTPASSED": { var indicator = extendedMessage.ExtendedMetadata!["localizedResult"]!; var displayName = extendedMessage.ExtendedMetadata!["displayName"]; var testResult = $"{AnsiCodes.Colorize(indicator, TerminalColor.Green)} {displayName}"; - var status = new NodeStatus(node!.Project, node.TargetFramework, testResult, project.Stopwatch); + var status = new NodeStatus(node.Project, node.TargetFramework, testResult, project.Stopwatch); UpdateNodeStatus(buildEventContext, status); break; } - case "VSTESTTLSKIPPED": + case "TLTESTSKIPPED": { var indicator = extendedMessage.ExtendedMetadata!["localizedResult"]!; var displayName = extendedMessage.ExtendedMetadata!["displayName"]; var testResult = $"{AnsiCodes.Colorize(indicator, TerminalColor.Yellow)} {displayName}"; - var status = new NodeStatus(node!.Project, node.TargetFramework, testResult, project.Stopwatch); + var status = new NodeStatus(node.Project, node.TargetFramework, testResult, project.Stopwatch); UpdateNodeStatus(buildEventContext, status); break; } - case "VSTESTTLFINISH": + case "TLTESTFINISH": { _ = int.TryParse(extendedMessage.ExtendedMetadata!["total"]!, out int total); _ = int.TryParse(extendedMessage.ExtendedMetadata!["passed"]!, out int passed); From b53ee1cce38e637f9aa417f6db79e92c53710142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Thu, 8 Feb 2024 13:59:01 +0100 Subject: [PATCH 05/19] Fix colored target bug, and add tests --- src/Framework/Logging/AnsiCodes.cs | 12 ++ src/MSBuild.UnitTests/NodeStatus_Tests.cs | 6 +- .../NodeStatus_Transition_Tests.cs | 171 ++++++++++++++++++ ...deStatus_Tests.EverythingFits.verified.txt | 1 + ...odeStatus_Tests.GoesToProject.verified.txt | 1 + ...ests.NamespaceIsTruncatedNext.verified.txt | 1 + ..._Tests.TargetIsTruncatedFirst.verified.txt | 1 + ...zeChange_Tests.EverythingFits.verified.txt | 1 + ...izeChange_Tests.GoesToProject.verified.txt | 1 + ...ests.NamespaceIsTruncatedNext.verified.txt | 1 + ..._Tests.TargetIsTruncatedFirst.verified.txt | 1 + ...ition_Tests.NodeTargetChanges.verified.txt | 4 + ...eTargetChangesToColoredTarget.verified.txt | 4 + ...n_Tests.NodeTargetUpdatesTime.verified.txt | 4 + ...eWithColoredTargetUpdatesTime.verified.txt | 4 + src/MSBuild.UnitTests/StaticStopwatch.cs | 23 +++ src/MSBuild.UnitTests/TickingStopwatch.cs | 32 ++++ src/MSBuild/TerminalLogger/NodesFrame.cs | 17 +- src/MSBuild/TerminalLogger/TerminalLogger.cs | 2 - 19 files changed, 280 insertions(+), 7 deletions(-) create mode 100644 src/MSBuild.UnitTests/NodeStatus_Transition_Tests.cs create mode 100644 src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.EverythingFits.verified.txt create mode 100644 src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.GoesToProject.verified.txt create mode 100644 src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.NamespaceIsTruncatedNext.verified.txt create mode 100644 src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.TargetIsTruncatedFirst.verified.txt create mode 100644 src/MSBuild.UnitTests/Snapshots/NodeStatus_SizeChange_Tests.EverythingFits.verified.txt create mode 100644 src/MSBuild.UnitTests/Snapshots/NodeStatus_SizeChange_Tests.GoesToProject.verified.txt create mode 100644 src/MSBuild.UnitTests/Snapshots/NodeStatus_SizeChange_Tests.NamespaceIsTruncatedNext.verified.txt create mode 100644 src/MSBuild.UnitTests/Snapshots/NodeStatus_SizeChange_Tests.TargetIsTruncatedFirst.verified.txt create mode 100644 src/MSBuild.UnitTests/Snapshots/NodeStatus_Transition_Tests.NodeTargetChanges.verified.txt create mode 100644 src/MSBuild.UnitTests/Snapshots/NodeStatus_Transition_Tests.NodeTargetChangesToColoredTarget.verified.txt create mode 100644 src/MSBuild.UnitTests/Snapshots/NodeStatus_Transition_Tests.NodeTargetUpdatesTime.verified.txt create mode 100644 src/MSBuild.UnitTests/Snapshots/NodeStatus_Transition_Tests.NodeWithColoredTargetUpdatesTime.verified.txt create mode 100644 src/MSBuild.UnitTests/StaticStopwatch.cs create mode 100644 src/MSBuild.UnitTests/TickingStopwatch.cs diff --git a/src/Framework/Logging/AnsiCodes.cs b/src/Framework/Logging/AnsiCodes.cs index 8466220026b..e3a8c3aabbf 100644 --- a/src/Framework/Logging/AnsiCodes.cs +++ b/src/Framework/Logging/AnsiCodes.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Text.RegularExpressions; + namespace Microsoft.Build.Logging.TerminalLogger; /// @@ -150,4 +152,14 @@ public static string MakeBold(string? s) /// Column index. /// Control codes to set the desired position. public static string SetCursorHorizontal(int column) => $"{CSI}{column}G"; + + /// + /// Removes all ANSI codes from the text. + /// + /// + /// + public static string RemoveAnsiCodes(string text) + { + return new Regex(@"\x1B\[[^@-~]*[@-~]").Replace(text, ""); + } } diff --git a/src/MSBuild.UnitTests/NodeStatus_Tests.cs b/src/MSBuild.UnitTests/NodeStatus_Tests.cs index 42e821c4ae6..238ba625ae3 100644 --- a/src/MSBuild.UnitTests/NodeStatus_Tests.cs +++ b/src/MSBuild.UnitTests/NodeStatus_Tests.cs @@ -3,8 +3,10 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.Build.Logging.TerminalLogger; @@ -19,11 +21,11 @@ namespace Microsoft.Build.CommandLine.UnitTests; [UsesVerify] -public class NodeStatus_Tests +public class NodeStatus_SizeChange_Tests { private readonly NodeStatus _status = new("Namespace.Project", "TargetFramework", "Target", new MockStopwatch()); - public NodeStatus_Tests() + public NodeStatus_SizeChange_Tests() { UseProjectRelativeDirectory("Snapshots"); } diff --git a/src/MSBuild.UnitTests/NodeStatus_Transition_Tests.cs b/src/MSBuild.UnitTests/NodeStatus_Transition_Tests.cs new file mode 100644 index 00000000000..bcc3dc607b8 --- /dev/null +++ b/src/MSBuild.UnitTests/NodeStatus_Transition_Tests.cs @@ -0,0 +1,171 @@ +// 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.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +using Microsoft.Build.Logging.TerminalLogger; + +using VerifyTests; +using VerifyXunit; +using Xunit; + +using static VerifyXunit.Verifier; + + +namespace Microsoft.Build.CommandLine.UnitTests; + +[UsesVerify] +public class NodeStatus_Transition_Tests +{ + public NodeStatus_Transition_Tests() + { + UseProjectRelativeDirectory("Snapshots"); + } + + [Fact] + public async Task NodeTargetChanges() + { + var rendered = Animate( + [ + new("Namespace.Project", "TargetFramework", "Build", new MockStopwatch()) + ], + [ + new("Namespace.Project", "TargetFramework", "Testing", new MockStopwatch()) + ]); + + await VerifyReplay(rendered); + } + + [Fact] + public async Task NodeTargetUpdatesTime() + { + // This test look like there is no change between the frames, but we ask the stopwatch for time they will increase the number. + // We need this because animations check that NodeStatus reference is the same. + // And we cannot use MockStopwatch because we don't know when to call Tick on them, and if we do it right away, the time will update in "both" nodes. + NodeStatus node = new("Namespace.Project", "TargetFramework", "Build", new TickingStopwatch()); + var rendered = Animate( + [ + node, + ], + [ + node, + ]); + + await VerifyReplay(rendered); + } + + [Fact] + public async Task NodeTargetChangesToColoredTarget() + { + var rendered = Animate( + [ + new("Namespace.Project", "TargetFramework", "Testing", new MockStopwatch()) + ], + [ + new("Namespace.Project", "TargetFramework", $"{AnsiCodes.Colorize("failed", TerminalColor.Red)} MyTestName1", new MockStopwatch()) + ]); + + await VerifyReplay(rendered); + } + + [Fact] + public async Task NodeWithColoredTargetUpdatesTime() + { + // This test look like there is no change between the frames, but we ask the stopwatch for time they will increase the number. + // We need this because animations check that NodeStatus reference is the same. + // And we cannot use MockStopwatch because we don't know when to call Tick on them, and if we do it right away, the time will update in "both" nodes. + NodeStatus node = new("Namespace.Project", "TargetFramework", $"{AnsiCodes.Colorize("passed", TerminalColor.Green)} MyTestName1", new TickingStopwatch()); + var rendered = Animate( + [ + node, + ], + [ + node + ]); + + await VerifyReplay(rendered); + } + + /// + /// Chains and renders node status updates and outputs replay able string of all the transitions. + /// + /// Takes array of arrays. The inner array is collection of nodes that are currently running. The outer array is how they update over time. + /// + private string Animate(params NodeStatus[][] nodeStatusesUpdates) + { + var width = 80; + var height = 1; + + NodesFrame previousFrame = new(Array.Empty(), 0, 0); + StringBuilder result = new StringBuilder(); + foreach (var nodeStatuses in nodeStatusesUpdates) + { + NodesFrame currentFrame = new NodesFrame(nodeStatuses, width, height); + result.Append(currentFrame.Render(previousFrame)); + previousFrame = currentFrame; + } + + return result.ToString(); + } + + private async Task VerifyReplay(string rendered) + { + try + { + await Verify(rendered); + } + catch (Exception ex) + { + if (ex.GetType().Name != "VerifyException") + { + throw; + } + + if (!ex.Message.StartsWith("Directory:")) + { + throw; + } + + string? directory = null; + string? received = null; + string? verified = null; + foreach (var line in ex.Message.Split('\n')) + { + var trimmed = line.TrimStart(' ', '-'); + Extract(trimmed, "Directory", ref directory); + Extract(trimmed, "Received", ref received); + Extract(trimmed, "Verified", ref verified); + } + + if (directory == null || received == null || verified == null) + { + throw; + } + + var pipeline = $$""" | % { "`n`n" } { $_ -split "(?=`e)" | % { Write-Host -NoNewline $_; Start-Sleep 0.5 }; Write-Host }"""; + throw new Exception($$""" + {{ex.Message.TrimEnd('\n')}} + + Received replay: + Get-Content {{Path.Combine(directory, received)}} {{pipeline}} + + Verified replay: + Get-Content {{Path.Combine(directory, verified)}} {{pipeline}} + """); + } + + void Extract(string line, string prefix, ref string? output) + { + if (line.StartsWith($"{prefix}: ")) + { + output = line.Substring(prefix.Length + 2); + } + } + } +} diff --git a/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.EverythingFits.verified.txt b/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.EverythingFits.verified.txt new file mode 100644 index 00000000000..5f282702bb0 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.EverythingFits.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.GoesToProject.verified.txt b/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.GoesToProject.verified.txt new file mode 100644 index 00000000000..5f282702bb0 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.GoesToProject.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.NamespaceIsTruncatedNext.verified.txt b/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.NamespaceIsTruncatedNext.verified.txt new file mode 100644 index 00000000000..5f282702bb0 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.NamespaceIsTruncatedNext.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.TargetIsTruncatedFirst.verified.txt b/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.TargetIsTruncatedFirst.verified.txt new file mode 100644 index 00000000000..5f282702bb0 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.TargetIsTruncatedFirst.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/NodeStatus_SizeChange_Tests.EverythingFits.verified.txt b/src/MSBuild.UnitTests/Snapshots/NodeStatus_SizeChange_Tests.EverythingFits.verified.txt new file mode 100644 index 00000000000..a889f734e14 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/NodeStatus_SizeChange_Tests.EverythingFits.verified.txt @@ -0,0 +1 @@ + Namespace.Project TargetFramework Target (0.0s) \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/NodeStatus_SizeChange_Tests.GoesToProject.verified.txt b/src/MSBuild.UnitTests/Snapshots/NodeStatus_SizeChange_Tests.GoesToProject.verified.txt new file mode 100644 index 00000000000..74eb4993b40 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/NodeStatus_SizeChange_Tests.GoesToProject.verified.txt @@ -0,0 +1 @@ +Project \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/NodeStatus_SizeChange_Tests.NamespaceIsTruncatedNext.verified.txt b/src/MSBuild.UnitTests/Snapshots/NodeStatus_SizeChange_Tests.NamespaceIsTruncatedNext.verified.txt new file mode 100644 index 00000000000..a06cd82177c --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/NodeStatus_SizeChange_Tests.NamespaceIsTruncatedNext.verified.txt @@ -0,0 +1 @@ + Project TargetFramework  (0.0s) \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/NodeStatus_SizeChange_Tests.TargetIsTruncatedFirst.verified.txt b/src/MSBuild.UnitTests/Snapshots/NodeStatus_SizeChange_Tests.TargetIsTruncatedFirst.verified.txt new file mode 100644 index 00000000000..014bb0cb3be --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/NodeStatus_SizeChange_Tests.TargetIsTruncatedFirst.verified.txt @@ -0,0 +1 @@ + Namespace.Project TargetFramework  (0.0s) \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/NodeStatus_Transition_Tests.NodeTargetChanges.verified.txt b/src/MSBuild.UnitTests/Snapshots/NodeStatus_Transition_Tests.NodeTargetChanges.verified.txt new file mode 100644 index 00000000000..c76bd410799 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/NodeStatus_Transition_Tests.NodeTargetChanges.verified.txt @@ -0,0 +1,4 @@ + + Namespace.Project TargetFramework Build (0.0s) + + Namespace.Project TargetFramework Testing (0.0s) diff --git a/src/MSBuild.UnitTests/Snapshots/NodeStatus_Transition_Tests.NodeTargetChangesToColoredTarget.verified.txt b/src/MSBuild.UnitTests/Snapshots/NodeStatus_Transition_Tests.NodeTargetChangesToColoredTarget.verified.txt new file mode 100644 index 00000000000..3be7cd9e970 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/NodeStatus_Transition_Tests.NodeTargetChangesToColoredTarget.verified.txt @@ -0,0 +1,4 @@ + + Namespace.Project TargetFramework Testing (0.0s) + + Namespace.Project TargetFramework failed MyTestName1 (0.0s) diff --git a/src/MSBuild.UnitTests/Snapshots/NodeStatus_Transition_Tests.NodeTargetUpdatesTime.verified.txt b/src/MSBuild.UnitTests/Snapshots/NodeStatus_Transition_Tests.NodeTargetUpdatesTime.verified.txt new file mode 100644 index 00000000000..5f7a118d89e --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/NodeStatus_Transition_Tests.NodeTargetUpdatesTime.verified.txt @@ -0,0 +1,4 @@ + + Namespace.Project TargetFramework Build (0.0s) + +(0.2s) diff --git a/src/MSBuild.UnitTests/Snapshots/NodeStatus_Transition_Tests.NodeWithColoredTargetUpdatesTime.verified.txt b/src/MSBuild.UnitTests/Snapshots/NodeStatus_Transition_Tests.NodeWithColoredTargetUpdatesTime.verified.txt new file mode 100644 index 00000000000..70de0fffa5d --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/NodeStatus_Transition_Tests.NodeWithColoredTargetUpdatesTime.verified.txt @@ -0,0 +1,4 @@ + + Namespace.Project TargetFramework passed MyTestName1 (0.0s) + +(0.2s) diff --git a/src/MSBuild.UnitTests/StaticStopwatch.cs b/src/MSBuild.UnitTests/StaticStopwatch.cs new file mode 100644 index 00000000000..c1afad3179c --- /dev/null +++ b/src/MSBuild.UnitTests/StaticStopwatch.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Net.Http.Headers; +using Microsoft.Build.Logging.TerminalLogger; + +namespace Microsoft.Build.CommandLine.UnitTests; + +/// +/// Stopwatch that always show the time provided in constructor. +/// +internal sealed class StaticStopwatch : StopwatchAbstraction +{ + public StaticStopwatch(double elapsedSeconds) + { + ElapsedSeconds = elapsedSeconds; + } + + public override double ElapsedSeconds { get; } + + public override void Start() => throw new System.NotImplementedException(); + public override void Stop() => throw new System.NotImplementedException(); +} diff --git a/src/MSBuild.UnitTests/TickingStopwatch.cs b/src/MSBuild.UnitTests/TickingStopwatch.cs new file mode 100644 index 00000000000..e0cd213ebeb --- /dev/null +++ b/src/MSBuild.UnitTests/TickingStopwatch.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Build.Logging.TerminalLogger; + +namespace Microsoft.Build.CommandLine.UnitTests; + +/// +/// Stopwatch that will increase by 0.1, every time you ask them for time. Useful for animations, because they check that NodeStatus +/// reference stays the same, and also for ensuring we are grabbing the time only once per frame. +/// +internal sealed class TickingStopwatch : StopwatchAbstraction +{ + private double _elapsedSeconds; + + public TickingStopwatch(double elapsedSeconds = 0.0) + { + _elapsedSeconds = elapsedSeconds; + } + + public override double ElapsedSeconds + { + get + { + var elapsed = _elapsedSeconds; + _elapsedSeconds += 0.1; + return elapsed; + } + } + public override void Start() => throw new System.NotImplementedException(); + public override void Stop() => throw new System.NotImplementedException(); +} diff --git a/src/MSBuild/TerminalLogger/NodesFrame.cs b/src/MSBuild/TerminalLogger/NodesFrame.cs index 33fbf2c8d42..acdf64a74cd 100644 --- a/src/MSBuild/TerminalLogger/NodesFrame.cs +++ b/src/MSBuild/TerminalLogger/NodesFrame.cs @@ -54,11 +54,19 @@ internal ReadOnlySpan RenderNodeStatus(int i) string? targetFramework = status.TargetFramework; string target = status.Target; - int renderedWidth = Length(durationString, project, targetFramework, target); + // The target may contain ANSI codes, we need to remove them to get the real + // size of the string as it will be printed on the screen, so when we move cursor + // backwards we move it by the amount of characters that will be seen by user, and not + // by the amount of characters + ANSI codes (that won't be seen). + // If we don't remove ANSI codes, the cursor will move too much too the left, and time + // time will render before the end of line, instead of at the end of line. + var renderedTarget = AnsiCodes.RemoveAnsiCodes(target); + + int renderedWidth = Length(durationString, project, targetFramework, renderedTarget); if (renderedWidth > Width) { - renderedWidth -= target.Length; + renderedWidth -= renderedTarget.Length; target = string.Empty; if (renderedWidth > Width) @@ -74,7 +82,7 @@ internal ReadOnlySpan RenderNodeStatus(int i) } } - return $"{TerminalLogger.Indentation}{project}{(targetFramework is null ? string.Empty : " ")}{AnsiCodes.Colorize(targetFramework, TerminalLogger.TargetFrameworkColor)} {AnsiCodes.SetCursorHorizontal(MaxColumn)}{AnsiCodes.MoveCursorBackward(target.Length + durationString.Length + 1)}{target} {durationString}".AsSpan(); + return $"{TerminalLogger.Indentation}{project}{(targetFramework is null ? string.Empty : " ")}{AnsiCodes.Colorize(targetFramework, TerminalLogger.TargetFrameworkColor)} {AnsiCodes.SetCursorHorizontal(MaxColumn)}{AnsiCodes.MoveCursorBackward(renderedTarget.Length + durationString.Length + 1)}{target} {durationString}".AsSpan(); static int Length(string durationString, string project, string? targetFramework, string target) => TerminalLogger.Indentation.Length + @@ -92,6 +100,9 @@ public string Render(NodesFrame previousFrame) StringBuilder sb = _renderBuilder; sb.Clear(); + // Move cursor back to 1st line of nodes. + sb.AppendLine($"{AnsiCodes.CSI}{previousFrame.NodesCount + 1}{AnsiCodes.MoveUpToLineStart}"); + int i = 0; for (; i < NodesCount; i++) { diff --git a/src/MSBuild/TerminalLogger/TerminalLogger.cs b/src/MSBuild/TerminalLogger/TerminalLogger.cs index 9df37bf6f3d..6b93fa3f6e1 100644 --- a/src/MSBuild/TerminalLogger/TerminalLogger.cs +++ b/src/MSBuild/TerminalLogger/TerminalLogger.cs @@ -856,8 +856,6 @@ internal void DisplayNodes() Terminal.Write(AnsiCodes.HideCursor); try { - // Move cursor back to 1st line of nodes. - Terminal.WriteLine($"{AnsiCodes.CSI}{_currentFrame.NodesCount + 1}{AnsiCodes.MoveUpToLineStart}"); Terminal.Write(rendered); } finally From e4e4baaaaa1635cce887dcb49e09df2dc990fb0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Thu, 8 Feb 2024 14:02:40 +0100 Subject: [PATCH 06/19] Compile the regex --- src/Framework/Logging/AnsiCodes.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Framework/Logging/AnsiCodes.cs b/src/Framework/Logging/AnsiCodes.cs index e3a8c3aabbf..399d7817217 100644 --- a/src/Framework/Logging/AnsiCodes.cs +++ b/src/Framework/Logging/AnsiCodes.cs @@ -124,6 +124,11 @@ internal static class AnsiCodes /// public const string RemoveProgress = "\x1b]9;4;0;\x1b\\"; + /// + /// Remove all ANSI escape codes from a string. + /// + private static readonly Regex RemoveAnsiRegex = new Regex(@"\x1B\[[^@-~]*[@-~]", RegexOptions.Compiled); + public static string Colorize(string? s, TerminalColor color) { if (string.IsNullOrWhiteSpace(s)) @@ -160,6 +165,6 @@ public static string MakeBold(string? s) /// public static string RemoveAnsiCodes(string text) { - return new Regex(@"\x1B\[[^@-~]*[@-~]").Replace(text, ""); + return RemoveAnsiRegex.Replace(text, ""); } } From 43e54297ba4f25c8d93bb165a621314bdc432e87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Thu, 8 Feb 2024 15:03:57 +0100 Subject: [PATCH 07/19] verified renamed --- .../ColorizedNodeStatus_Tests.EverythingFits.verified.txt | 1 - .../ColorizedNodeStatus_Tests.GoesToProject.verified.txt | 1 - ...lorizedNodeStatus_Tests.NamespaceIsTruncatedNext.verified.txt | 1 - ...ColorizedNodeStatus_Tests.TargetIsTruncatedFirst.verified.txt | 1 - 4 files changed, 4 deletions(-) delete mode 100644 src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.EverythingFits.verified.txt delete mode 100644 src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.GoesToProject.verified.txt delete mode 100644 src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.NamespaceIsTruncatedNext.verified.txt delete mode 100644 src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.TargetIsTruncatedFirst.verified.txt diff --git a/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.EverythingFits.verified.txt b/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.EverythingFits.verified.txt deleted file mode 100644 index 5f282702bb0..00000000000 --- a/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.EverythingFits.verified.txt +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.GoesToProject.verified.txt b/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.GoesToProject.verified.txt deleted file mode 100644 index 5f282702bb0..00000000000 --- a/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.GoesToProject.verified.txt +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.NamespaceIsTruncatedNext.verified.txt b/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.NamespaceIsTruncatedNext.verified.txt deleted file mode 100644 index 5f282702bb0..00000000000 --- a/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.NamespaceIsTruncatedNext.verified.txt +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.TargetIsTruncatedFirst.verified.txt b/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.TargetIsTruncatedFirst.verified.txt deleted file mode 100644 index 5f282702bb0..00000000000 --- a/src/MSBuild.UnitTests/Snapshots/ColorizedNodeStatus_Tests.TargetIsTruncatedFirst.verified.txt +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file From 9de8600b89f9d4cbcb2cf9c3e62706ba59184ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Thu, 8 Feb 2024 15:07:15 +0100 Subject: [PATCH 08/19] Fix class name --- .../{NodeStatus_Tests.cs => NodeStatus_SizeChange_Tests.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/MSBuild.UnitTests/{NodeStatus_Tests.cs => NodeStatus_SizeChange_Tests.cs} (100%) diff --git a/src/MSBuild.UnitTests/NodeStatus_Tests.cs b/src/MSBuild.UnitTests/NodeStatus_SizeChange_Tests.cs similarity index 100% rename from src/MSBuild.UnitTests/NodeStatus_Tests.cs rename to src/MSBuild.UnitTests/NodeStatus_SizeChange_Tests.cs From 4a1696b05038eb52c4f24947edcfe4a72bc6588b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Thu, 8 Feb 2024 15:15:56 +0100 Subject: [PATCH 09/19] Fix rendering without target --- .../Snapshots/NodeStatus_Tests.EverythingFits.verified.txt | 1 - .../Snapshots/NodeStatus_Tests.GoesToProject.verified.txt | 1 - .../NodeStatus_Tests.NamespaceIsTruncatedNext.verified.txt | 1 - .../NodeStatus_Tests.TargetIsTruncatedFirst.verified.txt | 1 - src/MSBuild/TerminalLogger/NodesFrame.cs | 2 +- 5 files changed, 1 insertion(+), 5 deletions(-) delete mode 100644 src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.EverythingFits.verified.txt delete mode 100644 src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.GoesToProject.verified.txt delete mode 100644 src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.NamespaceIsTruncatedNext.verified.txt delete mode 100644 src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.TargetIsTruncatedFirst.verified.txt diff --git a/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.EverythingFits.verified.txt b/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.EverythingFits.verified.txt deleted file mode 100644 index a889f734e14..00000000000 --- a/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.EverythingFits.verified.txt +++ /dev/null @@ -1 +0,0 @@ - Namespace.Project TargetFramework Target (0.0s) \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.GoesToProject.verified.txt b/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.GoesToProject.verified.txt deleted file mode 100644 index 74eb4993b40..00000000000 --- a/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.GoesToProject.verified.txt +++ /dev/null @@ -1 +0,0 @@ -Project \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.NamespaceIsTruncatedNext.verified.txt b/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.NamespaceIsTruncatedNext.verified.txt deleted file mode 100644 index a06cd82177c..00000000000 --- a/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.NamespaceIsTruncatedNext.verified.txt +++ /dev/null @@ -1 +0,0 @@ - Project TargetFramework  (0.0s) \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.TargetIsTruncatedFirst.verified.txt b/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.TargetIsTruncatedFirst.verified.txt deleted file mode 100644 index 014bb0cb3be..00000000000 --- a/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.TargetIsTruncatedFirst.verified.txt +++ /dev/null @@ -1 +0,0 @@ - Namespace.Project TargetFramework  (0.0s) \ No newline at end of file diff --git a/src/MSBuild/TerminalLogger/NodesFrame.cs b/src/MSBuild/TerminalLogger/NodesFrame.cs index acdf64a74cd..cdcaef9f7fa 100644 --- a/src/MSBuild/TerminalLogger/NodesFrame.cs +++ b/src/MSBuild/TerminalLogger/NodesFrame.cs @@ -67,7 +67,7 @@ internal ReadOnlySpan RenderNodeStatus(int i) if (renderedWidth > Width) { renderedWidth -= renderedTarget.Length; - target = string.Empty; + renderedTarget = target = string.Empty; if (renderedWidth > Width) { From 40339e4007c6543f36d6688afeaf6ecb1262d178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Thu, 8 Feb 2024 15:51:08 +0100 Subject: [PATCH 10/19] Localize --- src/MSBuild/Resources/Strings.resx | 7 +++++- src/MSBuild/Resources/xlf/Strings.cs.xlf | 6 +++++ src/MSBuild/Resources/xlf/Strings.de.xlf | 6 +++++ src/MSBuild/Resources/xlf/Strings.es.xlf | 6 +++++ src/MSBuild/Resources/xlf/Strings.fr.xlf | 6 +++++ src/MSBuild/Resources/xlf/Strings.it.xlf | 6 +++++ src/MSBuild/Resources/xlf/Strings.ja.xlf | 6 +++++ src/MSBuild/Resources/xlf/Strings.ko.xlf | 6 +++++ src/MSBuild/Resources/xlf/Strings.pl.xlf | 6 +++++ src/MSBuild/Resources/xlf/Strings.pt-BR.xlf | 6 +++++ src/MSBuild/Resources/xlf/Strings.ru.xlf | 6 +++++ src/MSBuild/Resources/xlf/Strings.tr.xlf | 6 +++++ src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf | 6 +++++ src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf | 6 +++++ src/MSBuild/TerminalLogger/TerminalLogger.cs | 25 +++++++++---------- 15 files changed, 96 insertions(+), 14 deletions(-) diff --git a/src/MSBuild/Resources/Strings.resx b/src/MSBuild/Resources/Strings.resx index 9cf90fa9ae4..ed23fafb9ab 100644 --- a/src/MSBuild/Resources/Strings.resx +++ b/src/MSBuild/Resources/Strings.resx @@ -1619,4 +1619,9 @@ {4}: duration in seconds with 1 decimal point - \ No newline at end of file + + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + {0} string, localized result e.g. Failed surrounded by ANSI colors. +{1-5} whole number + + diff --git a/src/MSBuild/Resources/xlf/Strings.cs.xlf b/src/MSBuild/Resources/xlf/Strings.cs.xlf index 7f92b00795d..63129a97924 100644 --- a/src/MSBuild/Resources/xlf/Strings.cs.xlf +++ b/src/MSBuild/Resources/xlf/Strings.cs.xlf @@ -1676,6 +1676,12 @@ Když se nastaví na MessageUponIsolationViolation (nebo jeho krátký {3}: BuildResult_{X} {4}: duration in seconds with 1 decimal point + + + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + {0} string, localized result e.g. Failed surrounded by ANSI colors. +{1-5} whole number MSBUILD : error MSB1002: This switch does not take any parameters. diff --git a/src/MSBuild/Resources/xlf/Strings.de.xlf b/src/MSBuild/Resources/xlf/Strings.de.xlf index 3074a2b73b8..4120a7d16e8 100644 --- a/src/MSBuild/Resources/xlf/Strings.de.xlf +++ b/src/MSBuild/Resources/xlf/Strings.de.xlf @@ -1664,6 +1664,12 @@ Dieses Protokollierungsformat ist standardmäßig aktiviert. {3}: BuildResult_{X} {4}: duration in seconds with 1 decimal point + + + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + {0} string, localized result e.g. Failed surrounded by ANSI colors. +{1-5} whole number MSBUILD : error MSB1002: This switch does not take any parameters. diff --git a/src/MSBuild/Resources/xlf/Strings.es.xlf b/src/MSBuild/Resources/xlf/Strings.es.xlf index aac77f21df3..879c2bdd3df 100644 --- a/src/MSBuild/Resources/xlf/Strings.es.xlf +++ b/src/MSBuild/Resources/xlf/Strings.es.xlf @@ -1670,6 +1670,12 @@ Esta marca es experimental y puede que no funcione según lo previsto. {3}: BuildResult_{X} {4}: duration in seconds with 1 decimal point + + + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + {0} string, localized result e.g. Failed surrounded by ANSI colors. +{1-5} whole number MSBUILD : error MSB1002: This switch does not take any parameters. diff --git a/src/MSBuild/Resources/xlf/Strings.fr.xlf b/src/MSBuild/Resources/xlf/Strings.fr.xlf index b49a800ef75..268732dca8e 100644 --- a/src/MSBuild/Resources/xlf/Strings.fr.xlf +++ b/src/MSBuild/Resources/xlf/Strings.fr.xlf @@ -1663,6 +1663,12 @@ Remarque : verbosité des enregistreurs d’événements de fichiers {3}: BuildResult_{X} {4}: duration in seconds with 1 decimal point + + + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + {0} string, localized result e.g. Failed surrounded by ANSI colors. +{1-5} whole number MSBUILD : error MSB1002: This switch does not take any parameters. diff --git a/src/MSBuild/Resources/xlf/Strings.it.xlf b/src/MSBuild/Resources/xlf/Strings.it.xlf index 31e215aa734..87d95f711ee 100644 --- a/src/MSBuild/Resources/xlf/Strings.it.xlf +++ b/src/MSBuild/Resources/xlf/Strings.it.xlf @@ -1674,6 +1674,12 @@ Nota: livello di dettaglio dei logger di file {3}: BuildResult_{X} {4}: duration in seconds with 1 decimal point + + + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + {0} string, localized result e.g. Failed surrounded by ANSI colors. +{1-5} whole number MSBUILD : error MSB1002: This switch does not take any parameters. diff --git a/src/MSBuild/Resources/xlf/Strings.ja.xlf b/src/MSBuild/Resources/xlf/Strings.ja.xlf index 05c8077165b..cb2b91a0115 100644 --- a/src/MSBuild/Resources/xlf/Strings.ja.xlf +++ b/src/MSBuild/Resources/xlf/Strings.ja.xlf @@ -1663,6 +1663,12 @@ {3}: BuildResult_{X} {4}: duration in seconds with 1 decimal point + + + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + {0} string, localized result e.g. Failed surrounded by ANSI colors. +{1-5} whole number MSBUILD : error MSB1002: This switch does not take any parameters. diff --git a/src/MSBuild/Resources/xlf/Strings.ko.xlf b/src/MSBuild/Resources/xlf/Strings.ko.xlf index 166dc28fb66..ea61758148d 100644 --- a/src/MSBuild/Resources/xlf/Strings.ko.xlf +++ b/src/MSBuild/Resources/xlf/Strings.ko.xlf @@ -1663,6 +1663,12 @@ {3}: BuildResult_{X} {4}: duration in seconds with 1 decimal point + + + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + {0} string, localized result e.g. Failed surrounded by ANSI colors. +{1-5} whole number MSBUILD : error MSB1002: This switch does not take any parameters. diff --git a/src/MSBuild/Resources/xlf/Strings.pl.xlf b/src/MSBuild/Resources/xlf/Strings.pl.xlf index 5c6b4f748f9..cf9e95faa1b 100644 --- a/src/MSBuild/Resources/xlf/Strings.pl.xlf +++ b/src/MSBuild/Resources/xlf/Strings.pl.xlf @@ -1674,6 +1674,12 @@ Ta flaga jest eksperymentalna i może nie działać zgodnie z oczekiwaniami. {3}: BuildResult_{X} {4}: duration in seconds with 1 decimal point + + + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + {0} string, localized result e.g. Failed surrounded by ANSI colors. +{1-5} whole number MSBUILD : error MSB1002: This switch does not take any parameters. diff --git a/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf b/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf index bb64f66b98f..efa56ad38e1 100644 --- a/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf +++ b/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf @@ -1664,6 +1664,12 @@ arquivo de resposta. {3}: BuildResult_{X} {4}: duration in seconds with 1 decimal point + + + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + {0} string, localized result e.g. Failed surrounded by ANSI colors. +{1-5} whole number MSBUILD : error MSB1002: This switch does not take any parameters. diff --git a/src/MSBuild/Resources/xlf/Strings.ru.xlf b/src/MSBuild/Resources/xlf/Strings.ru.xlf index d0635052bb3..ea1954fa290 100644 --- a/src/MSBuild/Resources/xlf/Strings.ru.xlf +++ b/src/MSBuild/Resources/xlf/Strings.ru.xlf @@ -1662,6 +1662,12 @@ {3}: BuildResult_{X} {4}: duration in seconds with 1 decimal point + + + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + {0} string, localized result e.g. Failed surrounded by ANSI colors. +{1-5} whole number MSBUILD : error MSB1002: This switch does not take any parameters. diff --git a/src/MSBuild/Resources/xlf/Strings.tr.xlf b/src/MSBuild/Resources/xlf/Strings.tr.xlf index c46582a4f6c..7a06d995445 100644 --- a/src/MSBuild/Resources/xlf/Strings.tr.xlf +++ b/src/MSBuild/Resources/xlf/Strings.tr.xlf @@ -1667,6 +1667,12 @@ {3}: BuildResult_{X} {4}: duration in seconds with 1 decimal point + + + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + {0} string, localized result e.g. Failed surrounded by ANSI colors. +{1-5} whole number MSBUILD : error MSB1002: This switch does not take any parameters. diff --git a/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf b/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf index 261148ad676..c29b8a0e463 100644 --- a/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf @@ -1663,6 +1663,12 @@ {3}: BuildResult_{X} {4}: duration in seconds with 1 decimal point + + + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + {0} string, localized result e.g. Failed surrounded by ANSI colors. +{1-5} whole number MSBUILD : error MSB1002: This switch does not take any parameters. diff --git a/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf b/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf index 6c8728c8730..57bf6bdc4de 100644 --- a/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf @@ -1663,6 +1663,12 @@ {3}: BuildResult_{X} {4}: duration in seconds with 1 decimal point + + + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + Test run {0}. Total: {1} Failed: {2} Passed: {3} Skipped: {4}, Duration: {5}s + {0} string, localized result e.g. Failed surrounded by ANSI colors. +{1-5} whole number MSBUILD : error MSB1002: This switch does not take any parameters. diff --git a/src/MSBuild/TerminalLogger/TerminalLogger.cs b/src/MSBuild/TerminalLogger/TerminalLogger.cs index 6b93fa3f6e1..932efafa46c 100644 --- a/src/MSBuild/TerminalLogger/TerminalLogger.cs +++ b/src/MSBuild/TerminalLogger/TerminalLogger.cs @@ -10,9 +10,6 @@ using System.Text.RegularExpressions; using System.Diagnostics; using System.Collections.Concurrent; -using static System.Net.Mime.MediaTypeNames; - - #if NET7_0_OR_GREATER using System.Diagnostics.CodeAnalysis; @@ -327,16 +324,18 @@ private void BuildFinished(object sender, BuildFinishedEventArgs e) 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"); - if (testRunSummaries.Any(t => t.Failed > 0) || _buildHasErrors) - { - var testResult = $"Test run {AnsiCodes.Colorize("failed", TerminalColor.Red)}. Total: {total} Failed: {failed} Passed: {passed} Skipped: {skipped}, Duration: {testDuration}s"; - Terminal.WriteLine(testResult); - } - else - { - var testResult = $"Test run {AnsiCodes.Colorize("passed", TerminalColor.Green)}. Total: {total} Failed: {failed} Passed: {passed} Skipped: {skipped}, Duration: {testDuration}s"; - Terminal.WriteLine(testResult); - } + + var colorizedResult = testRunSummaries.Any(t => t.Failed > 0) || _buildHasErrors + ? 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 From e3687319a10f729f2df41009d6a03429cd6d4a87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Thu, 8 Feb 2024 16:40:06 +0100 Subject: [PATCH 11/19] Merge conflict --- src/MSBuild/Resources/Strings.resx | 7 ------- src/MSBuild/TerminalLogger/TerminalLogger.cs | 15 ++++++--------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/MSBuild/Resources/Strings.resx b/src/MSBuild/Resources/Strings.resx index efeca5995c5..f9ee2c769cb 100644 --- a/src/MSBuild/Resources/Strings.resx +++ b/src/MSBuild/Resources/Strings.resx @@ -1105,9 +1105,6 @@ LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. -<<<<<<< HEAD - -======= MSBUILD : error MSB1067: Must provide a feature name for the featureavailability switch. @@ -1117,7 +1114,6 @@ ->>>>>>> main MSBUILD : error MSB1014: Must provide an item name for the getItem switch. {StrBegin="MSBUILD : error MSB1014: "}UE: This happens if the user does something like "msbuild.exe -getItem". The user must pass in an actual item name @@ -1610,7 +1606,6 @@ Terminal Logger was not used because the output is being redirected to a file. -<<<<<<< HEAD {0}{1} test {2} ({3}s) @@ -1637,7 +1632,6 @@ {0} string, localized result e.g. Failed surrounded by ANSI colors. {1-5} whole number -======= ->>>>>>> main diff --git a/src/MSBuild/TerminalLogger/TerminalLogger.cs b/src/MSBuild/TerminalLogger/TerminalLogger.cs index 932efafa46c..ad4acf6d8f2 100644 --- a/src/MSBuild/TerminalLogger/TerminalLogger.cs +++ b/src/MSBuild/TerminalLogger/TerminalLogger.cs @@ -623,17 +623,14 @@ private void TargetFinished(object sender, TargetFinishedEventArgs e) private void TaskStarted(object sender, TaskStartedEventArgs e) { var buildEventContext = e.BuildEventContext; - if (_restoreContext is null && buildEventContext is not null) + if (_restoreContext is null && buildEventContext is not null && e.TaskName == "MSBuild") { - if (e.TaskName == "MSBuild") - { - // This will yield the node, so preemptively mark it idle - UpdateNodeStatus(buildEventContext, null); + // This will yield the node, so preemptively mark it idle + UpdateNodeStatus(buildEventContext, null); - if (_projects.TryGetValue(new ProjectContext(buildEventContext), out Project? project)) - { - project.Stopwatch.Stop(); - } + if (_projects.TryGetValue(new ProjectContext(buildEventContext), out Project? project)) + { + project.Stopwatch.Stop(); } } } From 11248b188203c6665232b90e5445479bf4a4b5bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Thu, 8 Feb 2024 17:31:19 +0100 Subject: [PATCH 12/19] duration fix --- src/MSBuild/TerminalLogger/Project.cs | 2 +- src/MSBuild/TerminalLogger/TerminalLogger.cs | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/MSBuild/TerminalLogger/Project.cs b/src/MSBuild/TerminalLogger/Project.cs index 403a66375e7..959bab12960 100644 --- a/src/MSBuild/TerminalLogger/Project.cs +++ b/src/MSBuild/TerminalLogger/Project.cs @@ -47,7 +47,7 @@ public Project(string? targetFramework, StopwatchAbstraction? stopwatch) public string? TargetFramework { get; } /// - /// True when the project has run target with name "_TestRunStart" defined in . + /// True when the project has run target with name "_TestRunStart" defined in . /// public bool IsTestProject { get; set; } diff --git a/src/MSBuild/TerminalLogger/TerminalLogger.cs b/src/MSBuild/TerminalLogger/TerminalLogger.cs index ad4acf6d8f2..be5876fd34c 100644 --- a/src/MSBuild/TerminalLogger/TerminalLogger.cs +++ b/src/MSBuild/TerminalLogger/TerminalLogger.cs @@ -180,9 +180,9 @@ public ProjectContext(BuildEventContext context) private ConcurrentBag _testRunSummaries = new(); /// - /// Name of target that identifies a project that has tests. + /// Name of target that identifies a project that has tests, and that they just started. /// - private static string _testTarget = "_TestRunStart"; + private static string _testStartTarget = "_TestRunStart"; /// /// Time of the oldest observed test target start. @@ -351,6 +351,8 @@ private void BuildFinished(object sender, BuildFinishedEventArgs e) _buildHasErrors = false; _buildHasWarnings = false; _restoreFailed = false; + _testStartTime = null; + _testEndTime = null; } /// @@ -575,7 +577,7 @@ private void TargetStarted(object sender, TargetStartedEventArgs e) string projectFile = Path.GetFileNameWithoutExtension(e.ProjectFile); - var isTestTarget = e.TargetName == _testTarget; + var isTestTarget = e.TargetName == _testStartTarget; var targetName = isTestTarget ? "Testing" : e.TargetName; if (isTestTarget) @@ -608,13 +610,6 @@ private void UpdateNodeStatus(BuildEventContext buildEventContext, NodeStatus? n /// private void TargetFinished(object sender, TargetFinishedEventArgs e) { - if (e.TargetName == _testTarget) - { - _testEndTime = _testEndTime == null - ? e.Timestamp - : e.Timestamp > _testEndTime - ? e.Timestamp : _testEndTime; - } } /// @@ -713,6 +708,11 @@ private void MessageRaised(object sender, BuildMessageEventArgs e) Skipped = skipped, Failed = failed, }); + + _testEndTime = _testEndTime == null + ? e.Timestamp + : e.Timestamp > _testEndTime + ? e.Timestamp : _testEndTime; break; } } From 828745e47f6a3d218dc83bdae3eb36162bba32fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Fri, 16 Feb 2024 08:29:55 +0100 Subject: [PATCH 13/19] Pass colored parts of target separately --- src/Framework/Logging/AnsiCodes.cs | 15 ----------- .../NodeStatus_Transition_Tests.cs | 15 ++++++++--- src/MSBuild/TerminalLogger/NodeStatus.cs | 17 +++++++++++- src/MSBuild/TerminalLogger/NodesFrame.cs | 27 ++++++++++--------- src/MSBuild/TerminalLogger/TerminalLogger.cs | 10 +++---- 5 files changed, 46 insertions(+), 38 deletions(-) diff --git a/src/Framework/Logging/AnsiCodes.cs b/src/Framework/Logging/AnsiCodes.cs index 399d7817217..7c8deffcafd 100644 --- a/src/Framework/Logging/AnsiCodes.cs +++ b/src/Framework/Logging/AnsiCodes.cs @@ -124,11 +124,6 @@ internal static class AnsiCodes /// public const string RemoveProgress = "\x1b]9;4;0;\x1b\\"; - /// - /// Remove all ANSI escape codes from a string. - /// - private static readonly Regex RemoveAnsiRegex = new Regex(@"\x1B\[[^@-~]*[@-~]", RegexOptions.Compiled); - public static string Colorize(string? s, TerminalColor color) { if (string.IsNullOrWhiteSpace(s)) @@ -157,14 +152,4 @@ public static string MakeBold(string? s) /// Column index. /// Control codes to set the desired position. public static string SetCursorHorizontal(int column) => $"{CSI}{column}G"; - - /// - /// Removes all ANSI codes from the text. - /// - /// - /// - public static string RemoveAnsiCodes(string text) - { - return RemoveAnsiRegex.Replace(text, ""); - } } diff --git a/src/MSBuild.UnitTests/NodeStatus_Transition_Tests.cs b/src/MSBuild.UnitTests/NodeStatus_Transition_Tests.cs index bcc3dc607b8..e7cb136a4ca 100644 --- a/src/MSBuild.UnitTests/NodeStatus_Transition_Tests.cs +++ b/src/MSBuild.UnitTests/NodeStatus_Transition_Tests.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; using Microsoft.Build.Logging.TerminalLogger; - +using Shouldly; using VerifyTests; using VerifyXunit; using Xunit; @@ -28,6 +28,15 @@ public NodeStatus_Transition_Tests() UseProjectRelativeDirectory("Snapshots"); } + [Fact] + public void NodeStatusTargetThrowsForInputWithAnsi() + { +#if DEBUG + Func newNodeStatus = () => new NodeStatus("project", "tfm", AnsiCodes.Colorize("colorized target", TerminalColor.Green), new MockStopwatch()); + newNodeStatus.ShouldThrow().Message.ShouldContain("Target should not contain any escape codes, if you want to colorize target use the other constructor."); +#endif + } + [Fact] public async Task NodeTargetChanges() { @@ -68,7 +77,7 @@ public async Task NodeTargetChangesToColoredTarget() new("Namespace.Project", "TargetFramework", "Testing", new MockStopwatch()) ], [ - new("Namespace.Project", "TargetFramework", $"{AnsiCodes.Colorize("failed", TerminalColor.Red)} MyTestName1", new MockStopwatch()) + new("Namespace.Project", "TargetFramework", TerminalColor.Red, "failed", "MyTestName1", new MockStopwatch()) ]); await VerifyReplay(rendered); @@ -80,7 +89,7 @@ public async Task NodeWithColoredTargetUpdatesTime() // This test look like there is no change between the frames, but we ask the stopwatch for time they will increase the number. // We need this because animations check that NodeStatus reference is the same. // And we cannot use MockStopwatch because we don't know when to call Tick on them, and if we do it right away, the time will update in "both" nodes. - NodeStatus node = new("Namespace.Project", "TargetFramework", $"{AnsiCodes.Colorize("passed", TerminalColor.Green)} MyTestName1", new TickingStopwatch()); + NodeStatus node = new("Namespace.Project", "TargetFramework", TerminalColor.Green, "passed", "MyTestName1", new TickingStopwatch()); var rendered = Animate( [ node, diff --git a/src/MSBuild/TerminalLogger/NodeStatus.cs b/src/MSBuild/TerminalLogger/NodeStatus.cs index d199edc461c..2c3dae9955d 100644 --- a/src/MSBuild/TerminalLogger/NodeStatus.cs +++ b/src/MSBuild/TerminalLogger/NodeStatus.cs @@ -13,17 +13,30 @@ internal class NodeStatus { public string Project { get; } public string? TargetFramework { get; } + public TerminalColor TargetPrefixColor { get; } = TerminalColor.Default; + public string? TargetPrefix { get; } public string Target { get; } public StopwatchAbstraction Stopwatch { get; } public NodeStatus(string project, string? targetFramework, string target, StopwatchAbstraction stopwatch) { + Debug.Assert(!target.Contains("\x1B"), "Target should not contain any escape codes, if you want to colorize target use the other constructor."); Project = project; TargetFramework = targetFramework; Target = target; Stopwatch = stopwatch; } + public NodeStatus(string project, string? targetFramework, TerminalColor targetPrefixColor, string targetPrefix, string target, StopwatchAbstraction stopwatch) + { + Project = project; + TargetFramework = targetFramework; + TargetPrefixColor = targetPrefixColor; + TargetPrefix = targetPrefix; + Target = target; + Stopwatch = stopwatch; + } + /// /// Equality is based on the project, target framework, and target, but NOT the elapsed time. /// @@ -31,7 +44,9 @@ public override bool Equals(object? obj) => obj is NodeStatus status && Project == status.Project && TargetFramework == status.TargetFramework && - Target == status.Target; + Target == status.Target && + TargetPrefixColor == status.TargetPrefixColor && + TargetPrefix == status.TargetPrefix; public override string ToString() { diff --git a/src/MSBuild/TerminalLogger/NodesFrame.cs b/src/MSBuild/TerminalLogger/NodesFrame.cs index cdcaef9f7fa..d2498661143 100644 --- a/src/MSBuild/TerminalLogger/NodesFrame.cs +++ b/src/MSBuild/TerminalLogger/NodesFrame.cs @@ -53,21 +53,21 @@ internal ReadOnlySpan RenderNodeStatus(int i) string project = status.Project; string? targetFramework = status.TargetFramework; string target = status.Target; + string? targetPrefix = status.TargetPrefix; + TerminalColor targetPrefixColor = status.TargetPrefixColor; - // The target may contain ANSI codes, we need to remove them to get the real - // size of the string as it will be printed on the screen, so when we move cursor - // backwards we move it by the amount of characters that will be seen by user, and not - // by the amount of characters + ANSI codes (that won't be seen). - // If we don't remove ANSI codes, the cursor will move too much too the left, and time - // time will render before the end of line, instead of at the end of line. - var renderedTarget = AnsiCodes.RemoveAnsiCodes(target); + var targetWithoutAnsiLength = targetPrefix != null + // +1 because we will join them by space in the final output. + ? targetPrefix.Length + 1 + target.Length + : target.Length; - int renderedWidth = Length(durationString, project, targetFramework, renderedTarget); + int renderedWidth = Length(durationString, project, targetFramework, targetWithoutAnsiLength); if (renderedWidth > Width) { - renderedWidth -= renderedTarget.Length; - renderedTarget = target = string.Empty; + renderedWidth -= targetWithoutAnsiLength; + targetPrefix = target = string.Empty; + targetWithoutAnsiLength = 0; if (renderedWidth > Width) { @@ -82,13 +82,14 @@ internal ReadOnlySpan RenderNodeStatus(int i) } } - return $"{TerminalLogger.Indentation}{project}{(targetFramework is null ? string.Empty : " ")}{AnsiCodes.Colorize(targetFramework, TerminalLogger.TargetFrameworkColor)} {AnsiCodes.SetCursorHorizontal(MaxColumn)}{AnsiCodes.MoveCursorBackward(renderedTarget.Length + durationString.Length + 1)}{target} {durationString}".AsSpan(); + var renderedTarget = targetPrefix != null ? $"{AnsiCodes.Colorize(targetPrefix, targetPrefixColor)} {target}" : target; + return $"{TerminalLogger.Indentation}{project}{(targetFramework is null ? string.Empty : " ")}{AnsiCodes.Colorize(targetFramework, TerminalLogger.TargetFrameworkColor)} {AnsiCodes.SetCursorHorizontal(MaxColumn)}{AnsiCodes.MoveCursorBackward(targetWithoutAnsiLength + durationString.Length + 1)}{renderedTarget} {durationString}".AsSpan(); - static int Length(string durationString, string project, string? targetFramework, string target) => + static int Length(string durationString, string project, string? targetFramework, int targetWithoutAnsiLength) => TerminalLogger.Indentation.Length + project.Length + 1 + (targetFramework?.Length ?? -1) + 1 + - target.Length + 1 + + targetWithoutAnsiLength + 1 + durationString.Length; } diff --git a/src/MSBuild/TerminalLogger/TerminalLogger.cs b/src/MSBuild/TerminalLogger/TerminalLogger.cs index be5876fd34c..313035e428b 100644 --- a/src/MSBuild/TerminalLogger/TerminalLogger.cs +++ b/src/MSBuild/TerminalLogger/TerminalLogger.cs @@ -675,10 +675,9 @@ private void MessageRaised(object sender, BuildMessageEventArgs e) case "TLTESTPASSED": { var indicator = extendedMessage.ExtendedMetadata!["localizedResult"]!; - var displayName = extendedMessage.ExtendedMetadata!["displayName"]; + var displayName = extendedMessage.ExtendedMetadata!["displayName"]!; - var testResult = $"{AnsiCodes.Colorize(indicator, TerminalColor.Green)} {displayName}"; - var status = new NodeStatus(node.Project, node.TargetFramework, testResult, project.Stopwatch); + var status = new NodeStatus(node.Project, node.TargetFramework, TerminalColor.Green, indicator, displayName, project.Stopwatch); UpdateNodeStatus(buildEventContext, status); break; } @@ -686,10 +685,9 @@ private void MessageRaised(object sender, BuildMessageEventArgs e) case "TLTESTSKIPPED": { var indicator = extendedMessage.ExtendedMetadata!["localizedResult"]!; - var displayName = extendedMessage.ExtendedMetadata!["displayName"]; + var displayName = extendedMessage.ExtendedMetadata!["displayName"]!; - var testResult = $"{AnsiCodes.Colorize(indicator, TerminalColor.Yellow)} {displayName}"; - var status = new NodeStatus(node.Project, node.TargetFramework, testResult, project.Stopwatch); + var status = new NodeStatus(node.Project, node.TargetFramework, TerminalColor.Yellow, indicator, displayName, project.Stopwatch); UpdateNodeStatus(buildEventContext, status); break; } From 3d2b1616c151a448eaa183708f2ef379dad1e8cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Fri, 16 Feb 2024 08:38:36 +0100 Subject: [PATCH 14/19] Document node status ctors, to make it more obvious what the parameters are for --- src/MSBuild/TerminalLogger/NodeStatus.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/MSBuild/TerminalLogger/NodeStatus.cs b/src/MSBuild/TerminalLogger/NodeStatus.cs index 2c3dae9955d..6808278a921 100644 --- a/src/MSBuild/TerminalLogger/NodeStatus.cs +++ b/src/MSBuild/TerminalLogger/NodeStatus.cs @@ -18,6 +18,13 @@ internal class NodeStatus public string Target { get; } public StopwatchAbstraction Stopwatch { get; } + /// + /// Status of a node that is currently doing work. + /// + /// The project that is written on left side. + /// Target framework that is colorized and written on left side after project. + /// The currently running work, usually the currently running target. Written on right. + /// Duration of the current step. Written on right after target. public NodeStatus(string project, string? targetFramework, string target, StopwatchAbstraction stopwatch) { Debug.Assert(!target.Contains("\x1B"), "Target should not contain any escape codes, if you want to colorize target use the other constructor."); @@ -27,6 +34,15 @@ public NodeStatus(string project, string? targetFramework, string target, Stopwa Stopwatch = stopwatch; } + /// + /// Status of a node that is currently doing work. + /// + /// The project that is written on left side. + /// Target framework that is colorized and written on left side after project. + /// Color for the status of the currently running work written on right. + /// Colorized status for the currently running work, written on right, before target, and separated by 1 space from it. + /// The currently running work, usually the currently runnig target. Written on right. + /// Duration of the current step. Written on right after target. public NodeStatus(string project, string? targetFramework, TerminalColor targetPrefixColor, string targetPrefix, string target, StopwatchAbstraction stopwatch) { Project = project; From a150ae8a12c754d5c01b6d5343068b969b448fc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Fri, 16 Feb 2024 09:07:23 +0100 Subject: [PATCH 15/19] Apply suggestions from code review --- src/Framework/Logging/AnsiCodes.cs | 1 - src/MSBuild.UnitTests/NodeStatus_Transition_Tests.cs | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Framework/Logging/AnsiCodes.cs b/src/Framework/Logging/AnsiCodes.cs index 7c8deffcafd..04e716797ec 100644 --- a/src/Framework/Logging/AnsiCodes.cs +++ b/src/Framework/Logging/AnsiCodes.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Text.RegularExpressions; namespace Microsoft.Build.Logging.TerminalLogger; diff --git a/src/MSBuild.UnitTests/NodeStatus_Transition_Tests.cs b/src/MSBuild.UnitTests/NodeStatus_Transition_Tests.cs index e7cb136a4ca..aa9066e4bec 100644 --- a/src/MSBuild.UnitTests/NodeStatus_Transition_Tests.cs +++ b/src/MSBuild.UnitTests/NodeStatus_Transition_Tests.cs @@ -32,6 +32,7 @@ public NodeStatus_Transition_Tests() public void NodeStatusTargetThrowsForInputWithAnsi() { #if DEBUG + // This is testing a Debug.Assert, which won't throw in Release mode. Func newNodeStatus = () => new NodeStatus("project", "tfm", AnsiCodes.Colorize("colorized target", TerminalColor.Green), new MockStopwatch()); newNodeStatus.ShouldThrow().Message.ShouldContain("Target should not contain any escape codes, if you want to colorize target use the other constructor."); #endif From 2e8c3c29c5a1c66150fa38d6379f185af419f1bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Fri, 16 Feb 2024 09:07:50 +0100 Subject: [PATCH 16/19] Update src/Framework/Logging/AnsiCodes.cs --- src/Framework/Logging/AnsiCodes.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Framework/Logging/AnsiCodes.cs b/src/Framework/Logging/AnsiCodes.cs index 04e716797ec..8466220026b 100644 --- a/src/Framework/Logging/AnsiCodes.cs +++ b/src/Framework/Logging/AnsiCodes.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. - namespace Microsoft.Build.Logging.TerminalLogger; /// From 809092096f3ed6672b8b6512bcae976008c66c98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Fri, 16 Feb 2024 16:59:44 +0100 Subject: [PATCH 17/19] Fix extra space --- src/MSBuild.UnitTests/NodeStatus_Transition_Tests.cs | 2 +- src/MSBuild/TerminalLogger/NodeStatus.cs | 8 +++++++- src/MSBuild/TerminalLogger/NodesFrame.cs | 4 ++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/MSBuild.UnitTests/NodeStatus_Transition_Tests.cs b/src/MSBuild.UnitTests/NodeStatus_Transition_Tests.cs index e7cb136a4ca..9fee80689c1 100644 --- a/src/MSBuild.UnitTests/NodeStatus_Transition_Tests.cs +++ b/src/MSBuild.UnitTests/NodeStatus_Transition_Tests.cs @@ -33,7 +33,7 @@ public void NodeStatusTargetThrowsForInputWithAnsi() { #if DEBUG Func newNodeStatus = () => new NodeStatus("project", "tfm", AnsiCodes.Colorize("colorized target", TerminalColor.Green), new MockStopwatch()); - newNodeStatus.ShouldThrow().Message.ShouldContain("Target should not contain any escape codes, if you want to colorize target use the other constructor."); + newNodeStatus.ShouldThrow().Message.ShouldContain("Target should not contain any escape codes, if you want to colorize target use the other constructor."); #endif } diff --git a/src/MSBuild/TerminalLogger/NodeStatus.cs b/src/MSBuild/TerminalLogger/NodeStatus.cs index 6808278a921..021f506b061 100644 --- a/src/MSBuild/TerminalLogger/NodeStatus.cs +++ b/src/MSBuild/TerminalLogger/NodeStatus.cs @@ -1,6 +1,7 @@ // 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.Diagnostics; using Microsoft.Build.Shared; @@ -27,7 +28,12 @@ internal class NodeStatus /// Duration of the current step. Written on right after target. public NodeStatus(string project, string? targetFramework, string target, StopwatchAbstraction stopwatch) { - Debug.Assert(!target.Contains("\x1B"), "Target should not contain any escape codes, if you want to colorize target use the other constructor."); +#if DEBUG + if (target.Contains("\x1B")) + { + throw new ArgumentException("Target should not contain any escape codes, if you want to colorize target use the other constructor."); + } +#endif Project = project; TargetFramework = targetFramework; Target = target; diff --git a/src/MSBuild/TerminalLogger/NodesFrame.cs b/src/MSBuild/TerminalLogger/NodesFrame.cs index d2498661143..3c6fd5d8c6d 100644 --- a/src/MSBuild/TerminalLogger/NodesFrame.cs +++ b/src/MSBuild/TerminalLogger/NodesFrame.cs @@ -56,7 +56,7 @@ internal ReadOnlySpan RenderNodeStatus(int i) string? targetPrefix = status.TargetPrefix; TerminalColor targetPrefixColor = status.TargetPrefixColor; - var targetWithoutAnsiLength = targetPrefix != null + var targetWithoutAnsiLength = !string.IsNullOrWhiteSpace(targetPrefix) // +1 because we will join them by space in the final output. ? targetPrefix.Length + 1 + target.Length : target.Length; @@ -82,7 +82,7 @@ internal ReadOnlySpan RenderNodeStatus(int i) } } - var renderedTarget = targetPrefix != null ? $"{AnsiCodes.Colorize(targetPrefix, targetPrefixColor)} {target}" : target; + var renderedTarget = !string.IsNullOrWhiteSpace(targetPrefix) ? $"{AnsiCodes.Colorize(targetPrefix, targetPrefixColor)} {target}" : target; return $"{TerminalLogger.Indentation}{project}{(targetFramework is null ? string.Empty : " ")}{AnsiCodes.Colorize(targetFramework, TerminalLogger.TargetFrameworkColor)} {AnsiCodes.SetCursorHorizontal(MaxColumn)}{AnsiCodes.MoveCursorBackward(targetWithoutAnsiLength + durationString.Length + 1)}{renderedTarget} {durationString}".AsSpan(); static int Length(string durationString, string project, string? targetFramework, int targetWithoutAnsiLength) => From bbf5b798ed945127b2468afdd36aaafddeaad65e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Mon, 19 Feb 2024 10:15:03 +0100 Subject: [PATCH 18/19] Suppress nullcheck because we check it above --- src/MSBuild/TerminalLogger/NodesFrame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MSBuild/TerminalLogger/NodesFrame.cs b/src/MSBuild/TerminalLogger/NodesFrame.cs index 3c6fd5d8c6d..38c82c36f93 100644 --- a/src/MSBuild/TerminalLogger/NodesFrame.cs +++ b/src/MSBuild/TerminalLogger/NodesFrame.cs @@ -58,7 +58,7 @@ internal ReadOnlySpan RenderNodeStatus(int i) var targetWithoutAnsiLength = !string.IsNullOrWhiteSpace(targetPrefix) // +1 because we will join them by space in the final output. - ? targetPrefix.Length + 1 + target.Length + ? targetPrefix!.Length + 1 + target.Length : target.Length; int renderedWidth = Length(durationString, project, targetFramework, targetWithoutAnsiLength); From 11b8d55add4b9d38fd510795c50d90c162c22a30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Mon, 19 Feb 2024 13:58:29 +0100 Subject: [PATCH 19/19] Fix nits --- .../NodeStatus_SizeChange_Tests.cs | 2 -- src/MSBuild/TerminalLogger/NodeStatus.cs | 5 +--- src/MSBuild/TerminalLogger/TerminalLogger.cs | 27 +++++++------------ src/MSBuild/TerminalLogger/TestSummary.cs | 8 +----- 4 files changed, 11 insertions(+), 31 deletions(-) diff --git a/src/MSBuild.UnitTests/NodeStatus_SizeChange_Tests.cs b/src/MSBuild.UnitTests/NodeStatus_SizeChange_Tests.cs index 238ba625ae3..ba6e2f50ecd 100644 --- a/src/MSBuild.UnitTests/NodeStatus_SizeChange_Tests.cs +++ b/src/MSBuild.UnitTests/NodeStatus_SizeChange_Tests.cs @@ -3,10 +3,8 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Text; -using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.Build.Logging.TerminalLogger; diff --git a/src/MSBuild/TerminalLogger/NodeStatus.cs b/src/MSBuild/TerminalLogger/NodeStatus.cs index 021f506b061..3b3de635dee 100644 --- a/src/MSBuild/TerminalLogger/NodeStatus.cs +++ b/src/MSBuild/TerminalLogger/NodeStatus.cs @@ -50,13 +50,10 @@ public NodeStatus(string project, string? targetFramework, string target, Stopwa /// The currently running work, usually the currently runnig target. Written on right. /// Duration of the current step. Written on right after target. public NodeStatus(string project, string? targetFramework, TerminalColor targetPrefixColor, string targetPrefix, string target, StopwatchAbstraction stopwatch) + : this(project, targetFramework, target, stopwatch) { - Project = project; - TargetFramework = targetFramework; TargetPrefixColor = targetPrefixColor; TargetPrefix = targetPrefix; - Target = target; - Stopwatch = stopwatch; } /// diff --git a/src/MSBuild/TerminalLogger/TerminalLogger.cs b/src/MSBuild/TerminalLogger/TerminalLogger.cs index 313035e428b..195ebb95789 100644 --- a/src/MSBuild/TerminalLogger/TerminalLogger.cs +++ b/src/MSBuild/TerminalLogger/TerminalLogger.cs @@ -9,7 +9,6 @@ using Microsoft.Build.Shared; using System.Text.RegularExpressions; using System.Diagnostics; -using System.Collections.Concurrent; #if NET7_0_OR_GREATER using System.Diagnostics.CodeAnalysis; @@ -177,7 +176,7 @@ public ProjectContext(BuildEventContext context) /// /// One summary per finished project test run. /// - private ConcurrentBag _testRunSummaries = new(); + private List _testRunSummaries = new(); /// /// Name of target that identifies a project that has tests, and that they just started. @@ -294,9 +293,6 @@ private void BuildFinished(object sender, BuildFinishedEventArgs e) _projects.Clear(); - var testRunSummaries = _testRunSummaries.ToList(); - _testRunSummaries = new ConcurrentBag(); - Terminal.BeginUpdate(); try { @@ -317,15 +313,15 @@ private void BuildFinished(object sender, BuildFinishedEventArgs e) duration)); } - if (testRunSummaries.Any()) + 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 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) || _buildHasErrors + var colorizedResult = _testRunSummaries.Any(t => t.Failed > 0) || _buildHasErrors ? AnsiCodes.Colorize(ResourceUtilities.GetResourceString("BuildResult_Failed"), TerminalColor.Red) : AnsiCodes.Colorize(ResourceUtilities.GetResourceString("BuildResult_Succeeded"), TerminalColor.Green); @@ -348,6 +344,7 @@ private void BuildFinished(object sender, BuildFinishedEventArgs e) Terminal.EndUpdate(); } + _testRunSummaries.Clear(); _buildHasErrors = false; _buildHasWarnings = false; _restoreFailed = false; @@ -699,13 +696,7 @@ private void MessageRaised(object sender, BuildMessageEventArgs e) _ = int.TryParse(extendedMessage.ExtendedMetadata!["skipped"]!, out int skipped); _ = int.TryParse(extendedMessage.ExtendedMetadata!["failed"]!, out int failed); - _testRunSummaries.Add(new TestSummary - { - Total = total, - Passed = passed, - Skipped = skipped, - Failed = failed, - }); + _testRunSummaries.Add(new TestSummary(total, passed, skipped, failed)); _testEndTime = _testEndTime == null ? e.Timestamp diff --git a/src/MSBuild/TerminalLogger/TestSummary.cs b/src/MSBuild/TerminalLogger/TestSummary.cs index c7a800d4ad8..dff316e37b8 100644 --- a/src/MSBuild/TerminalLogger/TestSummary.cs +++ b/src/MSBuild/TerminalLogger/TestSummary.cs @@ -5,11 +5,5 @@ namespace Microsoft.Build.Logging.TerminalLogger { - internal class TestSummary - { - public int Total { get; set; } - public int Passed { get; set; } - public int Skipped { get; set; } - public int Failed { get; set; } - } + internal readonly record struct TestSummary(int Total, int Passed, int Skipped, int Failed); }