Skip to content

Commit

Permalink
Console logger support for IncludeEvaluationPropertiesAndItems
Browse files Browse the repository at this point in the history
Call IEventSource4.IncludeEvaluationPropertiesAndItems()

OutputItems was pretty much duplicated in ParallelConsoleLogger. Unify with the base implementation and extract methods that need to be replaced by the derived type.

Support for reading project configuration description either from ProjectStartedEventArgs.Items or ProjectEvaluationFinishedEventArgs.Items.
  • Loading branch information
KirillOsenkov committed Jun 15, 2021
1 parent 264a797 commit c813836
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 77 deletions.
78 changes: 50 additions & 28 deletions src/Build/Logging/BaseConsoleLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;

using Microsoft.Build.Evaluation;
using Microsoft.Build.Framework;
using Microsoft.Build.Internal;
using Microsoft.Build.Shared;
using System.Collections;
using System.Globalization;
using System.IO;

using ColorSetter = Microsoft.Build.Logging.ColorSetter;
using ColorResetter = Microsoft.Build.Logging.ColorResetter;
using WriteHandler = Microsoft.Build.Logging.WriteHandler;
using Microsoft.Build.Exceptions;

namespace Microsoft.Build.BackEnd.Logging
{
Expand Down Expand Up @@ -642,42 +644,56 @@ internal SortedList ExtractItemList(IEnumerable items)
/// </summary>
internal virtual void OutputItems(string itemType, ArrayList itemTypeList)
{
// Write each item, one per line
bool haveWrittenItemType = false;
setColor(ConsoleColor.DarkGray);
foreach (ITaskItem item in itemTypeList)
WriteItemType(itemType);

foreach (var item in itemTypeList)
{
if (!haveWrittenItemType)
string itemSpec = item switch
{
setColor(ConsoleColor.Gray);
WriteLinePretty(itemType);
haveWrittenItemType = true;
setColor(ConsoleColor.DarkGray);
}
WriteLinePretty(" " /* indent slightly*/ + item.ItemSpec);
ITaskItem taskItem => taskItem.ItemSpec,
IItem iitem => iitem.EvaluatedInclude,
{ } misc => Convert.ToString(misc),
null => "null"
};

IDictionary metadata = item.CloneCustomMetadata();
WriteItemSpec(itemSpec);

foreach (DictionaryEntry metadatum in metadata)
var metadata = item switch
{
string valueOrError;
try
{
valueOrError = item.GetMetadata(metadatum.Key as string);
}
catch (InvalidProjectFileException e)
IMetadataContainer metadataContainer => metadataContainer.EnumerateMetadata(),
IItem<ProjectMetadata> iitem => iitem.Metadata?.Select(m => new KeyValuePair<string, string>(m.Name, m.EvaluatedValue)),
_ => null
};

if (metadata != null)
{
foreach (var metadatum in metadata)
{
valueOrError = e.Message;
WriteMetadata(metadatum.Key, metadatum.Value);
}

// A metadatum's "value" is its escaped value, since that's how we represent them internally.
// So unescape before returning to the world at large.
WriteLinePretty(" " + metadatum.Key + " = " + valueOrError);
}
}

resetColor();
}

protected virtual void WriteItemType(string itemType)
{
setColor(ConsoleColor.Gray);
WriteLinePretty(itemType);
setColor(ConsoleColor.DarkGray);
}

protected virtual void WriteItemSpec(string itemSpec)
{
WriteLinePretty(" " + itemSpec);
}

protected virtual void WriteMetadata(string name, string value)
{
WriteLinePretty(" " + name + " = " + value);
}

/// <summary>
/// Returns a performance counter for a given scope (either task name or target name)
/// from the given table.
Expand Down Expand Up @@ -959,6 +975,12 @@ public virtual void Initialize(IEventSource eventSource)
eventSource.MessageRaised += MessageHandler;
eventSource.CustomEventRaised += CustomEventHandler;
eventSource.StatusEventRaised += StatusEventHandler;

bool logPropertiesAndItemsAfterEvaluation = Utilities.Traits.Instance.EscapeHatches.LogPropertiesAndItemsAfterEvaluation ?? true;
if (logPropertiesAndItemsAfterEvaluation && eventSource is IEventSource4 eventSource4)
{
eventSource4.IncludeEvaluationPropertiesAndItems();
}
}
}

Expand Down
113 changes: 64 additions & 49 deletions src/Build/Logging/ParallelLogger/ParallelConsoleLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -540,20 +540,38 @@ public override void ProjectStartedHandler(object sender, ProjectStartedEventArg
}
}

ReadProjectConfigurationDescription(e.BuildEventContext, e.Items);
var projectKey = (e.BuildEventContext.NodeId, e.BuildEventContext.ProjectContextId);

// If the value is available at all, it will be either in the items
// from ProjectStarted (old behavior), or the items from ProjectEvaluationFinished (new behavior).
// First try the old behavior, and fallback to the new behavior.
var result = ReadProjectConfigurationDescription(e.Items);
if (result != null)
{
// Found the items directly on ProjectStarted
propertyOutputMap[projectKey] = result;
}
else
{
// Try to see if we saw the items on the corresponding ProjectEvaluationFinished
var evaluationKey = GetEvaluationKey(e.BuildEventContext);

// if the value was set from ProjectEvaluationFinished, copy it into the entry
// for this project
if (propertyOutputMap.TryGetValue(evaluationKey, out string value))
{
propertyOutputMap[projectKey] = value;
}
}
}

