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

Add Buildcheck events support + BuildSubmissionStarted #797

8 changes: 7 additions & 1 deletion src/StructuredLogger/BinaryLogger/BinaryLogRecordKind.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ public enum BinaryLogRecordKind
String,
TaskParameter,
FileUsed,
AssemblyLoad
AssemblyLoad,
BuildCheckMessage,
BuildCheckWarning,
BuildCheckError,
BuildCheckTracing,
BuildCheckAcquisition,
BuildSubmissionStarted,
}
}
5 changes: 4 additions & 1 deletion src/StructuredLogger/BinaryLogger/BinaryLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,17 @@ public sealed class BinaryLogger : ILogger
// - TaskParameterEventArgs: Added ParameterName and PropertyName properties
// version 22:
// - extend EnvironmentVariableRead with location where environment variable was used.
// version 23:
// - new record kinds: BuildCheckMessageEvent, BuildCheckWarningEvent, BuildCheckErrorEvent,
// BuildCheckTracingEvent, BuildCheckAcquisitionEvent, BuildSubmissionStartedEvent

// This should be never changed.
// The minimum version of the binary log reader that can read log of above version.
internal const int ForwardCompatibilityMinimalVersion = 18;

// The current version of the binary log representation.
// Changes with each update of the binary log format.
internal const int FileFormatVersion = 22;
internal const int FileFormatVersion = 23;
// The minimum version of the binary log reader that can read log of above version.
// This should be changed only when the binary log format is changed in a way that would prevent it from being
// read by older readers. (changing of the individual BuildEventArgs or adding new is fine - as reader can
Expand Down
29 changes: 29 additions & 0 deletions src/StructuredLogger/BinaryLogger/BuildCheckEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using Microsoft.Build.Framework;

namespace StructuredLogger.BinaryLogger
{
/// <summary>
/// Base class for all build check event args.
/// Not intended to be extended by external code.
/// </summary>
internal abstract class BuildCheckEventArgs : BuildEventArgs
{ }

/// <summary>
/// Transport mean for the BuildCheck tracing data from additional nodes.
/// </summary>
/// <param name="tracingData"></param>
internal sealed class BuildCheckTracingEventArgs(Dictionary<string, TimeSpan> tracingData) : BuildCheckEventArgs
{
public Dictionary<string, TimeSpan> TracingData { get; private set; } = tracingData;
}

internal sealed class BuildCheckAcquisitionEventArgs(string acquisitionPath, string projectPath) : BuildCheckEventArgs
{
public string AcquisitionPath { get; private set; } = acquisitionPath;

public string ProjectPath { get; private set; } = projectPath;
}
}
71 changes: 65 additions & 6 deletions src/StructuredLogger/BinaryLogger/BuildEventArgsReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,18 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Buffers;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using DotUtils.StreamUtils;
using Microsoft.Build.BackEnd;
using Microsoft.Build.Collections;
using Microsoft.Build.Framework;
using Microsoft.Build.Framework.Profiler;
using Microsoft.Build.Internal;
using Microsoft.Build.Shared;
using StructuredLogger.BinaryLogger;

Expand Down Expand Up @@ -331,7 +327,14 @@ void HandleError(FormatErrorMessage msgFactory, bool noThrow, ReaderErrorType re
BinaryLogRecordKind.UninitializedPropertyRead => ReadUninitializedPropertyReadEventArgs(),
BinaryLogRecordKind.PropertyInitialValueSet => ReadPropertyInitialValueSetEventArgs(),
BinaryLogRecordKind.AssemblyLoad => ReadAssemblyLoadEventArgs(),
_ => null
BinaryLogRecordKind.BuildCheckMessage => ReadBuildMessageEventArgs(),
BinaryLogRecordKind.BuildCheckWarning => ReadBuildWarningEventArgs(),
BinaryLogRecordKind.BuildCheckError => ReadBuildErrorEventArgs(),
BinaryLogRecordKind.BuildCheckTracing => ReadBuildCheckTracingEventArgs(),
BinaryLogRecordKind.BuildCheckAcquisition => ReadBuildCheckAcquisitionEventArgs(),
BinaryLogRecordKind.BuildSubmissionStarted => ReadBuildSubmissionStartedEventArgs(),
_ => null,

};

private void SkipBytes(int count)
Expand Down Expand Up @@ -1245,6 +1248,49 @@ private BuildEventArgs ReadEnvironmentVariableReadEventArgs()
return e;
}

private BuildEventArgs ReadBuildCheckTracingEventArgs()
{
var fields = ReadBuildEventArgsFields(readImportance: true);
var rawTracingData = ReadStringDictionary() ?? new Dictionary<string, string>();

var e = new BuildCheckTracingEventArgs(rawTracingData.ToDictionary(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this class already known to viewer?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

image

kvp => kvp.Key,
kvp => TimeSpan.FromTicks(long.Parse(kvp.Value))));
SetCommonFields(e, fields);
return e;
}

