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

Log properties and items on ProjectEvaluationFinished #6287

Merged
merged 7 commits into from Apr 2, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion eng/Packages.props
Expand Up @@ -2,7 +2,7 @@
<ItemGroup>
<PackageReference Update="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Update="jnm2.ReferenceAssemblies.net35" Version="1.0.1" />
<PackageReference Update="LargeAddressAware" Version="1.0.3" />
<PackageReference Update="LargeAddressAware" Version="1.0.5" />
<PackageReference Update="Microsoft.Build.NuGetSdkResolver" Version="$(NuGetBuildTasksVersion)" />
<PackageReference Update="Microsoft.CodeAnalysis.Build.Tasks" Version="$(MicrosoftNetCompilersToolsetVersion)" />
<PackageReference Update="Microsoft.DotNet.GenAPI" Version="2.1.0-prerelease-02404-02" />
Expand Down
Expand Up @@ -38,7 +38,7 @@ public abstract partial class BuildEventArgs : System.EventArgs
public Microsoft.Build.Framework.BuildEventContext BuildEventContext { get { throw null; } set { } }
public string HelpKeyword { get { throw null; } }
public virtual string Message { get { throw null; } protected set { } }
protected System.DateTime RawTimestamp { get { throw null; } set { } }
protected internal System.DateTime RawTimestamp { get { throw null; } set { } }
public string SenderName { get { throw null; } }
public int ThreadId { get { throw null; } }
public System.DateTime Timestamp { get { throw null; } }
Expand Down Expand Up @@ -253,6 +253,10 @@ public partial interface IEventSource3 : Microsoft.Build.Framework.IEventSource,
void IncludeEvaluationProfiles();
void IncludeTaskInputs();
}
public partial interface IEventSource4 : Microsoft.Build.Framework.IEventSource, Microsoft.Build.Framework.IEventSource2, Microsoft.Build.Framework.IEventSource3
{
void IncludeEvaluationPropertiesAndItems();
}
public partial interface IForwardingLogger : Microsoft.Build.Framework.ILogger, Microsoft.Build.Framework.INodeLogger
{
Microsoft.Build.Framework.IEventRedirector BuildEventRedirector { get; set; }
Expand Down Expand Up @@ -382,8 +386,11 @@ public sealed partial class ProjectEvaluationFinishedEventArgs : Microsoft.Build
{
public ProjectEvaluationFinishedEventArgs() { }
public ProjectEvaluationFinishedEventArgs(string message, params object[] messageArgs) { }
public System.Collections.IEnumerable GlobalProperties { get { throw null; } set { } }
public System.Collections.IEnumerable Items { get { throw null; } set { } }
public Microsoft.Build.Framework.Profiler.ProfilerResult? ProfilerResult { get { throw null; } set { } }
public string ProjectFile { get { throw null; } set { } }
public System.Collections.IEnumerable Properties { get { throw null; } set { } }
}
public partial class ProjectEvaluationStartedEventArgs : Microsoft.Build.Framework.BuildStatusEventArgs
{
Expand Down
Expand Up @@ -38,7 +38,7 @@ public abstract partial class BuildEventArgs : System.EventArgs
public Microsoft.Build.Framework.BuildEventContext BuildEventContext { get { throw null; } set { } }
public string HelpKeyword { get { throw null; } }
public virtual string Message { get { throw null; } protected set { } }
protected System.DateTime RawTimestamp { get { throw null; } set { } }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the context behind this change?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I needed to be able to read and write the raw timestamp without reflection. protected wasn’t enough because I needed access from LogMessagePacketBase and binlog writer, so I’m adding protected internal. I was worried about exposing the field directly because who knows whether changing that field could break BinaryFormatter and I didn’t call it Timestamp because having identifiers that differ by case only would break CLS compliance (not sure how much we care).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means it can be accessed freely within the assembly or by classes that extend it but not other classes? Is that right?

protected internal System.DateTime RawTimestamp { get { throw null; } set { } }
public string SenderName { get { throw null; } }
public int ThreadId { get { throw null; } }
public System.DateTime Timestamp { get { throw null; } }
Expand Down Expand Up @@ -253,6 +253,10 @@ public partial interface IEventSource3 : Microsoft.Build.Framework.IEventSource,
void IncludeEvaluationProfiles();
void IncludeTaskInputs();
}
public partial interface IEventSource4 : Microsoft.Build.Framework.IEventSource, Microsoft.Build.Framework.IEventSource2, Microsoft.Build.Framework.IEventSource3
{
void IncludeEvaluationPropertiesAndItems();
}
public partial interface IForwardingLogger : Microsoft.Build.Framework.ILogger, Microsoft.Build.Framework.INodeLogger
{
Microsoft.Build.Framework.IEventRedirector BuildEventRedirector { get; set; }
Expand Down Expand Up @@ -381,8 +385,11 @@ public sealed partial class ProjectEvaluationFinishedEventArgs : Microsoft.Build
{
public ProjectEvaluationFinishedEventArgs() { }
public ProjectEvaluationFinishedEventArgs(string message, params object[] messageArgs) { }
public System.Collections.IEnumerable GlobalProperties { get { throw null; } set { } }
public System.Collections.IEnumerable Items { get { throw null; } set { } }
public Microsoft.Build.Framework.Profiler.ProfilerResult? ProfilerResult { get { throw null; } set { } }
public string ProjectFile { get { throw null; } set { } }
public System.Collections.IEnumerable Properties { get { throw null; } set { } }
}
public partial class ProjectEvaluationStartedEventArgs : Microsoft.Build.Framework.BuildStatusEventArgs
{
Expand Down
Expand Up @@ -227,6 +227,7 @@ public partial class MuxLogger : Microsoft.Build.Framework.ILogger, Microsoft.Bu
public MuxLogger() { }
public bool IncludeEvaluationMetaprojects { get { throw null; } set { } }
public bool IncludeEvaluationProfiles { get { throw null; } set { } }
public bool IncludeEvaluationPropertiesAndItems { get { throw null; } set { } }
public bool IncludeTaskInputs { get { throw null; } set { } }
public string Parameters { get { throw null; } set { } }
public Microsoft.Build.Framework.LoggerVerbosity Verbosity { get { throw null; } set { } }
Expand Down
Expand Up @@ -72,6 +72,7 @@ public partial class MuxLogger : Microsoft.Build.Framework.ILogger, Microsoft.Bu
public MuxLogger() { }
public bool IncludeEvaluationMetaprojects { get { throw null; } set { } }
public bool IncludeEvaluationProfiles { get { throw null; } set { } }
public bool IncludeEvaluationPropertiesAndItems { get { throw null; } set { } }
public bool IncludeTaskInputs { get { throw null; } set { } }
public string Parameters { get { throw null; } set { } }
public Microsoft.Build.Framework.LoggerVerbosity Verbosity { get { throw null; } set { } }
Expand Down
19 changes: 18 additions & 1 deletion src/Build.UnitTests/BackEnd/MockLoggingService.cs
Expand Up @@ -5,6 +5,7 @@
using System.Collections;
using System.Collections.Generic;
using Microsoft.Build.Framework;
using Microsoft.Build.Framework.Profiler;
using Microsoft.Build.BackEnd.Logging;
using Microsoft.Build.Logging;
using Microsoft.Build.Shared;
Expand Down Expand Up @@ -203,6 +204,16 @@ public bool IncludeEvaluationProfile
set { }
}

/// <summary>
/// Log properties and items on ProjectEvaluationFinishedEventArgs
/// instead of ProjectStartedEventArgs.
/// </summary>
public bool IncludeEvaluationPropertiesAndItems
{
get => false;
set { }
}

/// <summary>
/// Should task events include task inputs?
/// </summary>
Expand Down Expand Up @@ -459,7 +470,13 @@ public void LogProjectEvaluationStarted(BuildEventContext eventContext, string p
/// <summary>
/// Logs a project evaluation finished event
/// </summary>
public void LogProjectEvaluationFinished(BuildEventContext projectEvaluationEventContext, string projectFile)
public void LogProjectEvaluationFinished(
BuildEventContext projectEvaluationEventContext,
string projectFile,
IEnumerable globalProperties,
IEnumerable properties,
IEnumerable items,
ProfilerResult? profilerResult)
{
}

Expand Down
114 changes: 111 additions & 3 deletions src/Build.UnitTests/BackEnd/NodePackets_Tests.cs
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections;
using System.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.BackEnd;
Expand Down Expand Up @@ -32,6 +33,7 @@ public void LogMessageConstructorNullBuildEvent()
}
);
}

