Skip to content

Commit

Permalink
Revert "Revert Emit eval props if requested by any sink (#10243)" (#1…
Browse files Browse the repository at this point in the history
…0508)

* Revert "Revert Emit eval props if requested by any sink (#10243) (#10447)"

This reverts commit bd46115.

* Prevent NullLogger to request eval props

* Remove extra newline
  • Loading branch information
JanKrivanek authored Aug 15, 2024
1 parent 3b1da00 commit befaec7
Show file tree
Hide file tree
Showing 15 changed files with 183 additions and 107 deletions.
1 change: 1 addition & 0 deletions documentation/wiki/ChangeWaves.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ A wave of features is set to "rotate out" (i.e. become standard functionality) t
- [Convert.ToString during a property evaluation uses the InvariantCulture for all types](https://github.com/dotnet/msbuild/pull/9874)
- [Fix oversharing of build results in ResultsCache](https://github.com/dotnet/msbuild/pull/9987)
- [Add ParameterName and PropertyName to TaskParameterEventArgs](https://github.com/dotnet/msbuild/pull/10130)
- [Emit eval props if requested by any sink](https://github.com/dotnet/msbuild/pull/10243)

### 17.10
- [AppDomain configuration is serialized without using BinFmt](https://github.com/dotnet/msbuild/pull/9320) - feature can be opted out only if [BinaryFormatter](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.formatters.binary.binaryformatter) is allowed at runtime by editing `MSBuild.runtimeconfig.json`
Expand Down
59 changes: 40 additions & 19 deletions src/Build.UnitTests/BackEnd/BuildManager_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public BuildManager_Tests(ITestOutputHelper output)
EnableNodeReuse = false
};
_buildManager = new BuildManager();
_projectCollection = new ProjectCollection();
_projectCollection = new ProjectCollection(globalProperties: null, _parameters.Loggers, ToolsetDefinitionLocations.Default);

_env = TestEnvironment.Create(output);
_inProcEnvCheckTransientEnvironmentVariable = _env.SetEnvironmentVariable("MSBUILDINPROCENVCHECK", "1");
Expand Down Expand Up @@ -137,8 +137,8 @@ public void SimpleBuild()
_logger.AssertLogContains("[success]");
Assert.Single(_logger.ProjectStartedEvents);

ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[0];
Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties);
ProjectEvaluationFinishedEventArgs evalFinishedEvent = _logger.EvaluationFinishedEvents[0];
Dictionary<string, string> properties = ExtractProjectStartedPropertyList(evalFinishedEvent.Properties);

Assert.True(properties.TryGetValue("InitialProperty1", out string propertyValue));
Assert.Equal("InitialProperty1", propertyValue);
Expand Down Expand Up @@ -254,8 +254,8 @@ public void SimpleGraphBuild()
_logger.AssertLogContains("[success]");
_logger.ProjectStartedEvents.Count.ShouldBe(1);

ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[0];
Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties);
ProjectEvaluationFinishedEventArgs evalFinishedEvent = _logger.EvaluationFinishedEvents[0];
Dictionary<string, string> properties = ExtractProjectStartedPropertyList(evalFinishedEvent.Properties);

properties.TryGetValue("InitialProperty1", out string propertyValue).ShouldBeTrue();
propertyValue.ShouldBe("InitialProperty1", StringCompareShould.IgnoreCase);
Expand Down Expand Up @@ -571,8 +571,8 @@ public void InProcForwardPropertiesFromChild()
_logger.AssertLogContains("[success]");
Assert.Single(_logger.ProjectStartedEvents);

ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[0];
Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties);
ProjectEvaluationFinishedEventArgs evalFinishedEvent = _logger.EvaluationFinishedEvents[0];
Dictionary<string, string> properties = ExtractProjectStartedPropertyList(evalFinishedEvent.Properties);

Assert.True(properties.TryGetValue("InitialProperty1", out string propertyValue));
Assert.Equal("InitialProperty1", propertyValue);
Expand Down Expand Up @@ -611,8 +611,8 @@ public void InProcMsBuildForwardAllPropertiesFromChild()
_logger.AssertLogContains("[success]");
Assert.Single(_logger.ProjectStartedEvents);

ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[0];
Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties);
ProjectEvaluationFinishedEventArgs evalFinishedEvent = _logger.EvaluationFinishedEvents[0];
Dictionary<string, string> properties = ExtractProjectStartedPropertyList(evalFinishedEvent.Properties);