private void ReadProjectConfigurationDescription(BuildEventContext buildEventContext, IEnumerable items)
private string ReadProjectConfigurationDescription(IEnumerable items)
{
if (buildEventContext == null || items == null)
if (items == null)
{
return;
return null;
}

// node and project context ids for the propertyOutputMap key.
int nodeID = buildEventContext.NodeId;
int projectContextId = buildEventContext.ProjectContextId;

ReuseableStringBuilder projectConfigurationDescription = null;

Internal.Utilities.EnumerateItems(items, item =>
Expand All @@ -578,14 +596,27 @@ private void ReadProjectConfigurationDescription(BuildEventContext buildEventCon
}
});

// Add the finished dictionary to propertyOutputMap.
if (projectConfigurationDescription != null)
{
propertyOutputMap.Add((nodeID, projectContextId), projectConfigurationDescription.ToString());
var result = projectConfigurationDescription.ToString();
(projectConfigurationDescription as IDisposable)?.Dispose();
return result;
}

return null;
}

/// <summary>
/// In case the items are stored on ProjectEvaluationFinishedEventArgs
/// (new behavior), we first store the value per evaluation, and then
/// in ProjectStarted, find the value from the project's evaluation
/// and use that.
/// </summary>
private (int, int) GetEvaluationKey(BuildEventContext buildEventContext)
// note that we use a negative number for evaluations so that we don't conflict
// with project context ids.
=> (buildEventContext.NodeId, -buildEventContext.EvaluationId);

/// <summary>
/// Handler for project finished events
/// </summary>
Expand Down Expand Up @@ -751,44 +782,23 @@ internal void WriteItems(BuildEventArgs e, IEnumerable items)
ShownBuildEventContext(e.BuildEventContext);
}

internal override void OutputItems(string itemType, ArrayList itemTypeList)
protected override void WriteItemType(string itemType)
{
// Write each item, one per line
bool haveWrittenItemType = false;
foreach (ITaskItem item in itemTypeList)
{
if (!haveWrittenItemType)
{
setColor(ConsoleColor.DarkGray);
WriteMessageAligned(itemType, false);
haveWrittenItemType = true;
}
setColor(ConsoleColor.Gray);

// Indent the text by two tab lengths
StringBuilder result = new StringBuilder((2 * tabWidth) + item.ItemSpec.Length);
result.Append(' ', 2 * tabWidth).Append(item.ItemSpec);
WriteMessageAligned(result.ToString(), false);

IDictionary metadata = item.CloneCustomMetadata();
setColor(ConsoleColor.DarkGray);
WriteMessageAligned(itemType, prefixAlreadyWritten: false);
setColor(ConsoleColor.Gray);
}

foreach (DictionaryEntry metadatum in metadata)
{
string valueOrError;
try
{
valueOrError = item.GetMetadata(metadatum.Key as string);
}
catch (InvalidProjectFileException e)
{
valueOrError = e.Message;
}
protected override void WriteItemSpec(string itemSpec)
{
WriteMessageAligned(new string(' ', 2 * tabWidth) + itemSpec, prefixAlreadyWritten: false);
}

WriteMessageAligned($"{new string(' ', 4 * tabWidth)}{metadatum.Key} = {valueOrError}", false);
}
}
resetColor();
protected override void WriteMetadata(string name, string value)
{
WriteMessageAligned($"{new string(' ', 4 * tabWidth)}{name} = {value}", prefixAlreadyWritten: false);
}

/// <summary>
/// Handler for target started events
/// </summary>
Expand Down Expand Up @@ -962,16 +972,16 @@ public override void TaskFinishedHandler(object sender, TaskFinishedEventArgs e)
/// <summary>
/// Finds the LogOutProperty string to be printed in messages.
/// </summary>
/// <param name="e"> Build event to extract context information from.</param>
/// <param name="e">Build event to extract context information from.</param>
internal string FindLogOutputProperties(BuildEventArgs e)
{
string projectConfigurationDescription = String.Empty;
if (e.BuildEventContext != null)
{
int nodeId = e.BuildEventContext.NodeId;
int projectContextId = e.BuildEventContext.ProjectContextId;
propertyOutputMap.TryGetValue((nodeId, projectContextId), out projectConfigurationDescription);
var key = (e.BuildEventContext.NodeId, e.BuildEventContext.ProjectContextId);
propertyOutputMap.TryGetValue(key, out projectConfigurationDescription);
}

return projectConfigurationDescription;
}

Expand Down Expand Up @@ -1168,7 +1178,12 @@ public override void StatusEventHandler(object sender, BuildStatusEventArgs e)
}
}

ReadProjectConfigurationDescription(projectEvaluationFinished.BuildEventContext, projectEvaluationFinished.Items);
var value = ReadProjectConfigurationDescription(projectEvaluationFinished.Items);
if (value != null)
{
var evaluationKey = GetEvaluationKey(e.BuildEventContext);
propertyOutputMap[evaluationKey] = value;
}
}
}

Expand Down

0 comments on commit c813836

Please sign in to comment.