From 8080b4c167af3a46db4092eac4a71e1b3545a969 Mon Sep 17 00:00:00 2001 From: Roman Konecny Date: Fri, 27 May 2022 10:44:14 +0200 Subject: [PATCH 01/10] Solving memory leak by reusing BuildManager and ProjectRoolElementCache --- src/Build/Definition/ProjectCollection.cs | 43 ++++++++++++++++++- .../PublicAPI/net/PublicAPI.Unshipped.txt | 1 + .../netstandard/PublicAPI.Unshipped.txt | 1 + src/MSBuild/XMake.cs | 12 +++++- 4 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/Build/Definition/ProjectCollection.cs b/src/Build/Definition/ProjectCollection.cs index 485b905abe0..096fcc073a4 100644 --- a/src/Build/Definition/ProjectCollection.cs +++ b/src/Build/Definition/ProjectCollection.cs @@ -145,6 +145,8 @@ public void Dispose() /// private static string s_assemblyDisplayVersion; + private static ProjectRootElementCacheBase s_projectRootElementCache = null; + /// /// The projects loaded into this collection. /// @@ -302,6 +304,26 @@ public ProjectCollection(IDictionary globalProperties, IEnumerab /// If set to true, only critical events will be logged. /// If set to true, load all projects as read-only. public ProjectCollection(IDictionary globalProperties, IEnumerable loggers, IEnumerable remoteLoggers, ToolsetDefinitionLocations toolsetDefinitionLocations, int maxNodeCount, bool onlyLogCriticalEvents, bool loadProjectsReadOnly) + : this(globalProperties, loggers, remoteLoggers, toolsetDefinitionLocations, maxNodeCount, onlyLogCriticalEvents, loadProjectsReadOnly, reuseProjectRootElementCache: false) + { + } + + /// + /// Instantiates a project collection with specified global properties and loggers and using the + /// specified toolset locations, node count, and setting of onlyLogCriticalEvents. + /// Global properties and loggers may be null. + /// Throws InvalidProjectFileException if any of the global properties are reserved. + /// May throw InvalidToolsetDefinitionException. + /// + /// The default global properties to use. May be null. + /// The loggers to register. May be null and specified to any build instead. + /// Any remote loggers to register. May be null and specified to any build instead. + /// The locations from which to load toolsets. + /// The maximum number of nodes to use for building. + /// If set to true, only critical events will be logged. + /// If set to true, load all projects as read-only. + /// If set to true, it will try to reuse singleton. + public ProjectCollection(IDictionary globalProperties, IEnumerable loggers, IEnumerable remoteLoggers, ToolsetDefinitionLocations toolsetDefinitionLocations, int maxNodeCount, bool onlyLogCriticalEvents, bool loadProjectsReadOnly, bool reuseProjectRootElementCache) { _loadedProjects = new LoadedProjectCollection(); ToolsetLocations = toolsetDefinitionLocations; @@ -311,10 +333,23 @@ public ProjectCollection(IDictionary globalProperties, IEnumerab { ProjectRootElementCache = new SimpleProjectRootElementCache(); } + else if (reuseProjectRootElementCache && s_projectRootElementCache != null) + { + ProjectRootElementCache = s_projectRootElementCache; + } else { - ProjectRootElementCache = new ProjectRootElementCache(autoReloadFromDisk: false, loadProjectsReadOnly); + // When we are reusing ProjectRootElementCache we need to reload XMLs if it has changed between MSBuild Server sessions/builds. + // If we are not reusing, cache will be released at end of build and as we do not support project files will changes during build + // we do not need to auto reload. + bool autoReloadFromDisk = reuseProjectRootElementCache; + ProjectRootElementCache = new ProjectRootElementCache(autoReloadFromDisk, loadProjectsReadOnly); + if (reuseProjectRootElementCache && s_projectRootElementCache == null) + { + s_projectRootElementCache = ProjectRootElementCache; + } } + OnlyLogCriticalEvents = onlyLogCriticalEvents; try @@ -1603,6 +1638,12 @@ protected virtual void Dispose(bool disposing) if (disposing) { ShutDownLoggingService(); + if (ProjectRootElementCache != null) + { + ProjectRootElementCache.ProjectRootElementAddedHandler -= ProjectRootElementCache_ProjectRootElementAddedHandler; + ProjectRootElementCache.ProjectRootElementDirtied -= ProjectRootElementCache_ProjectRootElementDirtiedHandler; + ProjectRootElementCache.ProjectDirtied -= ProjectRootElementCache_ProjectDirtiedHandler; + } Tracing.Dump(); } } diff --git a/src/Build/PublicAPI/net/PublicAPI.Unshipped.txt b/src/Build/PublicAPI/net/PublicAPI.Unshipped.txt index 349a8e57aac..ee20877adfb 100644 --- a/src/Build/PublicAPI/net/PublicAPI.Unshipped.txt +++ b/src/Build/PublicAPI/net/PublicAPI.Unshipped.txt @@ -1,3 +1,4 @@ +Microsoft.Build.Evaluation.ProjectCollection.ProjectCollection(System.Collections.Generic.IDictionary globalProperties, System.Collections.Generic.IEnumerable loggers, System.Collections.Generic.IEnumerable remoteLoggers, Microsoft.Build.Evaluation.ToolsetDefinitionLocations toolsetDefinitionLocations, int maxNodeCount, bool onlyLogCriticalEvents, bool loadProjectsReadOnly, bool reuseProjectRootElementCache) -> void Microsoft.Build.Execution.MSBuildClient Microsoft.Build.Execution.MSBuildClient.Execute(string commandLine, System.Threading.CancellationToken cancellationToken) -> Microsoft.Build.Execution.MSBuildClientExitResult Microsoft.Build.Execution.MSBuildClient.MSBuildClient(string exeLocation, string dllLocation) -> void diff --git a/src/Build/PublicAPI/netstandard/PublicAPI.Unshipped.txt b/src/Build/PublicAPI/netstandard/PublicAPI.Unshipped.txt index 39c901f1b5c..44179d2f0e1 100644 --- a/src/Build/PublicAPI/netstandard/PublicAPI.Unshipped.txt +++ b/src/Build/PublicAPI/netstandard/PublicAPI.Unshipped.txt @@ -1,3 +1,4 @@ +Microsoft.Build.Evaluation.ProjectCollection.ProjectCollection(System.Collections.Generic.IDictionary globalProperties, System.Collections.Generic.IEnumerable loggers, System.Collections.Generic.IEnumerable remoteLoggers, Microsoft.Build.Evaluation.ToolsetDefinitionLocations toolsetDefinitionLocations, int maxNodeCount, bool onlyLogCriticalEvents, bool loadProjectsReadOnly, bool reuseProjectRootElementCache) -> void Microsoft.Build.Execution.MSBuildClient Microsoft.Build.Execution.MSBuildClient.Execute(string commandLine, System.Threading.CancellationToken cancellationToken) -> Microsoft.Build.Execution.MSBuildClientExitResult Microsoft.Build.Execution.MSBuildClient.MSBuildClient(string exeLocation, string dllLocation) -> void diff --git a/src/MSBuild/XMake.cs b/src/MSBuild/XMake.cs index b7d98c179d5..37434c6fc65 100644 --- a/src/MSBuild/XMake.cs +++ b/src/MSBuild/XMake.cs @@ -1082,7 +1082,8 @@ string[] commandLine toolsetDefinitionLocations, cpuCount, onlyLogCriticalEvents, - loadProjectsReadOnly: !preprocessOnly + loadProjectsReadOnly: !preprocessOnly, + reuseProjectRootElementCache: s_isServerNode ); if (toolsVersion != null && !projectCollection.ContainsToolset(toolsVersion)) @@ -1315,7 +1316,14 @@ string[] commandLine FileUtilities.ClearCacheDirectory(); projectCollection?.Dispose(); - BuildManager.DefaultBuildManager.Dispose(); + // Build manager shall be reused for all build sessions. + // If, for one reason or another, this behavior needs to change in future + // please be aware that current code creates and keep running InProcNode even + // when its owning default build manager is disposed resulting in leek of memory and threads. + if (!s_isServerNode) + { + BuildManager.DefaultBuildManager.Dispose(); + } } return success; From e41cf8a6ff737fe197b89d7c856444a9f39b89b6 Mon Sep 17 00:00:00 2001 From: Roman Konecny Date: Fri, 27 May 2022 12:13:16 +0200 Subject: [PATCH 02/10] Do not clear project root element cache if in auto reload. --- src/Build/Evaluation/ProjectRootElementCache.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Build/Evaluation/ProjectRootElementCache.cs b/src/Build/Evaluation/ProjectRootElementCache.cs index 17ecae43227..208a43ed668 100644 --- a/src/Build/Evaluation/ProjectRootElementCache.cs +++ b/src/Build/Evaluation/ProjectRootElementCache.cs @@ -415,6 +415,12 @@ internal override void Clear() /// internal override void DiscardImplicitReferences() { + if (_autoReloadFromDisk) + { + // no need to clear it, as auto reload properly invalidates caches if changed. + return; + } + lock (_locker) { // Make a new Weak cache only with items that have been explicitly loaded, this will be a small number, there will most likely From 447225c121b96cdadf7bec6ca0e8d2ffb15900e2 Mon Sep 17 00:00:00 2001 From: Roman Konecny Date: Fri, 27 May 2022 22:02:32 +0200 Subject: [PATCH 03/10] Reduce if Co-authored-by: Forgind --- src/Build/Definition/ProjectCollection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Build/Definition/ProjectCollection.cs b/src/Build/Definition/ProjectCollection.cs index 096fcc073a4..f5d45290cd2 100644 --- a/src/Build/Definition/ProjectCollection.cs +++ b/src/Build/Definition/ProjectCollection.cs @@ -344,7 +344,7 @@ public ProjectCollection(IDictionary globalProperties, IEnumerab // we do not need to auto reload. bool autoReloadFromDisk = reuseProjectRootElementCache; ProjectRootElementCache = new ProjectRootElementCache(autoReloadFromDisk, loadProjectsReadOnly); - if (reuseProjectRootElementCache && s_projectRootElementCache == null) + if (reuseProjectRootElementCache) { s_projectRootElementCache = ProjectRootElementCache; } From 8f67732f52593a7a50dc03869885b11514e5d88f Mon Sep 17 00:00:00 2001 From: Roman Konecny Date: Thu, 2 Jun 2022 15:20:03 +0200 Subject: [PATCH 04/10] Sending and applying target console configuration to Server --- src/Build/BackEnd/Client/MSBuildClient.cs | 121 ++++++++- src/Build/BackEnd/Node/OutOfProcServerNode.cs | 26 +- .../BackEnd/Node/ServerNodeBuildCommand.cs | 41 ++- src/Build/Logging/BaseConsoleLogger.cs | 246 +++++++++++++++--- src/Build/Logging/ConsoleLogger.cs | 9 +- .../ParallelLogger/ParallelConsoleLogger.cs | 2 +- src/Framework/NativeMethods.cs | 1 + src/MSBuild/XMake.cs | 2 +- 8 files changed, 388 insertions(+), 60 deletions(-) diff --git a/src/Build/BackEnd/Client/MSBuildClient.cs b/src/Build/BackEnd/Client/MSBuildClient.cs index c8b3c4adc7f..8e0528c3e91 100644 --- a/src/Build/BackEnd/Client/MSBuildClient.cs +++ b/src/Build/BackEnd/Client/MSBuildClient.cs @@ -82,6 +82,28 @@ public sealed class MSBuildClient private int _numConsoleWritePackets; private long _sizeOfConsoleWritePackets; + /// + /// Width of the Console output device or -1 if unknown. + /// + private int _consoleBufferWidth; + + /// + /// True if console output accept ANSI colors codes. + /// False if console does not support ANSI codes or output is redirected to non screen type such as file or nul. + /// + private bool _acceptAnsiColorCodes; + + /// + /// True if console output is screen. It is expected that non screen output is post-processed and often does not need wrapping and coloring. + /// False if output is redirected to non screen type such as file or nul. + /// + private bool _consoleIsScreen; + + /// + /// Background color of client console, -1 if not detectable. + /// + private ConsoleColor _consoleBackgroundColor; + /// /// Public constructor with parameters. /// @@ -155,6 +177,8 @@ public MSBuildClientExitResult Execute(string commandLine, CancellationToken can return _exitResult; } + ConfigureAndQueryConsoleProperties(); + // Send build command. // Let's send it outside the packet pump so that we easier and quicker deal with possible issues with connection to server. MSBuildEventSource.Log.MSBuildServerBuildStart(commandLine); @@ -184,11 +208,6 @@ public MSBuildClientExitResult Execute(string commandLine, CancellationToken can packetPump.PacketReceivedEvent }; - if (NativeMethodsShared.IsWindows) - { - SupportVT100(); - } - while (!_buildFinished) { int index = WaitHandle.WaitAny(waitHandles); @@ -228,13 +247,89 @@ public MSBuildClientExitResult Execute(string commandLine, CancellationToken can return _exitResult; } - private void SupportVT100() + private void ConfigureAndQueryConsoleProperties() + { + QueryIsScreenAndTryEnableAnsiColorCodes(); + QueryConsoleBufferWidth(); + QueryConsoleBackgroundColor(); + } + + private void QueryIsScreenAndTryEnableAnsiColorCodes() + { + if (NativeMethodsShared.IsWindows) + { + _acceptAnsiColorCodes = false; + _consoleIsScreen = false; + try + { + IntPtr stdOut = NativeMethodsShared.GetStdHandle(NativeMethodsShared.STD_OUTPUT_HANDLE); + if (NativeMethodsShared.GetConsoleMode(stdOut, out uint consoleMode)) + { + bool success; + if ((consoleMode & NativeMethodsShared.ENABLE_VIRTUAL_TERMINAL_PROCESSING) == NativeMethodsShared.ENABLE_VIRTUAL_TERMINAL_PROCESSING && + (consoleMode & NativeMethodsShared.DISABLE_NEWLINE_AUTO_RETURN) == NativeMethodsShared.DISABLE_NEWLINE_AUTO_RETURN) + { + // Console is already in required state + success = true; + } + else + { + consoleMode |= NativeMethodsShared.ENABLE_VIRTUAL_TERMINAL_PROCESSING | NativeMethodsShared.DISABLE_NEWLINE_AUTO_RETURN; + success = NativeMethodsShared.SetConsoleMode(stdOut, consoleMode); + } + + if (success) + { + _acceptAnsiColorCodes = true; + } + + uint fileType = NativeMethodsShared.GetFileType(stdOut); + // The std out is a char type(LPT or Console) + _consoleIsScreen = fileType == NativeMethodsShared.FILE_TYPE_CHAR; + _acceptAnsiColorCodes &= _consoleIsScreen; + } + } + catch (Exception ex) + { + CommunicationsUtilities.Trace("MSBuild client warning: problem during enabling support for VT100: {0}.", ex); + } + } + else + { + // On posix OSes we expect console always supports VT100 coloring unless it is redirected + _acceptAnsiColorCodes = _consoleIsScreen = !Console.IsOutputRedirected; + } + } + + private void QueryConsoleBufferWidth() { - IntPtr stdOut = NativeMethodsShared.GetStdHandle(NativeMethodsShared.STD_OUTPUT_HANDLE); - if (NativeMethodsShared.GetConsoleMode(stdOut, out uint consoleMode)) + _consoleBufferWidth = -1; + try + { + _consoleBufferWidth = Console.BufferWidth; + } + catch (Exception ex) + { + // on Win8 machines while in IDE Console.BufferWidth will throw (while it talks to native console it gets "operation aborted" native error) + // this is probably temporary workaround till we understand what is the reason for that exception + CommunicationsUtilities.Trace("MSBuild client warning: problem during querying console buffer width.", ex); + } + } + + /// + /// Some platforms do not allow getting current background color. There + /// is not way to check, but not-supported exception is thrown. Assume + /// black, but don't crash. + /// + private void QueryConsoleBackgroundColor() + { + try + { + _consoleBackgroundColor = Console.BackgroundColor; + } + catch (PlatformNotSupportedException) { - consoleMode |= NativeMethodsShared.ENABLE_VIRTUAL_TERMINAL_PROCESSING | NativeMethodsShared.DISABLE_NEWLINE_AUTO_RETURN; - NativeMethodsShared.SetConsoleMode(stdOut, consoleMode); + _consoleBackgroundColor = ConsoleColor.Black; } } @@ -341,7 +436,11 @@ private ServerNodeBuildCommand GetServerNodeBuildCommand(string commandLine) startupDirectory: Directory.GetCurrentDirectory(), buildProcessEnvironment: envVars, CultureInfo.CurrentCulture, - CultureInfo.CurrentUICulture); + CultureInfo.CurrentUICulture, + _consoleBufferWidth, + _acceptAnsiColorCodes, + _consoleIsScreen, + _consoleBackgroundColor); } private ServerNodeHandshake GetHandshake() diff --git a/src/Build/BackEnd/Node/OutOfProcServerNode.cs b/src/Build/BackEnd/Node/OutOfProcServerNode.cs index f795a3eceae..ee7d3072a14 100644 --- a/src/Build/BackEnd/Node/OutOfProcServerNode.cs +++ b/src/Build/BackEnd/Node/OutOfProcServerNode.cs @@ -10,6 +10,7 @@ using Microsoft.Build.Shared; using Microsoft.Build.Internal; using System.Threading.Tasks; +using Microsoft.Build.BackEnd.Logging; namespace Microsoft.Build.Execution { @@ -320,13 +321,34 @@ private void HandleServerNodeBuildCommand(ServerNodeBuildCommand command) return; } - // set build process context + // Set build process context Directory.SetCurrentDirectory(command.StartupDirectory); CommunicationsUtilities.SetEnvironment(command.BuildProcessEnvironment); Thread.CurrentThread.CurrentCulture = command.Culture; Thread.CurrentThread.CurrentUICulture = command.UICulture; - // configure console output redirection + // Configure console configuration so Loggers can change their behavior based on Target (client) Console properties. + ConsoleConfiguration.Provider = new TargetConsoleConfiguration(command.ConsoleBufferWidth, command.AcceptAnsiColorCodes, command.ConsoleIsScreen, command.ConsoleBackgroundColor); + + // Also try our best to increase chance custom Loggers which use Console static members will work as expected. + try + { + if (NativeMethodsShared.IsWindows && command.ConsoleBufferWidth > 0) + { + Console.BufferWidth = command.ConsoleBufferWidth; + } + + if ((int)command.ConsoleBackgroundColor != -1) + { + Console.BackgroundColor = command.ConsoleBackgroundColor; + } + } + catch (Exception) + { + // Ignore exception, it is best effort only + } + + // Configure console output redirection var oldOut = Console.Out; var oldErr = Console.Error; (int exitCode, string exitType) buildResult; diff --git a/src/Build/BackEnd/Node/ServerNodeBuildCommand.cs b/src/Build/BackEnd/Node/ServerNodeBuildCommand.cs index 48ab050cf1e..21945a1344d 100644 --- a/src/Build/BackEnd/Node/ServerNodeBuildCommand.cs +++ b/src/Build/BackEnd/Node/ServerNodeBuildCommand.cs @@ -1,6 +1,5 @@ // 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; @@ -18,6 +17,10 @@ internal sealed class ServerNodeBuildCommand : INodePacket private Dictionary _buildProcessEnvironment = default!; private CultureInfo _culture = default!; private CultureInfo _uiCulture = default!; + private int _consoleBufferWidth = default; + private bool _acceptAnsiColorCodes = default; + private ConsoleColor _consoleBackgroundColor = default; + private bool _consoleIsScreen = default; /// /// Retrieves the packet type. @@ -49,6 +52,30 @@ internal sealed class ServerNodeBuildCommand : INodePacket /// public CultureInfo UICulture => _uiCulture; + /// + /// Buffer width of destination Console. + /// Console loggers are supposed, on Windows OS, to be wrapping to avoid output trimming. + /// -1 console buffer width can't be obtained. + /// + public int ConsoleBufferWidth => _consoleBufferWidth; + + /// + /// True if console output accept ANSI colors codes. + /// False if output is redirected to non screen type such as file or nul. + /// + public bool AcceptAnsiColorCodes => _acceptAnsiColorCodes; + + /// + /// True if console output is screen. It is expected that non screen output is post-processed and often does not need wrapping and coloring. + /// False if output is redirected to non screen type such as file or nul. + /// + public bool ConsoleIsScreen => _consoleIsScreen; + + /// + /// Background color of client console, -1 if not detectable + /// + public ConsoleColor ConsoleBackgroundColor => _consoleBackgroundColor; + /// /// Private constructor for deserialization /// @@ -56,13 +83,19 @@ private ServerNodeBuildCommand() { } - public ServerNodeBuildCommand(string commandLine, string startupDirectory, Dictionary buildProcessEnvironment, CultureInfo culture, CultureInfo uiCulture) + public ServerNodeBuildCommand(string commandLine, string startupDirectory, Dictionary buildProcessEnvironment, CultureInfo culture, CultureInfo uiCulture, + int consoleBufferWidth, bool acceptAnsiColorCodes, bool consoleIsScreen, ConsoleColor consoleBackgroundColor) { _commandLine = commandLine; _startupDirectory = startupDirectory; _buildProcessEnvironment = buildProcessEnvironment; _culture = culture; _uiCulture = uiCulture; + + _consoleBufferWidth = consoleBufferWidth; + _acceptAnsiColorCodes = acceptAnsiColorCodes; + _consoleIsScreen = consoleIsScreen; + _consoleBackgroundColor = consoleBackgroundColor; } /// @@ -76,6 +109,10 @@ public void Translate(ITranslator translator) translator.TranslateDictionary(ref _buildProcessEnvironment, StringComparer.OrdinalIgnoreCase); translator.TranslateCulture(ref _culture); translator.TranslateCulture(ref _uiCulture); + translator.Translate(ref _consoleBufferWidth); + translator.Translate(ref _acceptAnsiColorCodes); + translator.Translate(ref _consoleIsScreen); + translator.TranslateEnum(ref _consoleBackgroundColor, (int)_consoleBackgroundColor); } /// diff --git a/src/Build/Logging/BaseConsoleLogger.cs b/src/Build/Logging/BaseConsoleLogger.cs index ea87f587b70..b2580c7686b 100644 --- a/src/Build/Logging/BaseConsoleLogger.cs +++ b/src/Build/Logging/BaseConsoleLogger.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; @@ -28,38 +29,8 @@ namespace Microsoft.Build.BackEnd.Logging internal abstract class BaseConsoleLogger : INodeLogger { - /// - /// When set, we'll try reading background color. - /// - private static bool _supportReadingBackgroundColor = true; - #region Properties - /// - /// Some platforms do not allow getting current background color. There - /// is not way to check, but not-supported exception is thrown. Assume - /// black, but don't crash. - /// - internal static ConsoleColor BackgroundColor - { - get - { - if (_supportReadingBackgroundColor) - { - try - { - return Console.BackgroundColor; - } - catch (PlatformNotSupportedException) - { - _supportReadingBackgroundColor = false; - } - } - - return ConsoleColor.Black; - } - } - /// /// Gets or sets the level of detail to show in the event log. /// @@ -314,16 +285,7 @@ internal void IsRunningWithCharacterFileType() if (NativeMethodsShared.IsWindows) { - // Get the std out handle - IntPtr stdHandle = NativeMethodsShared.GetStdHandle(NativeMethodsShared.STD_OUTPUT_HANDLE); - - if (stdHandle != NativeMethods.InvalidHandle) - { - uint fileType = NativeMethodsShared.GetFileType(stdHandle); - - // The std out is a char type(LPT or Console) - runningWithCharacterFileType = (fileType == NativeMethodsShared.FILE_TYPE_CHAR); - } + runningWithCharacterFileType = ConsoleConfiguration.OutputIsScreen; } } @@ -367,7 +329,7 @@ internal static void SetColor(ConsoleColor c) { try { - Console.ForegroundColor = TransformColor(c, BackgroundColor); + Console.ForegroundColor = TransformColor(c, ConsoleConfiguration.BackgroundColor); } catch (IOException) { @@ -480,7 +442,7 @@ internal void InitializeConsoleMethods(LoggerVerbosity logverbosity, WriteHandle try { - ConsoleColor c = BackgroundColor; + ConsoleColor c = ConsoleConfiguration.BackgroundColor; } catch (IOException) { @@ -1278,4 +1240,204 @@ private bool ApplyVerbosityParameter(string parameterValue) #endregion } + + /// + /// Console configuration needed for proper Console logging. + /// + internal interface IConsoleConfiguration + { + /// + /// Buffer width of destination Console. + /// Console loggers are supposed, on Windows OS, to be wrapping to avoid output trimming. + /// -1 console buffer width can't be obtained. + /// + int BufferWidth { get; } + + /// + /// True if console output accept ANSI colors codes. + /// False if output is redirected to non screen type such as file or nul. + /// + bool AcceptAnsiColorCodes { get; } + + /// + /// True if console output is screen. It is expected that non screen output is post-processed and often does not need wrapping and coloring. + /// False if output is redirected to non screen type such as file or nul. + /// + bool OutputIsScreen { get; } + + /// + /// Background color of client console, -1 if not detectable + /// Some platforms do not allow getting current background color. There + /// is not way to check, but not-supported exception is thrown. Assume + /// black, but don't crash. + /// + ConsoleColor BackgroundColor { get; } + } + + /// + /// Console configuration of target Console at which we will render output. + /// It is supposed to be Console fromm other process to which output from this process will be redirected. + /// + internal class TargetConsoleConfiguration : IConsoleConfiguration + { + public TargetConsoleConfiguration(int bufferWidth, bool acceptAnsiColorCodes, bool outputIsScreen, ConsoleColor backgroundColor) + { + BufferWidth = bufferWidth; + AcceptAnsiColorCodes = acceptAnsiColorCodes; + OutputIsScreen = outputIsScreen; + BackgroundColor = backgroundColor; + } + + public int BufferWidth { get; } + public bool AcceptAnsiColorCodes { get; } + public bool OutputIsScreen { get; } + public ConsoleColor BackgroundColor { get; } + } + + /// + /// Console configuration of current process Console. + /// + internal class InProcessConsoleConfiguration : IConsoleConfiguration + { + /// + /// When set, we'll try reading background color. + /// + private static bool s_supportReadingBackgroundColor = true; + + public int BufferWidth => Console.BufferWidth; + + public bool AcceptAnsiColorCodes + { + get + { + bool acceptAnsiColorCodes = false; + if (NativeMethodsShared.IsWindows && !Console.IsOutputRedirected) + { + try + { + IntPtr stdOut = NativeMethodsShared.GetStdHandle(NativeMethodsShared.STD_OUTPUT_HANDLE); + if (NativeMethodsShared.GetConsoleMode(stdOut, out uint consoleMode)) + { + acceptAnsiColorCodes = (consoleMode & NativeMethodsShared.ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0; + } + } + catch (Exception ex) + { + Debug.Assert(false, $"MSBuild client warning: problem during enabling support for VT100: {ex}."); + } + } + else + { + // On posix OSes we expect console always supports VT100 coloring unless it is redirected + acceptAnsiColorCodes = !Console.IsOutputRedirected; + } + + return acceptAnsiColorCodes; + } + } + + public ConsoleColor BackgroundColor + { + get + { + if (s_supportReadingBackgroundColor) + { + try + { + return Console.BackgroundColor; + } + catch (PlatformNotSupportedException) + { + s_supportReadingBackgroundColor = false; + } + } + + return ConsoleColor.Black; + } + } + + public bool OutputIsScreen + { + get + { + bool isScreen = false; + + if (NativeMethodsShared.IsWindows) + { + // Get the std out handle + IntPtr stdHandle = NativeMethodsShared.GetStdHandle(NativeMethodsShared.STD_OUTPUT_HANDLE); + + if (stdHandle != NativeMethods.InvalidHandle) + { + uint fileType = NativeMethodsShared.GetFileType(stdHandle); + + // The std out is a char type(LPT or Console) + isScreen = fileType == NativeMethodsShared.FILE_TYPE_CHAR; + } + } + else + { + isScreen = !Console.IsOutputRedirected; + } + + return isScreen; + } + } + } + + /// + /// Target console configuration. + /// If console output is redirected to other process console, like for example MSBuild Server does, + /// we need to know property of target/final console at which our output will be rendered. + /// If console is rendered at current process Console, we grab properties from Console and/or by WinAPI. + /// + internal static class ConsoleConfiguration + { + /// + /// Get or set current target console configuration provider. + /// + public static IConsoleConfiguration Provider + { + get { return Instance.s_instance; } + set { Instance.s_instance = value; } + } + + private static class Instance + { + // Explicit static constructor to tell C# compiler + // not to mark type as beforefieldinit + static Instance() + { + } + + internal static IConsoleConfiguration s_instance = new InProcessConsoleConfiguration(); + } + + /// + /// Buffer width of destination Console. + /// Console loggers are supposed, on Windows OS, to be wrapping to avoid output trimming. + /// -1 console buffer width can't be obtained. + /// + public static int BufferWidth => Provider.BufferWidth; + + /// + /// True if console output accept ANSI colors codes. + /// False if output is redirected to non screen type such as file or nul. + /// + public static bool AcceptAnsiColorCodes => Provider.AcceptAnsiColorCodes; + + /// + /// Background color of client console, -1 if not detectable + /// Some platforms do not allow getting current background color. There + /// is not way to check, but not-supported exception is thrown. Assume + /// black, but don't crash. + /// + public static ConsoleColor BackgroundColor => Provider.BackgroundColor; + + /// + /// True if console output is screen. It is expected that non screen output is post-processed and often does not need wrapping and coloring. + /// False if output is redirected to non screen type such as file or nul. + /// + public static bool OutputIsScreen => Provider.OutputIsScreen; + } } diff --git a/src/Build/Logging/ConsoleLogger.cs b/src/Build/Logging/ConsoleLogger.cs index 543667811bf..d4320ced186 100644 --- a/src/Build/Logging/ConsoleLogger.cs +++ b/src/Build/Logging/ConsoleLogger.cs @@ -3,6 +3,7 @@ using System; +using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Framework; using Microsoft.Build.Shared; @@ -109,6 +110,7 @@ private void InitializeBaseConsoleLogger() bool useMPLogger = false; bool disableConsoleColor = false; bool forceConsoleColor = false; + bool preferConsoleColor = false; if (!string.IsNullOrEmpty(_parameters)) { string[] parameterComponents = _parameters.Split(BaseConsoleLogger.parameterDelimiters); @@ -132,10 +134,15 @@ private void InitializeBaseConsoleLogger() { forceConsoleColor = true; } + if (string.Equals(param, "PREFERCONSOLECOLOR", StringComparison.OrdinalIgnoreCase)) + { + // Use ansi color codes if current target console do support it + preferConsoleColor = ConsoleConfiguration.AcceptAnsiColorCodes; + } } } - if (forceConsoleColor) + if (forceConsoleColor || (!disableConsoleColor && preferConsoleColor)) { _colorSet = BaseConsoleLogger.SetColorAnsi; _colorReset = BaseConsoleLogger.ResetColorAnsi; diff --git a/src/Build/Logging/ParallelLogger/ParallelConsoleLogger.cs b/src/Build/Logging/ParallelLogger/ParallelConsoleLogger.cs index b0d0c7eb7b3..fe63ce04137 100644 --- a/src/Build/Logging/ParallelLogger/ParallelConsoleLogger.cs +++ b/src/Build/Logging/ParallelLogger/ParallelConsoleLogger.cs @@ -88,7 +88,7 @@ private void CheckIfOutputSupportsAlignment() // Get the size of the console buffer so messages can be formatted to the console width try { - _bufferWidth = Console.BufferWidth; + _bufferWidth = ConsoleConfiguration.BufferWidth; _alignMessages = true; } catch (Exception) diff --git a/src/Framework/NativeMethods.cs b/src/Framework/NativeMethods.cs index 5b990331277..2f928186628 100644 --- a/src/Framework/NativeMethods.cs +++ b/src/Framework/NativeMethods.cs @@ -60,6 +60,7 @@ internal static class NativeMethods internal static HandleRef NullHandleRef = new HandleRef(null, IntPtr.Zero); internal static IntPtr NullIntPtr = new IntPtr(0); + internal static IntPtr InvalidHandle = new IntPtr(-1); // As defined in winnt.h: internal const ushort PROCESSOR_ARCHITECTURE_INTEL = 0; diff --git a/src/MSBuild/XMake.cs b/src/MSBuild/XMake.cs index b7d98c179d5..fb13a461da1 100644 --- a/src/MSBuild/XMake.cs +++ b/src/MSBuild/XMake.cs @@ -3202,7 +3202,7 @@ List loggers // Always use ANSI escape codes when the build is initiated by server if (s_isServerNode) { - consoleParameters = AggregateParameters(consoleParameters, new[] { "FORCECONSOLECOLOR" }); + consoleParameters = $"PREFERCONSOLECOLOR;{consoleParameters}"; } // Check to see if there is a possibility we will be logging from an out-of-proc node. From c9c15ac6352644cf9cbe424b090fcb9112ac46a7 Mon Sep 17 00:00:00 2001 From: Roman Konecny Date: Fri, 3 Jun 2022 14:51:08 +0200 Subject: [PATCH 05/10] AddPrefeConsoleColor CLI doc --- src/MSBuild/Resources/Strings.resx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/MSBuild/Resources/Strings.resx b/src/MSBuild/Resources/Strings.resx index c69053daa0c..c33245ceae6 100644 --- a/src/MSBuild/Resources/Strings.resx +++ b/src/MSBuild/Resources/Strings.resx @@ -319,7 +319,9 @@ Copyright (C) Microsoft Corporation. All rights reserved. mode. This logging style is on by default. ForceConsoleColor--Use ANSI console colors even if console does not support it - Verbosity--overrides the -verbosity setting for this + PreferConsoleColor--Use ANSI console colors only if + target console does support it + Verbosity--overrides the -verbosity setting for this logger. Example: -consoleLoggerParameters:PerformanceSummary;NoSummary; From 173060e286dab2cd0d850ff8c914d4b5fe9b7499 Mon Sep 17 00:00:00 2001 From: Roman Konecny Date: Wed, 8 Jun 2022 09:34:10 +0200 Subject: [PATCH 06/10] Update XLF --- 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 ++++-- 13 files changed, 52 insertions(+), 26 deletions(-) diff --git a/src/MSBuild/Resources/xlf/Strings.cs.xlf b/src/MSBuild/Resources/xlf/Strings.cs.xlf index 7ccdc9ab43e..677d56274cc 100644 --- a/src/MSBuild/Resources/xlf/Strings.cs.xlf +++ b/src/MSBuild/Resources/xlf/Strings.cs.xlf @@ -512,13 +512,15 @@ Copyright (C) Microsoft Corporation. Všechna práva vyhrazena. mode. This logging style is on by default. ForceConsoleColor--Use ANSI console colors even if console does not support it - Verbosity--overrides the -verbosity setting for this + PreferConsoleColor--Use ANSI console colors only if + target console does support it + Verbosity--overrides the -verbosity setting for this logger. Example: -consoleLoggerParameters:PerformanceSummary;NoSummary; Verbosity=minimal - -consoleloggerparameters:<parameters> + -consoleloggerparameters:<parameters> Parametry protokolovacího nástroje konzoly. (Krátký tvar: -clp) Dostupné parametry: PerformanceSummary – zobrazí dobu zpracování úloh, cílů diff --git a/src/MSBuild/Resources/xlf/Strings.de.xlf b/src/MSBuild/Resources/xlf/Strings.de.xlf index e2c30e3ac4f..12af6eb477d 100644 --- a/src/MSBuild/Resources/xlf/Strings.de.xlf +++ b/src/MSBuild/Resources/xlf/Strings.de.xlf @@ -509,13 +509,15 @@ Beispiel: mode. This logging style is on by default. ForceConsoleColor--Use ANSI console colors even if console does not support it - Verbosity--overrides the -verbosity setting for this + PreferConsoleColor--Use ANSI console colors only if + target console does support it + Verbosity--overrides the -verbosity setting for this logger. Example: -consoleLoggerParameters:PerformanceSummary;NoSummary; Verbosity=minimal - -consoleloggerparameters:<Parameter> + -consoleloggerparameters:<Parameter> Parameter für die Konsolenprotokollierung. (Kurzform: -clp) Folgende Parameter sind verfügbar: PerformanceSummary: Zeigt die in Aufgaben, Zielen und diff --git a/src/MSBuild/Resources/xlf/Strings.es.xlf b/src/MSBuild/Resources/xlf/Strings.es.xlf index 0195e35549f..d089c52d291 100644 --- a/src/MSBuild/Resources/xlf/Strings.es.xlf +++ b/src/MSBuild/Resources/xlf/Strings.es.xlf @@ -513,13 +513,15 @@ Copyright (C) Microsoft Corporation. Todos los derechos reservados. mode. This logging style is on by default. ForceConsoleColor--Use ANSI console colors even if console does not support it - Verbosity--overrides the -verbosity setting for this + PreferConsoleColor--Use ANSI console colors only if + target console does support it + Verbosity--overrides the -verbosity setting for this logger. Example: -consoleLoggerParameters:PerformanceSummary;NoSummary; Verbosity=minimal - -consoleLoggerParameters:<parámetros> + -consoleLoggerParameters:<parámetros> Parámetros del registrador de consola. (Forma corta: -clp) Los parámetros disponibles son: PerformanceSummary: muestra el tiempo empleado en tareas, destinos diff --git a/src/MSBuild/Resources/xlf/Strings.fr.xlf b/src/MSBuild/Resources/xlf/Strings.fr.xlf index 3065aadd997..b42e8b553ec 100644 --- a/src/MSBuild/Resources/xlf/Strings.fr.xlf +++ b/src/MSBuild/Resources/xlf/Strings.fr.xlf @@ -509,13 +509,15 @@ Copyright (C) Microsoft Corporation. Tous droits réservés. mode. This logging style is on by default. ForceConsoleColor--Use ANSI console colors even if console does not support it - Verbosity--overrides the -verbosity setting for this + PreferConsoleColor--Use ANSI console colors only if + target console does support it + Verbosity--overrides the -verbosity setting for this logger. Example: -consoleLoggerParameters:PerformanceSummary;NoSummary; Verbosity=minimal - -consoleLoggerParameters:<paramètres> + -consoleLoggerParameters:<paramètres> Paramètres du journaliseur de la console. (Forme abrégée : -clp) Paramètres disponibles : PerformanceSummary--Affiche la durée des tâches, des cibles diff --git a/src/MSBuild/Resources/xlf/Strings.it.xlf b/src/MSBuild/Resources/xlf/Strings.it.xlf index 9c59271398b..80ecce717c9 100644 --- a/src/MSBuild/Resources/xlf/Strings.it.xlf +++ b/src/MSBuild/Resources/xlf/Strings.it.xlf @@ -519,13 +519,15 @@ Esempio: mode. This logging style is on by default. ForceConsoleColor--Use ANSI console colors even if console does not support it - Verbosity--overrides the -verbosity setting for this + PreferConsoleColor--Use ANSI console colors only if + target console does support it + Verbosity--overrides the -verbosity setting for this logger. Example: -consoleLoggerParameters:PerformanceSummary;NoSummary; Verbosity=minimal - -consoleLoggerParameters:<parametri> + -consoleLoggerParameters:<parametri> Parametri per il logger di console. Forma breve: -clp. I parametri disponibili sono: PerformanceSummary: indica il tempo impiegato per le diff --git a/src/MSBuild/Resources/xlf/Strings.ja.xlf b/src/MSBuild/Resources/xlf/Strings.ja.xlf index 9c2bee6bc61..75e8bb75f83 100644 --- a/src/MSBuild/Resources/xlf/Strings.ja.xlf +++ b/src/MSBuild/Resources/xlf/Strings.ja.xlf @@ -509,13 +509,15 @@ Copyright (C) Microsoft Corporation.All rights reserved. mode. This logging style is on by default. ForceConsoleColor--Use ANSI console colors even if console does not support it - Verbosity--overrides the -verbosity setting for this + PreferConsoleColor--Use ANSI console colors only if + target console does support it + Verbosity--overrides the -verbosity setting for this logger. Example: -consoleLoggerParameters:PerformanceSummary;NoSummary; Verbosity=minimal - -consoleLoggerParameters:<parameters> + -consoleLoggerParameters:<parameters> コンソール ロガーへのパラメーターです。(短縮形: -clp) 利用可能なパラメーター: PerformanceSummary--タスク、ターゲット、プロジェクトにかかった時間を diff --git a/src/MSBuild/Resources/xlf/Strings.ko.xlf b/src/MSBuild/Resources/xlf/Strings.ko.xlf index 3d3b611cc74..b2cbad8117d 100644 --- a/src/MSBuild/Resources/xlf/Strings.ko.xlf +++ b/src/MSBuild/Resources/xlf/Strings.ko.xlf @@ -509,13 +509,15 @@ Copyright (C) Microsoft Corporation. All rights reserved. mode. This logging style is on by default. ForceConsoleColor--Use ANSI console colors even if console does not support it - Verbosity--overrides the -verbosity setting for this + PreferConsoleColor--Use ANSI console colors only if + target console does support it + Verbosity--overrides the -verbosity setting for this logger. Example: -consoleLoggerParameters:PerformanceSummary;NoSummary; Verbosity=minimal - -consoleLoggerParameters:<parameters> + -consoleLoggerParameters:<parameters> 콘솔 로거에 대한 매개 변수입니다. (약식: -clp) 사용 가능한 매개 변수는 다음과 같습니다. PerformanceSummary--작업, 대상 및 프로젝트에서 소요된 시간을 diff --git a/src/MSBuild/Resources/xlf/Strings.pl.xlf b/src/MSBuild/Resources/xlf/Strings.pl.xlf index 2c2f7464b68..e7dec45a8d7 100644 --- a/src/MSBuild/Resources/xlf/Strings.pl.xlf +++ b/src/MSBuild/Resources/xlf/Strings.pl.xlf @@ -519,13 +519,15 @@ Copyright (C) Microsoft Corporation. Wszelkie prawa zastrzeżone. mode. This logging style is on by default. ForceConsoleColor--Use ANSI console colors even if console does not support it - Verbosity--overrides the -verbosity setting for this + PreferConsoleColor--Use ANSI console colors only if + target console does support it + Verbosity--overrides the -verbosity setting for this logger. Example: -consoleLoggerParameters:PerformanceSummary;NoSummary; Verbosity=minimal - -consoleLoggerParameters:<parametry> + -consoleLoggerParameters:<parametry> Parametry rejestratora konsoli. (Krótka wersja: -clp) Dostępne parametry: PerformanceSummary — pokazuje czas spędzony diff --git a/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf b/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf index 6dc052fc5bd..80b55165e6b 100644 --- a/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf +++ b/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf @@ -510,13 +510,15 @@ isoladamente. mode. This logging style is on by default. ForceConsoleColor--Use ANSI console colors even if console does not support it - Verbosity--overrides the -verbosity setting for this + PreferConsoleColor--Use ANSI console colors only if + target console does support it + Verbosity--overrides the -verbosity setting for this logger. Example: -consoleLoggerParameters:PerformanceSummary;NoSummary; Verbosity=minimal - -consoleLoggerParameters:<parameters> + -consoleLoggerParameters:<parameters> Parâmetros do agente do console. (Forma abreviada: -clp) Os parâmetros disponíveis são: PerformanceSummary – mostrar o tempo gasto nas tarefas, nos destinos diff --git a/src/MSBuild/Resources/xlf/Strings.ru.xlf b/src/MSBuild/Resources/xlf/Strings.ru.xlf index b39acf70142..d59d0abd1c4 100644 --- a/src/MSBuild/Resources/xlf/Strings.ru.xlf +++ b/src/MSBuild/Resources/xlf/Strings.ru.xlf @@ -508,13 +508,15 @@ Copyright (C) Microsoft Corporation. All rights reserved. mode. This logging style is on by default. ForceConsoleColor--Use ANSI console colors even if console does not support it - Verbosity--overrides the -verbosity setting for this + PreferConsoleColor--Use ANSI console colors only if + target console does support it + Verbosity--overrides the -verbosity setting for this logger. Example: -consoleLoggerParameters:PerformanceSummary;NoSummary; Verbosity=minimal - -consoleLoggerParameters:<параметры> + -consoleLoggerParameters:<параметры> Параметры журнала консоли. (Краткая форма: -clp) Доступны следующие параметры: PerformanceSummary--выводить время, затраченное на выполнение задач, diff --git a/src/MSBuild/Resources/xlf/Strings.tr.xlf b/src/MSBuild/Resources/xlf/Strings.tr.xlf index eb65858ec3a..266cc59600b 100644 --- a/src/MSBuild/Resources/xlf/Strings.tr.xlf +++ b/src/MSBuild/Resources/xlf/Strings.tr.xlf @@ -509,13 +509,15 @@ Telif Hakkı (C) Microsoft Corporation. Tüm hakları saklıdır. mode. This logging style is on by default. ForceConsoleColor--Use ANSI console colors even if console does not support it - Verbosity--overrides the -verbosity setting for this + PreferConsoleColor--Use ANSI console colors only if + target console does support it + Verbosity--overrides the -verbosity setting for this logger. Example: -consoleLoggerParameters:PerformanceSummary;NoSummary; Verbosity=minimal - -consoleLoggerParameters:<parametreler> + -consoleLoggerParameters:<parametreler> Konsol günlükçüsü için parametreler. (Kısa biçim: -clp) Kullanılabilir parametreler: PerformanceSummary--Görevlerde, hedeflerde ve diff --git a/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf b/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf index a4fc1ecdd32..ccd7c4c9392 100644 --- a/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf @@ -509,13 +509,15 @@ Copyright (C) Microsoft Corporation. All rights reserved. mode. This logging style is on by default. ForceConsoleColor--Use ANSI console colors even if console does not support it - Verbosity--overrides the -verbosity setting for this + PreferConsoleColor--Use ANSI console colors only if + target console does support it + Verbosity--overrides the -verbosity setting for this logger. Example: -consoleLoggerParameters:PerformanceSummary;NoSummary; Verbosity=minimal - -consoleloggerparameters:<parameters> + -consoleloggerparameters:<parameters> 控制台记录器的参数。(缩写: -clp) 可用参数包括: PerformanceSummary -- 显示在任务、目标和项目上 diff --git a/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf b/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf index 2dc040b10b9..f6306ec8319 100644 --- a/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf @@ -509,13 +509,15 @@ Copyright (C) Microsoft Corporation. 著作權所有,並保留一切權利。 mode. This logging style is on by default. ForceConsoleColor--Use ANSI console colors even if console does not support it - Verbosity--overrides the -verbosity setting for this + PreferConsoleColor--Use ANSI console colors only if + target console does support it + Verbosity--overrides the -verbosity setting for this logger. Example: -consoleLoggerParameters:PerformanceSummary;NoSummary; Verbosity=minimal - -consoleLoggerParameters:<參數> + -consoleLoggerParameters:<參數> 主控台記錄器的參數。(簡短形式: -clp) 可用的參數為: PerformanceSummary--顯示工作、目標 From 1bf7ff0a25c3fa2387894875c2c86a154881d41b Mon Sep 17 00:00:00 2001 From: Roman Konecny Date: Wed, 8 Jun 2022 17:20:20 +0200 Subject: [PATCH 07/10] Temporary debug tracing to catch CI flaky test. --- src/MSBuild.UnitTests/MSBuildServer_Tests.cs | 1 + src/Shared/CommunicationsUtilities.cs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/MSBuild.UnitTests/MSBuildServer_Tests.cs b/src/MSBuild.UnitTests/MSBuildServer_Tests.cs index a7ecdde6176..5e778c340d6 100644 --- a/src/MSBuild.UnitTests/MSBuildServer_Tests.cs +++ b/src/MSBuild.UnitTests/MSBuildServer_Tests.cs @@ -133,6 +133,7 @@ public void VerifyMixedLegacyBehavior() { TransientTestFile project = _env.CreateFile("testProject.proj", printPidContents); _env.SetEnvironmentVariable("MSBUILDUSESERVER", "1"); + _env.SetEnvironmentVariable("MSBUILDDEBUGCOMM", "1"); string output = RunnerUtilities.ExecMSBuild(BuildEnvironmentHelper.Instance.CurrentMSBuildExePath, project.Path, out bool success, false, _output); success.ShouldBeTrue(); diff --git a/src/Shared/CommunicationsUtilities.cs b/src/Shared/CommunicationsUtilities.cs index 50e34e2bc2a..e7172529597 100644 --- a/src/Shared/CommunicationsUtilities.cs +++ b/src/Shared/CommunicationsUtilities.cs @@ -715,7 +715,8 @@ internal static void Trace(int nodeId, string format, params object[] args) fileName += ".txt"; - using (StreamWriter file = FileUtilities.OpenWrite(String.Format(CultureInfo.CurrentCulture, Path.Combine(s_debugDumpPath, fileName), Process.GetCurrentProcess().Id, nodeId), append: true)) + string path = String.Format(CultureInfo.CurrentCulture, Path.Combine(s_debugDumpPath, fileName), Process.GetCurrentProcess().Id, nodeId); + using (StreamWriter file = FileUtilities.OpenWrite(path, append: true)) { string message = String.Format(CultureInfo.CurrentCulture, format, args); long now = DateTime.UtcNow.Ticks; From cfd91992fd839dcd0a63e2869a06865ac2e2727b Mon Sep 17 00:00:00 2001 From: Roman Konecny Date: Fri, 10 Jun 2022 13:33:18 +0200 Subject: [PATCH 08/10] Revert "Temporary debug tracing to catch CI flaky test." This reverts commit 1bf7ff0a25c3fa2387894875c2c86a154881d41b. --- src/MSBuild.UnitTests/MSBuildServer_Tests.cs | 1 - src/Shared/CommunicationsUtilities.cs | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/MSBuild.UnitTests/MSBuildServer_Tests.cs b/src/MSBuild.UnitTests/MSBuildServer_Tests.cs index 5e778c340d6..a7ecdde6176 100644 --- a/src/MSBuild.UnitTests/MSBuildServer_Tests.cs +++ b/src/MSBuild.UnitTests/MSBuildServer_Tests.cs @@ -133,7 +133,6 @@ public void VerifyMixedLegacyBehavior() { TransientTestFile project = _env.CreateFile("testProject.proj", printPidContents); _env.SetEnvironmentVariable("MSBUILDUSESERVER", "1"); - _env.SetEnvironmentVariable("MSBUILDDEBUGCOMM", "1"); string output = RunnerUtilities.ExecMSBuild(BuildEnvironmentHelper.Instance.CurrentMSBuildExePath, project.Path, out bool success, false, _output); success.ShouldBeTrue(); diff --git a/src/Shared/CommunicationsUtilities.cs b/src/Shared/CommunicationsUtilities.cs index e7172529597..50e34e2bc2a 100644 --- a/src/Shared/CommunicationsUtilities.cs +++ b/src/Shared/CommunicationsUtilities.cs @@ -715,8 +715,7 @@ internal static void Trace(int nodeId, string format, params object[] args) fileName += ".txt"; - string path = String.Format(CultureInfo.CurrentCulture, Path.Combine(s_debugDumpPath, fileName), Process.GetCurrentProcess().Id, nodeId); - using (StreamWriter file = FileUtilities.OpenWrite(path, append: true)) + using (StreamWriter file = FileUtilities.OpenWrite(String.Format(CultureInfo.CurrentCulture, Path.Combine(s_debugDumpPath, fileName), Process.GetCurrentProcess().Id, nodeId), append: true)) { string message = String.Format(CultureInfo.CurrentCulture, format, args); long now = DateTime.UtcNow.Ticks; From 9ac3129c99a0c495b2d130fd886126f1631b44e6 Mon Sep 17 00:00:00 2001 From: Roman Konecny Date: Mon, 20 Jun 2022 10:44:37 +0200 Subject: [PATCH 09/10] Typo Co-authored-by: AR-May <67507805+AR-May@users.noreply.github.com> --- src/Build/Logging/BaseConsoleLogger.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Build/Logging/BaseConsoleLogger.cs b/src/Build/Logging/BaseConsoleLogger.cs index b2580c7686b..f449ff0d886 100644 --- a/src/Build/Logging/BaseConsoleLogger.cs +++ b/src/Build/Logging/BaseConsoleLogger.cs @@ -1276,7 +1276,7 @@ internal interface IConsoleConfiguration /// /// Console configuration of target Console at which we will render output. - /// It is supposed to be Console fromm other process to which output from this process will be redirected. + /// It is supposed to be Console from other process to which output from this process will be redirected. /// internal class TargetConsoleConfiguration : IConsoleConfiguration { From 0a8192faf2e8cde140a94a973dc94d5022892bbb Mon Sep 17 00:00:00 2001 From: Roman Konecny Date: Mon, 20 Jun 2022 16:45:26 +0200 Subject: [PATCH 10/10] Encapsulate client console properties --- src/Build/BackEnd/Client/MSBuildClient.cs | 69 ++++++++----------- src/Build/BackEnd/Node/OutOfProcServerNode.cs | 10 +-- .../BackEnd/Node/ServerNodeBuildCommand.cs | 45 +++--------- src/Build/Logging/BaseConsoleLogger.cs | 48 ++++++++++--- 4 files changed, 84 insertions(+), 88 deletions(-) diff --git a/src/Build/BackEnd/Client/MSBuildClient.cs b/src/Build/BackEnd/Client/MSBuildClient.cs index d135f23fd20..273cfd69f84 100644 --- a/src/Build/BackEnd/Client/MSBuildClient.cs +++ b/src/Build/BackEnd/Client/MSBuildClient.cs @@ -11,6 +11,7 @@ using System.Threading; using Microsoft.Build.BackEnd; using Microsoft.Build.BackEnd.Client; +using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Eventing; using Microsoft.Build.Execution; using Microsoft.Build.Framework; @@ -79,26 +80,9 @@ public sealed class MSBuildClient private long _sizeOfConsoleWritePackets; /// - /// Width of the Console output device or -1 if unknown. + /// Capture configuration of Client Console. /// - private int _consoleBufferWidth; - - /// - /// True if console output accept ANSI colors codes. - /// False if console does not support ANSI codes or output is redirected to non screen type such as file or nul. - /// - private bool _acceptAnsiColorCodes; - - /// - /// True if console output is screen. It is expected that non screen output is post-processed and often does not need wrapping and coloring. - /// False if output is redirected to non screen type such as file or nul. - /// - private bool _consoleIsScreen; - - /// - /// Background color of client console, -1 if not detectable. - /// - private ConsoleColor _consoleBackgroundColor; + private TargetConsoleConfiguration? _consoleConfiguration; /// /// Public constructor with parameters. @@ -244,17 +228,20 @@ public MSBuildClientExitResult Execute(string commandLine, CancellationToken can private void ConfigureAndQueryConsoleProperties() { - QueryIsScreenAndTryEnableAnsiColorCodes(); - QueryConsoleBufferWidth(); - QueryConsoleBackgroundColor(); + var (acceptAnsiColorCodes, outputIsScreen) = QueryIsScreenAndTryEnableAnsiColorCodes(); + int bufferWidth = QueryConsoleBufferWidth(); + ConsoleColor backgroundColor = QueryConsoleBackgroundColor(); + + _consoleConfiguration = new TargetConsoleConfiguration(bufferWidth, acceptAnsiColorCodes, outputIsScreen, backgroundColor); } - private void QueryIsScreenAndTryEnableAnsiColorCodes() + private (bool acceptAnsiColorCodes, bool outputIsScreen) QueryIsScreenAndTryEnableAnsiColorCodes() { + bool acceptAnsiColorCodes = false; + bool outputIsScreen = false; + if (NativeMethodsShared.IsWindows) { - _acceptAnsiColorCodes = false; - _consoleIsScreen = false; try { IntPtr stdOut = NativeMethodsShared.GetStdHandle(NativeMethodsShared.STD_OUTPUT_HANDLE); @@ -275,13 +262,13 @@ private void QueryIsScreenAndTryEnableAnsiColorCodes() if (success) { - _acceptAnsiColorCodes = true; + acceptAnsiColorCodes = true; } uint fileType = NativeMethodsShared.GetFileType(stdOut); // The std out is a char type(LPT or Console) - _consoleIsScreen = fileType == NativeMethodsShared.FILE_TYPE_CHAR; - _acceptAnsiColorCodes &= _consoleIsScreen; + outputIsScreen = fileType == NativeMethodsShared.FILE_TYPE_CHAR; + acceptAnsiColorCodes &= outputIsScreen; } } catch (Exception ex) @@ -292,16 +279,18 @@ private void QueryIsScreenAndTryEnableAnsiColorCodes() else { // On posix OSes we expect console always supports VT100 coloring unless it is redirected - _acceptAnsiColorCodes = _consoleIsScreen = !Console.IsOutputRedirected; + acceptAnsiColorCodes = outputIsScreen = !Console.IsOutputRedirected; } + + return (acceptAnsiColorCodes: acceptAnsiColorCodes, outputIsScreen: outputIsScreen); } - private void QueryConsoleBufferWidth() + private int QueryConsoleBufferWidth() { - _consoleBufferWidth = -1; + int consoleBufferWidth = -1; try { - _consoleBufferWidth = Console.BufferWidth; + consoleBufferWidth = Console.BufferWidth; } catch (Exception ex) { @@ -309,6 +298,8 @@ private void QueryConsoleBufferWidth() // this is probably temporary workaround till we understand what is the reason for that exception CommunicationsUtilities.Trace("MSBuild client warning: problem during querying console buffer width.", ex); } + + return consoleBufferWidth; } /// @@ -316,16 +307,19 @@ private void QueryConsoleBufferWidth() /// is not way to check, but not-supported exception is thrown. Assume /// black, but don't crash. /// - private void QueryConsoleBackgroundColor() + private ConsoleColor QueryConsoleBackgroundColor() { + ConsoleColor consoleBackgroundColor; try { - _consoleBackgroundColor = Console.BackgroundColor; + consoleBackgroundColor = Console.BackgroundColor; } catch (PlatformNotSupportedException) { - _consoleBackgroundColor = ConsoleColor.Black; + consoleBackgroundColor = ConsoleColor.Black; } + + return consoleBackgroundColor; } private bool TrySendPacket(Func packetResolver) @@ -420,10 +414,7 @@ private ServerNodeBuildCommand GetServerNodeBuildCommand(string commandLine) buildProcessEnvironment: envVars, CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture, - _consoleBufferWidth, - _acceptAnsiColorCodes, - _consoleIsScreen, - _consoleBackgroundColor); + _consoleConfiguration!); } private ServerNodeHandshake GetHandshake() diff --git a/src/Build/BackEnd/Node/OutOfProcServerNode.cs b/src/Build/BackEnd/Node/OutOfProcServerNode.cs index 19acaec8d01..0053b91705b 100644 --- a/src/Build/BackEnd/Node/OutOfProcServerNode.cs +++ b/src/Build/BackEnd/Node/OutOfProcServerNode.cs @@ -320,19 +320,19 @@ private void HandleServerNodeBuildCommand(ServerNodeBuildCommand command) Thread.CurrentThread.CurrentUICulture = command.UICulture; // Configure console configuration so Loggers can change their behavior based on Target (client) Console properties. - ConsoleConfiguration.Provider = new TargetConsoleConfiguration(command.ConsoleBufferWidth, command.AcceptAnsiColorCodes, command.ConsoleIsScreen, command.ConsoleBackgroundColor); + ConsoleConfiguration.Provider = command.ConsoleConfiguration; // Also try our best to increase chance custom Loggers which use Console static members will work as expected. try { - if (NativeMethodsShared.IsWindows && command.ConsoleBufferWidth > 0) + if (NativeMethodsShared.IsWindows && command.ConsoleConfiguration.BufferWidth > 0) { - Console.BufferWidth = command.ConsoleBufferWidth; + Console.BufferWidth = command.ConsoleConfiguration.BufferWidth; } - if ((int)command.ConsoleBackgroundColor != -1) + if ((int)command.ConsoleConfiguration.BackgroundColor != -1) { - Console.BackgroundColor = command.ConsoleBackgroundColor; + Console.BackgroundColor = command.ConsoleConfiguration.BackgroundColor; } } catch (Exception) diff --git a/src/Build/BackEnd/Node/ServerNodeBuildCommand.cs b/src/Build/BackEnd/Node/ServerNodeBuildCommand.cs index 21945a1344d..a83adf83e8c 100644 --- a/src/Build/BackEnd/Node/ServerNodeBuildCommand.cs +++ b/src/Build/BackEnd/Node/ServerNodeBuildCommand.cs @@ -4,6 +4,8 @@ using System; using System.Collections.Generic; using System.Globalization; +using Microsoft.Build.BackEnd.Logging; +using Microsoft.Build.Shared; namespace Microsoft.Build.BackEnd { @@ -17,10 +19,7 @@ internal sealed class ServerNodeBuildCommand : INodePacket private Dictionary _buildProcessEnvironment = default!; private CultureInfo _culture = default!; private CultureInfo _uiCulture = default!; - private int _consoleBufferWidth = default; - private bool _acceptAnsiColorCodes = default; - private ConsoleColor _consoleBackgroundColor = default; - private bool _consoleIsScreen = default; + private TargetConsoleConfiguration _consoleConfiguration = default!; /// /// Retrieves the packet type. @@ -53,28 +52,9 @@ internal sealed class ServerNodeBuildCommand : INodePacket public CultureInfo UICulture => _uiCulture; /// - /// Buffer width of destination Console. - /// Console loggers are supposed, on Windows OS, to be wrapping to avoid output trimming. - /// -1 console buffer width can't be obtained. + /// Console configuration of Client. /// - public int ConsoleBufferWidth => _consoleBufferWidth; - - /// - /// True if console output accept ANSI colors codes. - /// False if output is redirected to non screen type such as file or nul. - /// - public bool AcceptAnsiColorCodes => _acceptAnsiColorCodes; - - /// - /// True if console output is screen. It is expected that non screen output is post-processed and often does not need wrapping and coloring. - /// False if output is redirected to non screen type such as file or nul. - /// - public bool ConsoleIsScreen => _consoleIsScreen; - - /// - /// Background color of client console, -1 if not detectable - /// - public ConsoleColor ConsoleBackgroundColor => _consoleBackgroundColor; + public TargetConsoleConfiguration ConsoleConfiguration => _consoleConfiguration; /// /// Private constructor for deserialization @@ -84,18 +64,16 @@ private ServerNodeBuildCommand() } public ServerNodeBuildCommand(string commandLine, string startupDirectory, Dictionary buildProcessEnvironment, CultureInfo culture, CultureInfo uiCulture, - int consoleBufferWidth, bool acceptAnsiColorCodes, bool consoleIsScreen, ConsoleColor consoleBackgroundColor) + TargetConsoleConfiguration consoleConfiguration) { + ErrorUtilities.VerifyThrowInternalNull(consoleConfiguration, nameof(consoleConfiguration)); + _commandLine = commandLine; _startupDirectory = startupDirectory; _buildProcessEnvironment = buildProcessEnvironment; _culture = culture; _uiCulture = uiCulture; - - _consoleBufferWidth = consoleBufferWidth; - _acceptAnsiColorCodes = acceptAnsiColorCodes; - _consoleIsScreen = consoleIsScreen; - _consoleBackgroundColor = consoleBackgroundColor; + _consoleConfiguration = consoleConfiguration; } /// @@ -109,10 +87,7 @@ public void Translate(ITranslator translator) translator.TranslateDictionary(ref _buildProcessEnvironment, StringComparer.OrdinalIgnoreCase); translator.TranslateCulture(ref _culture); translator.TranslateCulture(ref _uiCulture); - translator.Translate(ref _consoleBufferWidth); - translator.Translate(ref _acceptAnsiColorCodes); - translator.Translate(ref _consoleIsScreen); - translator.TranslateEnum(ref _consoleBackgroundColor, (int)_consoleBackgroundColor); + translator.Translate(ref _consoleConfiguration, TargetConsoleConfiguration.FactoryForDeserialization); } /// diff --git a/src/Build/Logging/BaseConsoleLogger.cs b/src/Build/Logging/BaseConsoleLogger.cs index f449ff0d886..20bd109a974 100644 --- a/src/Build/Logging/BaseConsoleLogger.cs +++ b/src/Build/Logging/BaseConsoleLogger.cs @@ -1278,20 +1278,50 @@ internal interface IConsoleConfiguration /// Console configuration of target Console at which we will render output. /// It is supposed to be Console from other process to which output from this process will be redirected. /// - internal class TargetConsoleConfiguration : IConsoleConfiguration + internal class TargetConsoleConfiguration : IConsoleConfiguration, ITranslatable { + private int _bufferWidth; + private bool _acceptAnsiColorCodes; + private bool _outputIsScreen; + private ConsoleColor _backgroundColor; + public TargetConsoleConfiguration(int bufferWidth, bool acceptAnsiColorCodes, bool outputIsScreen, ConsoleColor backgroundColor) { - BufferWidth = bufferWidth; - AcceptAnsiColorCodes = acceptAnsiColorCodes; - OutputIsScreen = outputIsScreen; - BackgroundColor = backgroundColor; + _bufferWidth = bufferWidth; + _acceptAnsiColorCodes = acceptAnsiColorCodes; + _outputIsScreen = outputIsScreen; + _backgroundColor = backgroundColor; + } + + /// + /// Constructor for deserialization + /// + private TargetConsoleConfiguration() + { } - public int BufferWidth { get; } - public bool AcceptAnsiColorCodes { get; } - public bool OutputIsScreen { get; } - public ConsoleColor BackgroundColor { get; } + public int BufferWidth => _bufferWidth; + + public bool AcceptAnsiColorCodes => _acceptAnsiColorCodes; + + public bool OutputIsScreen => _outputIsScreen; + + public ConsoleColor BackgroundColor => _backgroundColor; + + public void Translate(ITranslator translator) + { + translator.Translate(ref _bufferWidth); + translator.Translate(ref _acceptAnsiColorCodes); + translator.Translate(ref _outputIsScreen); + translator.TranslateEnum(ref _backgroundColor, (int)_backgroundColor); + } + + internal static TargetConsoleConfiguration FactoryForDeserialization(ITranslator translator) + { + TargetConsoleConfiguration configuration = new(); + configuration.Translate(translator); + return configuration; + } } ///