Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft of a forwarding logger for terminal logger #9808

Draft
wants to merge 6 commits into
base: use-sourceroot-for-relative-paths
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/MSBuild/TerminalLogger/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ public Project(string? targetFramework, StopwatchAbstraction? stopwatch)
/// </summary>
public ReadOnlyMemory<char>? OutputPath { get; set; }

/// <summary>
/// Full path to the 'root' of this project's source control repository, if known.
/// </summary>
public ReadOnlyMemory<char>? SourceRoot { get; set; }

/// <summary>
/// The target framework of the project or null if not multi-targeting.
/// </summary>
Expand Down
156 changes: 149 additions & 7 deletions src/MSBuild/TerminalLogger/TerminalLogger.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -9,6 +9,10 @@
using Microsoft.Build.Shared;
using System.Text.RegularExpressions;
using System.Diagnostics;
using Microsoft.Build.Execution;
using Microsoft.Build.Utilities;



#if NET7_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
Expand Down Expand Up @@ -245,6 +249,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();
Expand Down Expand Up @@ -520,19 +529,37 @@ 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;
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.AltDirectorySeparatorChar))
{
relativeDisplayPathSpan = Path.Combine(relativePathFromWorkingDirToSourceRoot.ToString(), outputPathSpan.Slice(sourceRootSpan.Length).ToString()).AsSpan();
}
}
}

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
{
Expand Down Expand Up @@ -609,6 +636,32 @@ private void TargetFinished(object sender, TargetFinishedEventArgs e)
{
}

private void TryReadSourceControlInformationForProject(BuildEventContext? context, IEnumerable<ITaskItem>? sourceRoots)
{
if (context is null || sourceRoots is null)
{
return;
}

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();
}
}
}

/// <summary>
/// The <see cref="IEventSource.TaskStarted"/> callback.
/// </summary>
Expand Down Expand Up @@ -639,6 +692,17 @@ private void MessageRaised(object sender, BuildMessageEventArgs e)
}

string? message = e.Message;
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<ProjectItemInstance>);
}
}
}
if (message is not null && e.Importance == MessageImportance.High)
{
var hasProject = _projects.TryGetValue(new ProjectContext(buildEventContext), out Project? project);
Expand Down Expand Up @@ -940,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()
{
}
}
9 changes: 5 additions & 4 deletions src/MSBuild/XMake.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -2951,13 +2952,13 @@ private static string GetProjectDirectory(string[] projectSwitchParameters)


/// <summary>
/// Identifies if there is rsp files near the project file
/// Identifies if there is rsp files near the project file
/// </summary>
/// <returns>true if there autoresponse file was found</returns>
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])
Expand Down Expand Up @@ -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);
}
}
Expand Down