/// <summary>
/// Verify when creating a LogMessagePacket
/// that the correct Event Type is set.
Expand All @@ -53,6 +55,8 @@ public void VerifyEventType()
ProjectStartedEventArgs projectStarted = new ProjectStartedEventArgs(-1, "message", "help", "ProjectFile", "targetNames", null, null, null);
ProjectFinishedEventArgs projectFinished = new ProjectFinishedEventArgs("message", "help", "ProjectFile", true);
ExternalProjectStartedEventArgs externalStartedEvent = new ExternalProjectStartedEventArgs("message", "help", "senderName", "projectFile", "targetNames");
ProjectEvaluationStartedEventArgs evaluationStarted = new ProjectEvaluationStartedEventArgs();
ProjectEvaluationFinishedEventArgs evaluationFinished = new ProjectEvaluationFinishedEventArgs();

VerifyLoggingPacket(buildFinished, LoggingEventType.BuildFinishedEvent);
VerifyLoggingPacket(buildStarted, LoggingEventType.BuildStartedEvent);
Expand All @@ -67,16 +71,87 @@ public void VerifyEventType()
VerifyLoggingPacket(targetFinished, LoggingEventType.TargetFinishedEvent);
VerifyLoggingPacket(projectStarted, LoggingEventType.ProjectStartedEvent);
VerifyLoggingPacket(projectFinished, LoggingEventType.ProjectFinishedEvent);
VerifyLoggingPacket(evaluationStarted, LoggingEventType.ProjectEvaluationStartedEvent);
VerifyLoggingPacket(evaluationFinished, LoggingEventType.ProjectEvaluationFinishedEvent);
VerifyLoggingPacket(externalStartedEvent, LoggingEventType.CustomEvent);
}