Assert.True(properties.TryGetValue("InitialProperty1", out string propertyValue));
Assert.Equal("InitialProperty1", propertyValue);
Expand Down Expand Up @@ -655,8 +655,8 @@ public void MsBuildForwardAllPropertiesFromChildLaunchChildNode()
_logger.AssertLogContains("[success]");
Assert.Single(_logger.ProjectStartedEvents);

ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[0];
Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties);
ProjectEvaluationFinishedEventArgs evalFinishedEvent = _logger.EvaluationFinishedEvents[0];
Dictionary<string, string> properties = ExtractProjectStartedPropertyList(evalFinishedEvent.Properties);

Assert.True(properties.TryGetValue("InitialProperty1", out string propertyValue));
Assert.Equal("InitialProperty1", propertyValue);
Expand Down Expand Up @@ -704,7 +704,15 @@ public void OutOfProcNodeForwardCertainproperties()
var data = new BuildRequestData(project.FullPath, new Dictionary<string, string>(),
MSBuildDefaultToolsVersion, Array.Empty<string>(), null);

BuildResult result = _buildManager.Build(_parameters, data);
// We need to recreate build parameters to ensure proper capturing of newly set environment variables
BuildParameters parameters = new BuildParameters
{
ShutdownInProcNodeOnBuildFinish = true,
Loggers = new ILogger[] { _logger },
EnableNodeReuse = false
};

BuildResult result = _buildManager.Build(parameters, data);
Assert.Equal(BuildResultCode.Success, result.OverallResult);
_logger.AssertLogContains("[success]");
Assert.Single(_logger.ProjectStartedEvents);
Expand Down Expand Up @@ -760,11 +768,21 @@ public void OutOfProcNodeForwardCertainpropertiesAlsoGetResultsFromCache()
_env.SetEnvironmentVariable("MsBuildForwardPropertiesFromChild", "InitialProperty3;IAMNOTREAL");
_env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", "1");

_env.SetEnvironmentVariable("MSBUILDLOGPROPERTIESANDITEMSAFTEREVALUATION", "0");

var project = CreateProject(contents, null, _projectCollection, false);
var data = new BuildRequestData(project.FullPath, new Dictionary<string, string>(),
MSBuildDefaultToolsVersion, Array.Empty<string>(), null);

BuildResult result = _buildManager.Build(_parameters, data);
// We need to recreate build parameters to ensure proper capturing of newly set environment variables
BuildParameters parameters = new BuildParameters
{
ShutdownInProcNodeOnBuildFinish = true,
Loggers = new ILogger[] { _logger },
EnableNodeReuse = false
};

BuildResult result = _buildManager.Build(parameters, data);
Assert.Equal(BuildResultCode.Success, result.OverallResult);
_logger.AssertLogContains("[success]");
Assert.Equal(3, _logger.ProjectStartedEvents.Count);
Expand All @@ -785,7 +803,8 @@ public void OutOfProcNodeForwardCertainpropertiesAlsoGetResultsFromCache()
Assert.Equal("InitialProperty3", propertyValue);

projectStartedEvent = _logger.ProjectStartedEvents[2];
Assert.Null(projectStartedEvent.Properties);
properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties);
(properties == null || properties.Count == 0).ShouldBeTrue();
}

/// <summary>
Expand Down Expand Up @@ -822,7 +841,7 @@ public void ForwardNoPropertiesLaunchChildNode()

ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[0];
Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties);
Assert.Null(properties);
(properties == null || properties.Count == 0).ShouldBeTrue();
}

/// <summary>
Expand Down Expand Up @@ -919,7 +938,7 @@ public void ForwardNoPropertiesLaunchChildNodeDefault()

ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[0];
Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties);
Assert.Null(properties);
(properties == null || properties.Count == 0).ShouldBeTrue();
}

/// <summary>
Expand Down Expand Up @@ -3475,9 +3494,11 @@ private static string BuildAndCheckCache(BuildManager localBuildManager, IEnumer
/// </summary>
private static Dictionary<string, string> ExtractProjectStartedPropertyList(IEnumerable properties)
{
// Gather a sorted list of all the properties.
return properties?.Cast<DictionaryEntry>()
.ToDictionary(prop => (string)prop.Key, prop => (string)prop.Value, StringComparer.OrdinalIgnoreCase);
Dictionary<string, string> propertiesLookup = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
Internal.Utilities.EnumerateProperties(properties, propertiesLookup,
static (dict, kvp) => dict.Add(kvp.Key, kvp.Value));

return propertiesLookup;
}

/// <summary>
Expand Down
19 changes: 13 additions & 6 deletions src/Build.UnitTests/BackEnd/MockLoggingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,14 +222,21 @@ public bool IncludeEvaluationProfile
set { }
}

/// <summary>
/// Log properties and items on ProjectEvaluationFinishedEventArgs
/// instead of ProjectStartedEventArgs.
/// </summary>
public bool IncludeEvaluationPropertiesAndItems
/// <inheritdoc cref="ILoggingService.SetIncludeEvaluationPropertiesAndItemsInEvents"/>
public void SetIncludeEvaluationPropertiesAndItemsInEvents(bool inProjectStartedEvent,
bool inEvaluationFinishedEvent)
{ }