private BuildEventArgs ReadBuildCheckAcquisitionEventArgs()
{
var fields = ReadBuildEventArgsFields(readImportance: false);
var acquisitionPath = ReadDeduplicatedString();
var projectPath = ReadDeduplicatedString();
var e = new BuildCheckAcquisitionEventArgs(acquisitionPath, projectPath);
SetCommonFields(e, fields);
return e;
}

private BuildEventArgs ReadBuildSubmissionStartedEventArgs()
{
var fields = ReadBuildEventArgsFields(readImportance: false);
var e = new BuildSubmissionStartedEvent();

var globalProperties = ReadStringDictionary();
var entries = ReadStringList();
var targetNames = ReadStringList();
var flags = (BuildRequestDataFlags)ReadInt32();
var submissionId = ReadInt32();

e.GlobalProperties = globalProperties;
e.EntryProjectsFullPath = entries;
e.TargetNames = targetNames;
e.SubmissionId = submissionId;

SetCommonFields(e, fields);

return e;
}

private BuildEventArgs ReadFileUsedEventArgs()
{
var fields = ReadBuildEventArgsFields(readImportance: false);
Expand Down Expand Up @@ -1722,6 +1768,19 @@ private IEnumerable ReadProjectItems()
return list;
}

private IEnumerable<string> ReadStringList()
{
var count = ReadInt32();

var list = new string[count];
for (int i = 0; i < count; i++)
{
list[i] = ReadDeduplicatedString();
}

return list;
}

private IEnumerable ReadTaskItemList()
{
int count = ReadInt32();
Expand Down
91 changes: 91 additions & 0 deletions src/StructuredLogger/BinaryLogger/BuildSubmissionStartedEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using Microsoft.Build.Framework;

namespace StructuredLogger.BinaryLogger
{
internal class BuildSubmissionStartedEvent()
: BuildStatusEventArgs(message: "", helpKeyword: null, senderName: null, eventTimestamp: DateTime.UtcNow)
{
public IDictionary<string, string?> GlobalProperties { get; set; }

public IEnumerable<string> EntryProjectsFullPath { get; set; }

public IEnumerable<string> TargetNames { get; set; }

public BuildRequestDataFlags Flags { get; set; }

public int SubmissionId { get; set; }
}

public enum BuildRequestDataFlags
{
/// <summary>
/// No flags.
/// </summary>
None = 0,

/// <summary>
/// When this flag is present, the existing ProjectInstance in the build will be replaced by this one.
/// </summary>
ReplaceExistingProjectInstance = 1 << 0,

/// <summary>
/// When this flag is present, the "BuildResult" issued in response to this request will
/// include "BuildResult.ProjectStateAfterBuild".
/// </summary>
ProvideProjectStateAfterBuild = 1 << 1,

/// <summary>
/// When this flag is present and the project has previously been built on a node whose affinity is
/// incompatible with the affinity this request requires, we will ignore the project state (but not
/// target results) that were previously generated.
/// </summary>
/// <remarks>
/// This usually is not desired behavior. It is only provided for those cases where the client
/// knows that the new build request does not depend on project state generated by a previous request. Setting
/// this flag can provide a performance boost in the case of incompatible node affinities, as MSBuild would
/// otherwise have to serialize the project state from one node to another, which may be
/// expensive depending on how much data the project previously generated.
///
/// This flag has no effect on target results, so if a previous request already built a target, the new
/// request will not re-build that target (nor will any of the project state mutations which previously
/// occurred as a consequence of building that target be re-applied.)
/// </remarks>
IgnoreExistingProjectState = 1 << 2,

/// <summary>
/// When this flag is present, caches including the "ProjectRootElementCacheBase" will be cleared
/// after the build request completes. This is used when the build request is known to modify a lot of
/// state such as restoring packages or generating parts of the import graph.
/// </summary>
ClearCachesAfterBuild = 1 << 3,

/// <summary>
/// When this flag is present, the top level target(s) in the build request will be skipped if those targets
/// are not defined in the Project to build. This only applies to this build request (if another target calls
/// the "missing target" at any other point this will still result in an error).
/// </summary>
SkipNonexistentTargets = 1 << 4,

/// <summary>
/// When this flag is present, the "BuildResult" issued in response to this request will
/// include a "BuildResult.ProjectStateAfterBuild" that includes ONLY the
/// explicitly-requested properties, items, and metadata.
/// </summary>
ProvideSubsetOfStateAfterBuild = 1 << 5,

/// <summary>
/// When this flag is present, projects loaded during build will ignore missing imports ("ProjectLoadSettings.IgnoreMissingImports" and "ProjectLoadSettings.IgnoreInvalidImports").
/// This is especially useful during a restore since some imports might come from packages that haven't been restored yet.
/// </summary>
IgnoreMissingEmptyAndInvalidImports = 1 << 6,

/// <summary>
/// When this flag is present, an unresolved MSBuild project SDK will fail the build. This flag is used to
/// change the "IgnoreMissingEmptyAndInvalidImports" behavior to still fail when an SDK is missing
/// because those are more fatal.
/// </summary>
FailOnUnresolvedSdk = 1 << 7,
}
}