From 96c28d1979139a4fb078145144b361b33a2b53ce Mon Sep 17 00:00:00 2001 From: Chet Husk Date: Wed, 28 Feb 2024 13:01:05 -0600 Subject: [PATCH 1/6] wip --- src/MSBuild/TerminalLogger/Project.cs | 5 ++ src/MSBuild/TerminalLogger/TerminalLogger.cs | 61 +++++++++++++++++--- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/MSBuild/TerminalLogger/Project.cs b/src/MSBuild/TerminalLogger/Project.cs index 959bab12960..c4386f91c93 100644 --- a/src/MSBuild/TerminalLogger/Project.cs +++ b/src/MSBuild/TerminalLogger/Project.cs @@ -41,6 +41,11 @@ public Project(string? targetFramework, StopwatchAbstraction? stopwatch) /// public ReadOnlyMemory? OutputPath { get; set; } + /// + /// Full path to the 'root' of this project's source control repository, if known. + /// + public ReadOnlyMemory? SourceRoot { get; set; } + /// /// The target framework of the project or null if not multi-targeting. /// diff --git a/src/MSBuild/TerminalLogger/TerminalLogger.cs b/src/MSBuild/TerminalLogger/TerminalLogger.cs index 195ebb95789..40a42f79805 100644 --- a/src/MSBuild/TerminalLogger/TerminalLogger.cs +++ b/src/MSBuild/TerminalLogger/TerminalLogger.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; @@ -9,6 +9,8 @@ using Microsoft.Build.Shared; using System.Text.RegularExpressions; using System.Diagnostics; +using Microsoft.Build.Execution; + #if NET7_0_OR_GREATER using System.Diagnostics.CodeAnalysis; @@ -241,6 +243,8 @@ public void Initialize(IEventSource eventSource) eventSource.TargetFinished += TargetFinished; eventSource.TaskStarted += TaskStarted; + eventSource.StatusEventRaised += StatusMessageRaised; + eventSource.MessageRaised += MessageRaised; eventSource.WarningRaised += WarningRaised; eventSource.ErrorRaised += ErrorRaised; @@ -251,6 +255,11 @@ public void Initialize(IEventSource eventSource) } } + private void StatusMessageRaised(object sender, BuildStatusEventArgs e) + { + + } + /// public void Shutdown() { @@ -520,19 +529,35 @@ private void ProjectFinished(object sender, ProjectFinishedEventArgs e) urlString = uri.AbsoluteUri; } + var relativeDisplayPathSpan = outputPathSpan; + var workingDirectorySpan = _initialWorkingDirectory.AsSpan(); // If the output path is under the initial working directory, make the console output relative to that to save space. - if (outputPathSpan.StartsWith(_initialWorkingDirectory.AsSpan(), FileUtilities.PathComparison)) + if (outputPathSpan.StartsWith(workingDirectorySpan, FileUtilities.PathComparison)) { - if (outputPathSpan.Length > _initialWorkingDirectory.Length - && (outputPathSpan[_initialWorkingDirectory.Length] == Path.DirectorySeparatorChar - || outputPathSpan[_initialWorkingDirectory.Length] == Path.AltDirectorySeparatorChar)) + if (outputPathSpan.Length > workingDirectorySpan.Length + && (outputPathSpan[workingDirectorySpan.Length] == Path.DirectorySeparatorChar + || outputPathSpan[workingDirectorySpan.Length] == Path.AltDirectorySeparatorChar)) { - outputPathSpan = outputPathSpan.Slice(_initialWorkingDirectory.Length + 1); + relativeDisplayPathSpan = outputPathSpan.Slice(workingDirectorySpan.Length + 1); + } + } + // if the output path isn't under the working directory, but is under the source root, make the output relative to that to save space + else if (project.SourceRoot is not null) + { + var sourceRootSpan = project.SourceRoot.Value.Span; + if (outputPathSpan.StartsWith(sourceRootSpan, FileUtilities.PathComparison)) + { + if (outputPathSpan.Length > sourceRootSpan.Length + && (outputPathSpan[sourceRootSpan.Length] == Path.DirectorySeparatorChar + || outputPathSpan[sourceRootSpan.Length] == Path.AltDirectorySeparatorChar)) + { + relativeDisplayPathSpan = outputPathSpan.Slice(sourceRootSpan.Length + 1); + } } } Terminal.WriteLine(ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("ProjectFinished_OutputPath", - $"{AnsiCodes.LinkPrefix}{urlString}{AnsiCodes.LinkInfix}{outputPathSpan.ToString()}{AnsiCodes.LinkSuffix}")); + $"{AnsiCodes.LinkPrefix}{urlString}{AnsiCodes.LinkInfix}{relativeDisplayPathSpan.ToString()}{AnsiCodes.LinkSuffix}")); } else { @@ -609,6 +634,24 @@ private void TargetFinished(object sender, TargetFinishedEventArgs e) { } + private void TryReadSourceControlInformationForProject(BuildEventContext? context, IList sourceRoots) + { + if (context is null) + { + return; + } + + var projectContext = new ProjectContext(context); + if (_projects.TryGetValue(projectContext, out Project? project)) + { + var sourceControlSourceRoot = sourceRoots.FirstOrDefault(root => root.HasMetadata("SourceControl")); + if (sourceControlSourceRoot is not null) + { + project.SourceRoot = sourceControlSourceRoot.EvaluatedInclude.AsMemory(); + } + } + } + /// /// The callback. /// @@ -639,6 +682,10 @@ private void MessageRaised(object sender, BuildMessageEventArgs e) } string? message = e.Message; + if (e is TaskParameterEventArgs taskArgs && taskArgs.ItemType.Equals("SourceRoot", StringComparison.OrdinalIgnoreCase)) + { + TryReadSourceControlInformationForProject(taskArgs.BuildEventContext, taskArgs.Items as IList); + } if (message is not null && e.Importance == MessageImportance.High) { var hasProject = _projects.TryGetValue(new ProjectContext(buildEventContext), out Project? project); From f3e740009d2bd193b2573afaff9cec02c40a62c6 Mon Sep 17 00:00:00 2001 From: Chet Husk Date: Wed, 28 Feb 2024 16:45:32 -0600 Subject: [PATCH 2/6] wip --- src/MSBuild/TerminalLogger/TerminalLogger.cs | 38 ++++++++++++++++---- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/MSBuild/TerminalLogger/TerminalLogger.cs b/src/MSBuild/TerminalLogger/TerminalLogger.cs index 40a42f79805..883cdc929ad 100644 --- a/src/MSBuild/TerminalLogger/TerminalLogger.cs +++ b/src/MSBuild/TerminalLogger/TerminalLogger.cs @@ -10,6 +10,8 @@ using System.Text.RegularExpressions; using System.Diagnostics; using Microsoft.Build.Execution; +using Microsoft.Build.Utilities; + #if NET7_0_OR_GREATER @@ -249,6 +251,11 @@ public void Initialize(IEventSource eventSource) eventSource.WarningRaised += WarningRaised; eventSource.ErrorRaised += ErrorRaised; + if (eventSource is IEventSource3 eventSource3) + { + eventSource3.IncludeTaskInputs(); + } + if (eventSource is IEventSource4 eventSource4) { eventSource4.IncludeEvaluationPropertiesAndItems(); @@ -257,7 +264,17 @@ public void Initialize(IEventSource eventSource) private void StatusMessageRaised(object sender, BuildStatusEventArgs e) { - + // if (e is TaskParameterEventArgs taskArgs) + // { + // Debug.WriteLine(taskArgs.BuildEventContext.TaskId) + // if (taskArgs.Kind == TaskParameterMessageKind.AddItem) + // { + // if (taskArgs.ItemType.Equals("SourceRoot", StringComparison.OrdinalIgnoreCase)) + // { + // TryReadSourceControlInformationForProject(taskArgs.BuildEventContext, taskArgs.Items as IList); + // } + // } + // } } /// @@ -634,9 +651,9 @@ private void TargetFinished(object sender, TargetFinishedEventArgs e) { } - private void TryReadSourceControlInformationForProject(BuildEventContext? context, IList sourceRoots) + private void TryReadSourceControlInformationForProject(BuildEventContext? context, IEnumerable? sourceRoots) { - if (context is null) + if (context is null || sourceRoots is null) { return; } @@ -644,10 +661,10 @@ private void TryReadSourceControlInformationForProject(BuildEventContext? contex var projectContext = new ProjectContext(context); if (_projects.TryGetValue(projectContext, out Project? project)) { - var sourceControlSourceRoot = sourceRoots.FirstOrDefault(root => root.HasMetadata("SourceControl")); + var sourceControlSourceRoot = sourceRoots.FirstOrDefault(root => !string.IsNullOrEmpty(root.GetMetadata("SourceControl"))); if (sourceControlSourceRoot is not null) { - project.SourceRoot = sourceControlSourceRoot.EvaluatedInclude.AsMemory(); + project.SourceRoot = sourceControlSourceRoot.ItemSpec.AsMemory(); } } } @@ -682,9 +699,16 @@ private void MessageRaised(object sender, BuildMessageEventArgs e) } string? message = e.Message; - if (e is TaskParameterEventArgs taskArgs && taskArgs.ItemType.Equals("SourceRoot", StringComparison.OrdinalIgnoreCase)) + if (e is TaskParameterEventArgs taskArgs) { - TryReadSourceControlInformationForProject(taskArgs.BuildEventContext, taskArgs.Items as IList); + Debug.WriteLine(taskArgs.BuildEventContext?.TaskId); + if (taskArgs.Kind == TaskParameterMessageKind.AddItem) + { + if (taskArgs.ItemType.Equals("SourceRoot", StringComparison.OrdinalIgnoreCase)) + { + TryReadSourceControlInformationForProject(taskArgs.BuildEventContext, taskArgs.Items as IList); + } + } } if (message is not null && e.Importance == MessageImportance.High) { From 04a3173a0d25fdee918afd1582f5c91d467940e6 Mon Sep 17 00:00:00 2001 From: Chet Husk Date: Thu, 29 Feb 2024 11:57:08 -0600 Subject: [PATCH 3/6] correct display --- src/MSBuild/TerminalLogger/TerminalLogger.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/MSBuild/TerminalLogger/TerminalLogger.cs b/src/MSBuild/TerminalLogger/TerminalLogger.cs index 883cdc929ad..912c7364084 100644 --- a/src/MSBuild/TerminalLogger/TerminalLogger.cs +++ b/src/MSBuild/TerminalLogger/TerminalLogger.cs @@ -565,10 +565,11 @@ private void ProjectFinished(object sender, ProjectFinishedEventArgs e) if (outputPathSpan.StartsWith(sourceRootSpan, FileUtilities.PathComparison)) { if (outputPathSpan.Length > sourceRootSpan.Length - && (outputPathSpan[sourceRootSpan.Length] == Path.DirectorySeparatorChar - || outputPathSpan[sourceRootSpan.Length] == Path.AltDirectorySeparatorChar)) + // offsets are -1 here compared to above for reasons + && (outputPathSpan[sourceRootSpan.Length - 1 ] == Path.DirectorySeparatorChar + || outputPathSpan[sourceRootSpan.Length - 1] == Path.AltDirectorySeparatorChar)) { - relativeDisplayPathSpan = outputPathSpan.Slice(sourceRootSpan.Length + 1); + relativeDisplayPathSpan = outputPathSpan.Slice(sourceRootSpan.Length); } } } @@ -661,9 +662,17 @@ private void TryReadSourceControlInformationForProject(BuildEventContext? contex var projectContext = new ProjectContext(context); if (_projects.TryGetValue(projectContext, out Project? project)) { + if (project.SourceRoot is not null) + { + return; + } var sourceControlSourceRoot = sourceRoots.FirstOrDefault(root => !string.IsNullOrEmpty(root.GetMetadata("SourceControl"))); if (sourceControlSourceRoot is not null) { + // This takes the first root from source control the first time it's added to the build. + // This seems to be the Target InitializeSourceControlInformationFromSourceControlManager. + // So far this has been acceptable, but if a SourceRoot would be modified by a task later on + // (e.g. TranslateGitHubUrlsInSourceControlInformation) we would lose that modification. project.SourceRoot = sourceControlSourceRoot.ItemSpec.AsMemory(); } } From 89777bfb6c81df071156afce17aa79cf58db235f Mon Sep 17 00:00:00 2001 From: Chet Husk Date: Thu, 29 Feb 2024 12:28:09 -0600 Subject: [PATCH 4/6] compute relative paths from working directory to source root --- src/MSBuild/TerminalLogger/TerminalLogger.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/MSBuild/TerminalLogger/TerminalLogger.cs b/src/MSBuild/TerminalLogger/TerminalLogger.cs index 912c7364084..ad8a6cc48e6 100644 --- a/src/MSBuild/TerminalLogger/TerminalLogger.cs +++ b/src/MSBuild/TerminalLogger/TerminalLogger.cs @@ -562,14 +562,15 @@ private void ProjectFinished(object sender, ProjectFinishedEventArgs e) else if (project.SourceRoot is not null) { var sourceRootSpan = project.SourceRoot.Value.Span; + var relativePathFromWorkingDirToSourceRoot = Path.GetRelativePath(workingDirectorySpan.ToString(), sourceRootSpan.ToString()).AsSpan(); if (outputPathSpan.StartsWith(sourceRootSpan, FileUtilities.PathComparison)) { if (outputPathSpan.Length > sourceRootSpan.Length // offsets are -1 here compared to above for reasons - && (outputPathSpan[sourceRootSpan.Length - 1 ] == Path.DirectorySeparatorChar + && (outputPathSpan[sourceRootSpan.Length - 1] == Path.DirectorySeparatorChar || outputPathSpan[sourceRootSpan.Length - 1] == Path.AltDirectorySeparatorChar)) { - relativeDisplayPathSpan = outputPathSpan.Slice(sourceRootSpan.Length); + relativeDisplayPathSpan = Path.Combine(relativePathFromWorkingDirToSourceRoot.ToString(), outputPathSpan.Slice(sourceRootSpan.Length).ToString()).AsSpan(); } } } From 377264d3a73229dd525e72d2f15ecc7be206d666 Mon Sep 17 00:00:00 2001 From: Chet Husk Date: Thu, 29 Feb 2024 12:36:16 -0600 Subject: [PATCH 5/6] reduce diff --- src/MSBuild/TerminalLogger/TerminalLogger.cs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/MSBuild/TerminalLogger/TerminalLogger.cs b/src/MSBuild/TerminalLogger/TerminalLogger.cs index ad8a6cc48e6..01e3c0b69b2 100644 --- a/src/MSBuild/TerminalLogger/TerminalLogger.cs +++ b/src/MSBuild/TerminalLogger/TerminalLogger.cs @@ -245,8 +245,6 @@ public void Initialize(IEventSource eventSource) eventSource.TargetFinished += TargetFinished; eventSource.TaskStarted += TaskStarted; - eventSource.StatusEventRaised += StatusMessageRaised; - eventSource.MessageRaised += MessageRaised; eventSource.WarningRaised += WarningRaised; eventSource.ErrorRaised += ErrorRaised; @@ -262,21 +260,6 @@ public void Initialize(IEventSource eventSource) } } - private void StatusMessageRaised(object sender, BuildStatusEventArgs e) - { - // if (e is TaskParameterEventArgs taskArgs) - // { - // Debug.WriteLine(taskArgs.BuildEventContext.TaskId) - // if (taskArgs.Kind == TaskParameterMessageKind.AddItem) - // { - // if (taskArgs.ItemType.Equals("SourceRoot", StringComparison.OrdinalIgnoreCase)) - // { - // TryReadSourceControlInformationForProject(taskArgs.BuildEventContext, taskArgs.Items as IList); - // } - // } - // } - } - /// public void Shutdown() { From 8933ba53ba2986079bba12fc8f476472c5567195 Mon Sep 17 00:00:00 2001 From: Chet Husk Date: Thu, 29 Feb 2024 21:35:22 -0600 Subject: [PATCH 6/6] make a forwarding node logger for TL and wire it up --- src/MSBuild/TerminalLogger/TerminalLogger.cs | 78 ++++++++++++++++++++ src/MSBuild/XMake.cs | 9 ++- 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/src/MSBuild/TerminalLogger/TerminalLogger.cs b/src/MSBuild/TerminalLogger/TerminalLogger.cs index 01e3c0b69b2..3ab0db22d57 100644 --- a/src/MSBuild/TerminalLogger/TerminalLogger.cs +++ b/src/MSBuild/TerminalLogger/TerminalLogger.cs @@ -1004,3 +1004,81 @@ private int NodeIndexForContext(BuildEventContext context) #endregion } + +internal sealed class TerminalLoggerNodeForwardingLogger : IForwardingLogger +{ + public IEventRedirector? BuildEventRedirector { get; set; } + public int NodeId { get; set; } + public LoggerVerbosity Verbosity { get => LoggerVerbosity.Diagnostic; set { return; } } + public string? Parameters { get; set; } + + public void Initialize(IEventSource eventSource, int nodeCount) => Initialize(eventSource); + public void Initialize(IEventSource eventSource) + { + eventSource.BuildStarted += ForwardEventUnconditionally; + eventSource.BuildFinished += ForwardEventUnconditionally; + eventSource.ProjectStarted += ForwardEventUnconditionally; + eventSource.ProjectFinished += ForwardEventUnconditionally; + eventSource.TargetStarted += ForwardEventUnconditionally; + eventSource.TargetFinished += ForwardEventUnconditionally; + eventSource.TaskStarted += TaskStarted; + + eventSource.MessageRaised += MessageRaised; + eventSource.WarningRaised += ForwardEventUnconditionally; + eventSource.ErrorRaised += ForwardEventUnconditionally; + + if (eventSource is IEventSource3 eventSource3) + { + eventSource3.IncludeTaskInputs(); + } + + if (eventSource is IEventSource4 eventSource4) + { + eventSource4.IncludeEvaluationPropertiesAndItems(); + } + } + + public void ForwardEventUnconditionally(object sender, BuildEventArgs e) + { + BuildEventRedirector?.ForwardEvent(e); + } + + public void TaskStarted(object sender, TaskStartedEventArgs e) + { + // MSBuild tasks yield the build node, so forward this to the central node so it can update status + if (e.TaskName.Equals("MSBuild", StringComparison.OrdinalIgnoreCase)) + { + BuildEventRedirector?.ForwardEvent(e); + } + } + + public void MessageRaised(object sender, BuildMessageEventArgs e) + { + if (e.BuildEventContext is null) + { + return; + } + + // SourceRoot additions are used in output reporting, so forward those along + if (e is TaskParameterEventArgs taskArgs) + { + if (taskArgs.Kind == TaskParameterMessageKind.AddItem) + { + if (taskArgs.ItemType.Equals("SourceRoot", StringComparison.OrdinalIgnoreCase)) + { + BuildEventRedirector?.ForwardEvent(taskArgs); + } + } + } + + // High-priority messages are rendered for each project, so forward those along + if (e.Message is not null && e.Importance == MessageImportance.High) + { + BuildEventRedirector?.ForwardEvent(e); + } + } + + public void Shutdown() + { + } +} diff --git a/src/MSBuild/XMake.cs b/src/MSBuild/XMake.cs index 23c3d593e15..0a08f58f87f 100644 --- a/src/MSBuild/XMake.cs +++ b/src/MSBuild/XMake.cs @@ -43,6 +43,7 @@ using LoggerDescription = Microsoft.Build.Logging.LoggerDescription; using SimpleErrorLogger = Microsoft.Build.Logging.SimpleErrorLogger.SimpleErrorLogger; using TerminalLogger = Microsoft.Build.Logging.TerminalLogger.TerminalLogger; +using TerminalLoggerForwardingLogger = Microsoft.Build.Logging.TerminalLogger.TerminalLoggerNodeForwardingLogger; #nullable disable @@ -2951,13 +2952,13 @@ private static string GetProjectDirectory(string[] projectSwitchParameters) /// - /// Identifies if there is rsp files near the project file + /// Identifies if there is rsp files near the project file /// /// true if there autoresponse file was found private static bool CheckAndGatherProjectAutoResponseFile(CommandLineSwitches switchesFromAutoResponseFile, CommandLineSwitches commandLineSwitches, bool recursing, string commandLine) { bool found = false; - + var projectDirectory = GetProjectDirectory(commandLineSwitches[CommandLineSwitches.ParameterizedSwitch.Project]); if (!recursing && !commandLineSwitches[CommandLineSwitches.ParameterlessSwitch.NoAutoResponse]) @@ -3936,8 +3937,8 @@ private static void ProcessTerminalLogger(bool noConsoleLogger, } else { - // For performance, register this logger using the forwarding logger mechanism. - DistributedLoggerRecord forwardingLoggerRecord = CreateForwardingLoggerRecord(logger, string.Join(";", TerminalLogger.ConfigurableForwardingLoggerParameters), LoggerVerbosity.Quiet); + LoggerDescription terminalLoggerDescription = new LoggerDescription(typeof(TerminalLoggerForwardingLogger).FullName, typeof(TerminalLoggerForwardingLogger).Assembly.FullName, null, null, LoggerVerbosity.Diagnostic); + DistributedLoggerRecord forwardingLoggerRecord = new DistributedLoggerRecord(logger, terminalLoggerDescription); distributedLoggerRecords.Add(forwardingLoggerRecord); } }