/// <inheritdoc cref="ILoggingService.IncludeEvaluationPropertiesAndItemsInProjectStartedEvent"/>
public bool IncludeEvaluationPropertiesAndItemsInProjectStartedEvent
{
get => false;
}

/// <inheritdoc cref="ILoggingService.IncludeEvaluationPropertiesAndItemsInEvaluationFinishedEvent"/>
public bool IncludeEvaluationPropertiesAndItemsInEvaluationFinishedEvent
{
get => false;
set { }
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Build.UnitTests/ConsoleLogger_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ public void ErrorMessageWithMultiplePropertiesInMessage(bool includeEvaluationPr

if (includeEvaluationPropertiesAndItems)
{
pc.Collection.LoggingService.IncludeEvaluationPropertiesAndItems = true;
pc.Collection.LoggingService.SetIncludeEvaluationPropertiesAndItemsInEvents(inProjectStartedEvent: false, inEvaluationFinishedEvent: true);
}

var project = env.CreateTestProjectWithFiles(@"
Expand Down
25 changes: 10 additions & 15 deletions src/Build/BackEnd/BuildManager/BuildManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2771,7 +2771,8 @@ private NodeConfiguration GetNodeConfiguration()
, new LoggingNodeConfiguration(
loggingService.IncludeEvaluationMetaprojects,
loggingService.IncludeEvaluationProfile,
loggingService.IncludeEvaluationPropertiesAndItems,
loggingService.IncludeEvaluationPropertiesAndItemsInProjectStartedEvent,
loggingService.IncludeEvaluationPropertiesAndItemsInEvaluationFinishedEvent,
loggingService.IncludeTaskInputs));
}

Expand Down Expand Up @@ -3279,25 +3280,19 @@ public string? Parameters
/// </summary>
public void Initialize(IEventSource eventSource)
{
// The concrete type we get should always be our internal
// implementation and up-to-date, but we need to meet the
// external contract so can't specify that for the
// argument.

IEventSource4 eventSource4 = (IEventSource4)eventSource;

// Most checks in LoggingService are "does any attached logger
// specifically opt into this new behavior?". As such, the
// NullLogger shouldn't opt into them explicitly and should
// let other loggers opt in.

// IncludeEvaluationPropertiesAndItems is different though,
// because its check is "do ALL attached loggers opt into
// the new behavior?", since the new behavior removes
// information from old loggers. So the NullLogger must
// opt in to ensure it doesn't accidentally veto the new
// behavior.
eventSource4.IncludeEvaluationPropertiesAndItems();
// IncludeEvaluationPropertiesAndItems was different,
// because it checked "do ALL attached loggers opt into
// the new behavior?".
// It was fixed and hence we need to be careful not to opt in
// the behavior as it was done before - but let the other loggers choose.
//
// For this reason NullLogger MUST NOT call
// ((IEventSource4)eventSource).IncludeEvaluationPropertiesAndItems();
}

/// <summary>
Expand Down
18 changes: 15 additions & 3 deletions src/Build/BackEnd/Components/Logging/ILoggingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,24 @@ bool IncludeEvaluationProfile

/// <summary>
/// Should properties and items be logged on <see cref="ProjectEvaluationFinishedEventArgs"/>
/// instead of <see cref="ProjectStartedEventArgs"/>?
/// or/and <see cref="ProjectStartedEventArgs"/>?
/// </summary>
bool IncludeEvaluationPropertiesAndItems
void SetIncludeEvaluationPropertiesAndItemsInEvents(bool inProjectStartedEvent, bool inEvaluationFinishedEvent);

/// <summary>
/// Indicates whether properties and items should be logged on <see cref="ProjectStartedEventArgs"/>.
/// </summary>
bool IncludeEvaluationPropertiesAndItemsInProjectStartedEvent
{
get;
}

/// <summary>
/// Indicates whether properties and items should be logged on <see cref="ProjectEvaluationFinishedEventArgs"/>.
/// </summary>
bool IncludeEvaluationPropertiesAndItemsInEvaluationFinishedEvent
{
get;
set;
}

/// <summary>
Expand Down
90 changes: 64 additions & 26 deletions src/Build/BackEnd/Components/Logging/LoggingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -201,12 +201,6 @@ internal partial class LoggingService : ILoggingService, INodePacketHandler
/// </summary>
private bool? _includeEvaluationProfile;

/// <summary>
/// Whether properties and items should be logged on <see cref="ProjectEvaluationFinishedEventArgs"/>
/// instead of <see cref="ProjectStartedEventArgs"/>.
/// </summary>
private bool? _includeEvaluationPropertiesAndItems;

/// <summary>
/// Whether to include task inputs in task events.
/// </summary>
Expand Down Expand Up @@ -546,33 +540,77 @@ public bool IncludeTaskInputs
set => _includeTaskInputs = value;
}