private static TaskParameterEventArgs CreateTaskParameter()
private static BuildEventContext CreateBuildEventContext()
{
return new BuildEventContext(1, 2, 3, 4, 5, 6, 7);
}

private static ProjectEvaluationStartedEventArgs CreateProjectEvaluationStarted()
{
string projectFile = "test.csproj";
var result = new ProjectEvaluationStartedEventArgs(
ResourceUtilities.GetResourceString("EvaluationStarted"),
projectFile)
{
ProjectFile = projectFile
};
result.BuildEventContext = CreateBuildEventContext();

return result;
}

private static ProjectEvaluationFinishedEventArgs CreateProjectEvaluationFinished()
{
string projectFile = "test.csproj";
var result = new ProjectEvaluationFinishedEventArgs(
ResourceUtilities.GetResourceString("EvaluationFinished"),
projectFile)
{
ProjectFile = projectFile,
GlobalProperties = CreateProperties(),
Properties = CreateProperties(),
Items = new ArrayList
{
new DictionaryEntry("Compile", new TaskItemData("a", null)),
new DictionaryEntry("Compile", new TaskItemData("b", CreateStringDictionary())),
new DictionaryEntry("Reference", new TaskItemData("c", CreateStringDictionary())),
}
};
result.BuildEventContext = CreateBuildEventContext();

return result;
}

private static IEnumerable CreateProperties()
{
return new ArrayList
{
new DictionaryEntry("a", "b"),
new DictionaryEntry("c", "d")
};
}

private static Dictionary<string, string> CreateStringDictionary()
{
return new Dictionary<string, string>
{
{ "a", "b" },
{ "c", "d" }
};
}

private static TaskItemData[] CreateTaskItems()
{
var items = new TaskItemData[]
{
new TaskItemData("ItemSpec1", null),
new TaskItemData("ItemSpec2", Enumerable.Range(1,3).ToDictionary(i => i.ToString(), i => i.ToString() + "value"))
new TaskItemData("ItemSpec1", CreateStringDictionary()),
new TaskItemData("ItemSpec2", Enumerable.Range(1, 3).ToDictionary(i => i.ToString(), i => i.ToString() + "value"))
};
return items;
}

private static TaskParameterEventArgs CreateTaskParameter()
{
// touch ItemGroupLoggingHelper to ensure static constructor runs
_ = ItemGroupLoggingHelper.ItemGroupIncludeLogMessagePrefix;

var items = CreateTaskItems();
var result = new TaskParameterEventArgs(
TaskParameterMessageKind.TaskInput,
"ItemName",
Expand All @@ -88,6 +163,9 @@ private static TaskParameterEventArgs CreateTaskParameter()
Assert.Equal(@"Task Parameter:
ItemName=
ItemSpec1
ItemSpec1
a=b
c=d
ItemSpec2
1=1value
2=2value
Expand Down Expand Up @@ -127,7 +205,9 @@ public void TestTranslation()
new TargetFinishedEventArgs("message", "help", "targetName", "ProjectFile", "targetFile", true, targetOutputs),
new ProjectStartedEventArgs(-1, "message", "help", "ProjectFile", "targetNames", null, null, null),
new ProjectFinishedEventArgs("message", "help", "ProjectFile", true),
new ExternalProjectStartedEventArgs("message", "help", "senderName", "projectFile", "targetNames")
new ExternalProjectStartedEventArgs("message", "help", "senderName", "projectFile", "targetNames"),
CreateProjectEvaluationStarted(),
CreateProjectEvaluationFinished()
};

foreach (BuildEventArgs arg in testArgs)
Expand Down Expand Up @@ -283,6 +363,32 @@ private void CompareLogMessagePackets(LogMessagePacket left, LogMessagePacket ri
// Assert.AreEqual(leftProjectStarted.Properties, rightProjectStarted.Properties);
break;

case LoggingEventType.ProjectEvaluationStartedEvent:
ProjectEvaluationStartedEventArgs leftEvaluationStarted = left.NodeBuildEvent.Value.Value as ProjectEvaluationStartedEventArgs;
ProjectEvaluationStartedEventArgs rightEvaluationStarted = right.NodeBuildEvent.Value.Value as ProjectEvaluationStartedEventArgs;
Assert.NotNull(leftEvaluationStarted);
Assert.NotNull(rightEvaluationStarted);
Assert.Equal(leftEvaluationStarted.ProjectFile, rightEvaluationStarted.ProjectFile);
break;

case LoggingEventType.ProjectEvaluationFinishedEvent:
ProjectEvaluationFinishedEventArgs leftEvaluationFinished = left.NodeBuildEvent.Value.Value as ProjectEvaluationFinishedEventArgs;
ProjectEvaluationFinishedEventArgs rightEvaluationFinished = right.NodeBuildEvent.Value.Value as ProjectEvaluationFinishedEventArgs;
Assert.NotNull(leftEvaluationFinished);
Assert.NotNull(rightEvaluationFinished);
Assert.Equal(leftEvaluationFinished.ProjectFile, rightEvaluationFinished.ProjectFile);
Assert.Equal(leftEvaluationFinished.ProfilerResult, rightEvaluationFinished.ProfilerResult);
Assert.Equal(
TranslationHelpers.GetPropertiesString(leftEvaluationFinished.GlobalProperties),
TranslationHelpers.GetPropertiesString(rightEvaluationFinished.GlobalProperties));
Assert.Equal(
TranslationHelpers.GetPropertiesString(leftEvaluationFinished.Properties),
TranslationHelpers.GetPropertiesString(rightEvaluationFinished.Properties));
Assert.Equal(
TranslationHelpers.GetMultiItemsString(leftEvaluationFinished.Items),
TranslationHelpers.GetMultiItemsString(rightEvaluationFinished.Items));
break;

case LoggingEventType.TargetFinishedEvent:
TargetFinishedEventArgs leftTargetFinished = left.NodeBuildEvent.Value.Value as TargetFinishedEventArgs;
TargetFinishedEventArgs rightTargetFinished = right.NodeBuildEvent.Value.Value as TargetFinishedEventArgs;
Expand All @@ -292,6 +398,8 @@ private void CompareLogMessagePackets(LogMessagePacket left, LogMessagePacket ri
Assert.Equal(leftTargetFinished.Succeeded, rightTargetFinished.Succeeded);
Assert.Equal(leftTargetFinished.TargetFile, rightTargetFinished.TargetFile);
Assert.Equal(leftTargetFinished.TargetName, rightTargetFinished.TargetName);
//TODO: target output translation is a special case and is done in TranslateTargetFinishedEvent
//Assert.Equal(leftTargetFinished.TargetOutputs, rightTargetFinished.TargetOutputs);
break;

case LoggingEventType.TargetStartedEvent:
Expand Down
62 changes: 62 additions & 0 deletions src/Build.UnitTests/BackEnd/TranslationHelpers.cs
Expand Up @@ -2,10 +2,13 @@
// 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.Linq;
using System.IO;
using System.Text;
using Microsoft.Build.BackEnd;
using Microsoft.Build.Framework;

namespace Microsoft.Build.UnitTests.BackEnd
{
Expand Down Expand Up @@ -104,5 +107,64 @@ internal static bool CompareExceptions(Exception left, Exception right)

return CompareExceptions(left.InnerException, right.InnerException);
}

internal static string GetPropertiesString(IEnumerable properties)
{
var dictionary = properties
.OfType<DictionaryEntry>()
.ToDictionary(
(Func<DictionaryEntry, string>)(d => d.Key.ToString()),
(Func<DictionaryEntry, string>)(d => d.Value.ToString()));
return ToString(dictionary);
}

internal static string GetMultiItemsString(IEnumerable items)
{
var list = items
.OfType<DictionaryEntry>()
.Select(i => i.Key.ToString() + GetTaskItemString(i.Value));
var text = string.Join("\n", list);
return text;
}

internal static string GetItemsString(IEnumerable items)
{
var list = items
.OfType<object>()
.Select(i => GetTaskItemString(i));
var text = string.Join("\n", list);
return text;
}

internal static string GetTaskItemString(object item)
{
var sb = new StringBuilder();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReusableStringBuilder?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests, we don't care ;) I doubt there will be a difference on the test data of this size.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right; didn't notice.


if (item is ITaskItem taskItem)
{
sb.Append(taskItem.ItemSpec);
foreach (string name in taskItem.MetadataNames)
{
var value = taskItem.GetMetadata(name);
sb.Append($";{name}={value}");
}
}
else
{
sb.Append(Convert.ToString(item));
}

return sb.ToString();
}

internal static string ToString(IDictionary<string, string> dictionary)
{
if (dictionary == null)
{
return "null";
}

return string.Join(";", dictionary.Select(kvp => kvp.Key + "=" + kvp.Value));
}
}
}