/// <summary>
/// Should properties and items be logged on <see cref="ProjectEvaluationFinishedEventArgs"/>
/// instead of <see cref="ProjectStartedEventArgs"/>?
/// </summary>
public bool IncludeEvaluationPropertiesAndItems
/// <inheritdoc cref="ILoggingService.SetIncludeEvaluationPropertiesAndItemsInEvents"/>
public void SetIncludeEvaluationPropertiesAndItemsInEvents(bool inProjectStartedEvent, bool inEvaluationFinishedEvent)
{
get
_evalDataBehaviorSet = true;
IncludeEvaluationPropertiesAndItemsInEvaluationFinishedEvent = inEvaluationFinishedEvent;
IncludeEvaluationPropertiesAndItemsInProjectStartedEvent = inProjectStartedEvent;
}

private bool _evalDataBehaviorSet;
private bool _includeEvaluationPropertiesAndItemsInProjectStartedEvent;
private bool _includeEvaluationPropertiesAndItemsInEvaluationFinishedEvent;
private void InferEvalDataBehavior()
{
if (_evalDataBehaviorSet)
{
if (_includeEvaluationPropertiesAndItems == null)
return;
}
// Set this right away - to prevent SO exception in case of any future refactoring
// that would refer to the IncludeEvaluation... properties here
_evalDataBehaviorSet = true;

bool? escapeHatch = Traits.Instance.EscapeHatches.LogPropertiesAndItemsAfterEvaluation;
if (escapeHatch.HasValue)
{
IncludeEvaluationPropertiesAndItemsInEvaluationFinishedEvent = escapeHatch.Value;
IncludeEvaluationPropertiesAndItemsInProjectStartedEvent = !escapeHatch.Value;
}
else
{
var sinks = _eventSinkDictionary.Values.OfType<EventSourceSink>().ToList();

if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_12))
{
var escapeHatch = Traits.Instance.EscapeHatches.LogPropertiesAndItemsAfterEvaluation;
if (escapeHatch.HasValue)
{
_includeEvaluationPropertiesAndItems = escapeHatch.Value;
}
else
{
var sinks = _eventSinkDictionary.Values.OfType<EventSourceSink>();
// .All() on an empty list defaults to true, we want to default to false
_includeEvaluationPropertiesAndItems = sinks.Any() && sinks.All(sink => sink.IncludeEvaluationPropertiesAndItems);
}
// If any logger requested the data - we need to emit them
IncludeEvaluationPropertiesAndItemsInEvaluationFinishedEvent =
sinks.Any(sink => sink.IncludeEvaluationPropertiesAndItems);
// If any logger didn't request the data - hence it's likely legacy logger
// - we need to populate the data in legacy way
IncludeEvaluationPropertiesAndItemsInProjectStartedEvent =
sinks.Any(sink => !sink.IncludeEvaluationPropertiesAndItems);
}
else
{
bool allSinksIncludeEvalData = sinks.Any() && sinks.All(sink => sink.IncludeEvaluationPropertiesAndItems);

return _includeEvaluationPropertiesAndItems ?? false;
IncludeEvaluationPropertiesAndItemsInEvaluationFinishedEvent = allSinksIncludeEvalData;
IncludeEvaluationPropertiesAndItemsInProjectStartedEvent = !allSinksIncludeEvalData;
}
}
}

set => _includeEvaluationPropertiesAndItems = value;
/// <inheritdoc cref="ILoggingService.IncludeEvaluationPropertiesAndItemsInProjectStartedEvent"/>
public bool IncludeEvaluationPropertiesAndItemsInProjectStartedEvent
{
get
{
InferEvalDataBehavior();
return _includeEvaluationPropertiesAndItemsInProjectStartedEvent;
}
private set => _includeEvaluationPropertiesAndItemsInProjectStartedEvent = value;
}

/// <inheritdoc cref="ILoggingService.IncludeEvaluationPropertiesAndItemsInEvaluationFinishedEvent"/>
public bool IncludeEvaluationPropertiesAndItemsInEvaluationFinishedEvent
{
get
{
InferEvalDataBehavior();
return _includeEvaluationPropertiesAndItemsInEvaluationFinishedEvent;
}
private set => _includeEvaluationPropertiesAndItemsInEvaluationFinishedEvent = value;
}

/// <summary>
Expand Down
Loading

0 comments on commit befaec7

Please sign in to comment.