From 21f2ab84cc7b9e0d342800bd2cae1a7103732bf3 Mon Sep 17 00:00:00 2001 From: Roman Konecny Date: Tue, 20 Jun 2023 10:36:03 +0200 Subject: [PATCH 01/29] Fix to properly serialize TargetFinishedEventArgs.TargetOutput --- src/Build/BackEnd/Components/Communications/LogMessagePacket.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Build/BackEnd/Components/Communications/LogMessagePacket.cs b/src/Build/BackEnd/Components/Communications/LogMessagePacket.cs index c3b525eec89..1bcd3206909 100644 --- a/src/Build/BackEnd/Components/Communications/LogMessagePacket.cs +++ b/src/Build/BackEnd/Components/Communications/LogMessagePacket.cs @@ -31,7 +31,7 @@ internal LogMessagePacket(KeyValuePair? nodeBuildEvent) /// Constructor for deserialization /// private LogMessagePacket(ITranslator translator) - : base(translator) + : base(translator, new TargetFinishedTranslator(TranslateTargetFinishedEvent)) { } From efc9def861ddf5f18c43b7fa1702100a0a44541a Mon Sep 17 00:00:00 2001 From: Roman Konecny Date: Tue, 20 Jun 2023 10:49:56 +0200 Subject: [PATCH 02/29] New extended EventArgs for custom events data --- .../Components/RequestBuilder/TaskHost.cs | 4 +- src/Build/Instance/TaskFactoryLoggingHost.cs | 4 +- .../BinaryLogger/BuildEventArgsWriter.cs | 5 +- src/Framework/ExtendedBuildErrorEventArgs.cs | 126 +++++++++++++++ .../ExtendedBuildMessageEventArgs.cs | 144 ++++++++++++++++++ .../ExtendedBuildWarningEventArgs.cs | 126 +++++++++++++++ src/Framework/ExtendedCustomBuildEventArgs.cs | 80 ++++++++++ src/Framework/IExtendedBuildEventArgs.cs | 33 ++++ src/MSBuild/OutOfProcTaskHostNode.cs | 4 +- src/Shared/BinaryReaderExtensions.cs | 26 ++++ src/Shared/BinaryWriterExtensions.cs | 19 +++ src/Shared/LogMessagePacketBase.cs | 40 +++++ 12 files changed, 607 insertions(+), 4 deletions(-) create mode 100644 src/Framework/ExtendedBuildErrorEventArgs.cs create mode 100644 src/Framework/ExtendedBuildMessageEventArgs.cs create mode 100644 src/Framework/ExtendedBuildWarningEventArgs.cs create mode 100644 src/Framework/ExtendedCustomBuildEventArgs.cs create mode 100644 src/Framework/IExtendedBuildEventArgs.cs diff --git a/src/Build/BackEnd/Components/RequestBuilder/TaskHost.cs b/src/Build/BackEnd/Components/RequestBuilder/TaskHost.cs index 3e0ea3b7abf..0a272a124db 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/TaskHost.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/TaskHost.cs @@ -1079,7 +1079,9 @@ internal void MarkAsInactive() /// internal bool IsEventSerializable(BuildEventArgs e) { - if (!e.GetType().GetTypeInfo().IsSerializable) + // Types which are not serializable and are not IExtendedBuildEventArgs as + // those always implement custom serialization by WriteToStream and CreateFromStream. + if (!e.GetType().GetTypeInfo().IsSerializable && e is not IExtendedBuildEventArgs) { _taskLoggingContext.LogWarning(null, new BuildEventFileInfo(string.Empty), "ExpectedEventToBeSerializable", e.GetType().Name); return false; diff --git a/src/Build/Instance/TaskFactoryLoggingHost.cs b/src/Build/Instance/TaskFactoryLoggingHost.cs index 379987804ae..f447a4d2bbd 100644 --- a/src/Build/Instance/TaskFactoryLoggingHost.cs +++ b/src/Build/Instance/TaskFactoryLoggingHost.cs @@ -337,7 +337,9 @@ internal void MarkAsInactive() /// internal bool IsEventSerializable(BuildEventArgs e) { - if (!e.GetType().GetTypeInfo().IsSerializable) + // Types which are not serializable and are not IExtendedBuildEventArgs as + // those always implement custom serialization by WriteToStream and CreateFromStream. + if (!e.GetType().GetTypeInfo().IsSerializable && e is not IExtendedBuildEventArgs) { _loggingContext.LogWarning(null, new BuildEventFileInfo(string.Empty), "ExpectedEventToBeSerializable", e.GetType().Name); return false; diff --git a/src/Build/Logging/BinaryLogger/BuildEventArgsWriter.cs b/src/Build/Logging/BinaryLogger/BuildEventArgsWriter.cs index cf69bcbacbc..25a41c96be7 100644 --- a/src/Build/Logging/BinaryLogger/BuildEventArgsWriter.cs +++ b/src/Build/Logging/BinaryLogger/BuildEventArgsWriter.cs @@ -156,6 +156,7 @@ Base types and inheritance ("EventArgs" suffix omitted): TaskCommandLine TaskParameter UninitializedPropertyRead + ExtendedMessage BuildStatus TaskStarted TaskFinished @@ -168,11 +169,13 @@ Base types and inheritance ("EventArgs" suffix omitted): ProjectEvaluationStarted ProjectEvaluationFinished BuildError + ExtendedBuildError BuildWarning + ExtendedBuildWarning CustomBuild ExternalProjectStarted ExternalProjectFinished - + ExtendedCustomBuild */ private void WriteCore(BuildEventArgs e) diff --git a/src/Framework/ExtendedBuildErrorEventArgs.cs b/src/Framework/ExtendedBuildErrorEventArgs.cs new file mode 100644 index 00000000000..fa63d0e52f8 --- /dev/null +++ b/src/Framework/ExtendedBuildErrorEventArgs.cs @@ -0,0 +1,126 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Build.Shared; + +namespace Microsoft.Build.Framework; + +/// +/// Generic custom error events including extended data for event enriching. +/// Extended data are implemented by +/// +public sealed class ExtendedBuildErrorEventArgs : BuildErrorEventArgs, IExtendedBuildEventArgs +{ + /// + public string ExtendedType { get; set; } + + /// + public Dictionary? ExtendedMetadata { get; set; } + + /// + public string? ExtendedData { get; set; } + + /// + /// Default constructor. Used for deserialization. + /// + public ExtendedBuildErrorEventArgs() : this("undefined") { } + + /// + /// This constructor specifies only type of extended data. + /// + /// Type of . + public ExtendedBuildErrorEventArgs(string type) => ExtendedType = type; + + /// + /// This constructor allows all event data to be initialized + /// + /// Type of . + /// event sub-category + /// event code + /// file associated with the event + /// line number (0 if not applicable) + /// column number (0 if not applicable) + /// end line number (0 if not applicable) + /// end column number (0 if not applicable) + /// text message + /// help keyword + /// name of event sender + public ExtendedBuildErrorEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber, + string? message, string? helpKeyword, string? senderName) + : base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName) => ExtendedType = type; + + /// + /// This constructor which allows a timestamp to be set + /// + /// Type of . + /// event sub-category + /// event code + /// file associated with the event + /// line number (0 if not applicable) + /// column number (0 if not applicable) + /// end line number (0 if not applicable) + /// end column number (0 if not applicable) + /// text message + /// help keyword + /// name of event sender + /// Timestamp when event was created + public ExtendedBuildErrorEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber, + string? message, string? helpKeyword, string? senderName, DateTime eventTimestamp) + : base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName, eventTimestamp) => ExtendedType = type; + + /// + /// This constructor which allows a timestamp to be set + /// + /// Type of . + /// event sub-category + /// event code + /// file associated with the event + /// line number (0 if not applicable) + /// column number (0 if not applicable) + /// end line number (0 if not applicable) + /// end column number (0 if not applicable) + /// text message + /// help keyword + /// name of event sender + /// Timestamp when event was created + /// message arguments + public ExtendedBuildErrorEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber, + string? message, string? helpKeyword, string? senderName, DateTime eventTimestamp, params object[]? messageArgs) + : base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName, eventTimestamp, messageArgs) => ExtendedType = type; + + /// + /// This constructor which allows a timestamp to be set + /// + /// Type of . + /// event sub-category + /// event code + /// file associated with the event + /// line number (0 if not applicable) + /// column number (0 if not applicable) + /// end line number (0 if not applicable) + /// end column number (0 if not applicable) + /// text message + /// help keyword + /// A link pointing to more information about the error + /// name of event sender + /// Timestamp when event was created + /// message arguments + public ExtendedBuildErrorEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber, + string? message, string? helpKeyword, string? senderName, string? helpLink, DateTime eventTimestamp, params object[]? messageArgs) + : base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName, helpLink, eventTimestamp, messageArgs) => ExtendedType = type; + + internal override void WriteToStream(BinaryWriter writer) + { + base.WriteToStream(writer); + writer.WriteExtendedBuildEventData(this); + } + + internal override void CreateFromStream(BinaryReader reader, int version) + { + base.CreateFromStream(reader, version); + reader.ReadExtendedBuildEventData(this); + } +} diff --git a/src/Framework/ExtendedBuildMessageEventArgs.cs b/src/Framework/ExtendedBuildMessageEventArgs.cs new file mode 100644 index 00000000000..daa932c8bea --- /dev/null +++ b/src/Framework/ExtendedBuildMessageEventArgs.cs @@ -0,0 +1,144 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Build.Shared; + +namespace Microsoft.Build.Framework; + +/// +/// Generic custom build events including extended data for event enriching. +/// Extended data are implemented by +/// +public sealed class ExtendedBuildMessageEventArgs : BuildMessageEventArgs, IExtendedBuildEventArgs +{ + /// + public string ExtendedType { get; set; } + + /// + public Dictionary? ExtendedMetadata { get; set; } + + /// + public string? ExtendedData { get; set; } + + /// + /// Default constructor. Used for deserialization. + /// + public ExtendedBuildMessageEventArgs() : this("undefined") { } + + /// + /// This constructor specifies only type of extended data. + /// + /// Type of . + public ExtendedBuildMessageEventArgs(string type) => ExtendedType = type; + + /// + /// This constructor allows all event data to be initialized + /// + /// Type of . + /// text message + /// help keyword + /// name of event sender + /// importance of the message + public ExtendedBuildMessageEventArgs(string type, string? message, string? helpKeyword, string? senderName, MessageImportance importance) + : base(message, helpKeyword, senderName, importance) => ExtendedType = type; + + /// + /// This constructor allows a timestamp to be set + /// + /// Type of . + /// text message + /// help keyword + /// name of event sender + /// importance of the message + /// Timestamp when event was created + public ExtendedBuildMessageEventArgs(string type, string? message, string? helpKeyword, string? senderName, MessageImportance importance, DateTime eventTimestamp) + : base(message, helpKeyword, senderName, importance, eventTimestamp) => ExtendedType = type; + + /// + /// This constructor allows a timestamp to be set + /// + /// Type of . + /// text message + /// help keyword + /// name of event sender + /// importance of the message + /// Timestamp when event was created + /// message arguments + public ExtendedBuildMessageEventArgs(string type, string? message, string? helpKeyword, string? senderName, MessageImportance importance, DateTime eventTimestamp, params object[]? messageArgs) + : base(message, helpKeyword, senderName, importance, eventTimestamp, messageArgs) => ExtendedType = type; + + /// + /// This constructor allows all event data to be initialized + /// + /// Type of . + /// event sub-category + /// event code + /// file associated with the event + /// line number (0 if not applicable) + /// column number (0 if not applicable) + /// end line number (0 if not applicable) + /// end column number (0 if not applicable) + /// text message + /// help keyword + /// name of event sender + /// importance of the message + public ExtendedBuildMessageEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber, + string? message, string? helpKeyword, string? senderName, MessageImportance importance) + : base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName, importance) => ExtendedType = type; + + /// + /// This constructor which allows a timestamp to be set + /// + /// Type of . + /// event sub-category + /// event code + /// file associated with the event + /// line number (0 if not applicable) + /// column number (0 if not applicable) + /// end line number (0 if not applicable) + /// end column number (0 if not applicable) + /// text message + /// help keyword + /// name of event sender + /// importance of the message + /// Timestamp when event was created + public ExtendedBuildMessageEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber, + string? message, string? helpKeyword, string? senderName, MessageImportance importance, DateTime eventTimestamp) + : base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName, importance, eventTimestamp) => ExtendedType = type; + + /// + /// This constructor which allows a timestamp to be set + /// + /// Type of . + /// event sub-category + /// event code + /// file associated with the event + /// line number (0 if not applicable) + /// column number (0 if not applicable) + /// end line number (0 if not applicable) + /// end column number (0 if not applicable) + /// text message + /// help keyword + /// name of event sender + /// importance of the message + /// Timestamp when event was created + /// message arguments + public ExtendedBuildMessageEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber, + string? message, string? helpKeyword, string? senderName, MessageImportance importance, DateTime eventTimestamp, params object[]? messageArgs) + : base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName, importance, eventTimestamp, messageArgs) => ExtendedType = type; + + internal override void WriteToStream(BinaryWriter writer) + { + base.WriteToStream(writer); + writer.WriteExtendedBuildEventData(this); + } + + internal override void CreateFromStream(BinaryReader reader, int version) + { + base.CreateFromStream(reader, version); + reader.ReadExtendedBuildEventData(this); + } +} diff --git a/src/Framework/ExtendedBuildWarningEventArgs.cs b/src/Framework/ExtendedBuildWarningEventArgs.cs new file mode 100644 index 00000000000..d06fdf64121 --- /dev/null +++ b/src/Framework/ExtendedBuildWarningEventArgs.cs @@ -0,0 +1,126 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Build.Shared; + +namespace Microsoft.Build.Framework; + +/// +/// Generic custom warning events including extended data for event enriching. +/// Extended data are implemented by +/// +public sealed class ExtendedBuildWarningEventArgs : BuildWarningEventArgs, IExtendedBuildEventArgs +{ + /// + public string ExtendedType { get; set; } + + /// + public Dictionary? ExtendedMetadata { get; set; } + + /// + public string? ExtendedData { get; set; } + + /// + /// Default constructor. Used for deserialization. + /// + public ExtendedBuildWarningEventArgs() : this("undefined") { } + + /// + /// This constructor specifies only type of extended data. + /// + /// Type of . + public ExtendedBuildWarningEventArgs(string type) => ExtendedType = type; + + /// + /// This constructor allows all event data to be initialized + /// + /// Type of . + /// event sub-category + /// event code + /// file associated with the event + /// line number (0 if not applicable) + /// column number (0 if not applicable) + /// end line number (0 if not applicable) + /// end column number (0 if not applicable) + /// text message + /// help keyword + /// name of event sender + public ExtendedBuildWarningEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber, + string? message, string? helpKeyword, string? senderName) + : base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName) => ExtendedType = type; + + /// + /// This constructor which allows a timestamp to be set + /// + /// Type of . + /// event sub-category + /// event code + /// file associated with the event + /// line number (0 if not applicable) + /// column number (0 if not applicable) + /// end line number (0 if not applicable) + /// end column number (0 if not applicable) + /// text message + /// help keyword + /// name of event sender + /// Timestamp when event was created + public ExtendedBuildWarningEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber, + string? message, string? helpKeyword, string? senderName, DateTime eventTimestamp) + : base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName, eventTimestamp) => ExtendedType = type; + + /// + /// This constructor which allows a timestamp to be set + /// + /// Type of . + /// event sub-category + /// event code + /// file associated with the event + /// line number (0 if not applicable) + /// column number (0 if not applicable) + /// end line number (0 if not applicable) + /// end column number (0 if not applicable) + /// text message + /// help keyword + /// name of event sender + /// Timestamp when event was created + /// message arguments + public ExtendedBuildWarningEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber, + string? message, string? helpKeyword, string? senderName, DateTime eventTimestamp, params object[]? messageArgs) + : base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName, eventTimestamp, messageArgs) => ExtendedType = type; + + /// + /// This constructor which allows a timestamp to be set + /// + /// Type of . + /// event sub-category + /// event code + /// file associated with the event + /// line number (0 if not applicable) + /// column number (0 if not applicable) + /// end line number (0 if not applicable) + /// end column number (0 if not applicable) + /// text message + /// help keyword + /// A link pointing to more information about the error + /// name of event sender + /// Timestamp when event was created + /// message arguments + public ExtendedBuildWarningEventArgs(string type, string? subcategory, string? code, string? file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber, + string? message, string? helpKeyword, string? senderName, string? helpLink, DateTime eventTimestamp, params object[]? messageArgs) + : base(subcategory, code, file, lineNumber, columnNumber, endLineNumber, endColumnNumber, message, helpKeyword, senderName, helpLink, eventTimestamp, messageArgs) => ExtendedType = type; + + internal override void WriteToStream(BinaryWriter writer) + { + base.WriteToStream(writer); + writer.WriteExtendedBuildEventData(this); + } + + internal override void CreateFromStream(BinaryReader reader, int version) + { + base.CreateFromStream(reader, version); + reader.ReadExtendedBuildEventData(this); + } +} diff --git a/src/Framework/ExtendedCustomBuildEventArgs.cs b/src/Framework/ExtendedCustomBuildEventArgs.cs new file mode 100644 index 00000000000..3329ccaaac2 --- /dev/null +++ b/src/Framework/ExtendedCustomBuildEventArgs.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Build.Shared; + +namespace Microsoft.Build.Framework; + +/// +/// Generic custom event. +/// Extended data are implemented by +/// +public sealed class ExtendedCustomBuildEventArgs : CustomBuildEventArgs, IExtendedBuildEventArgs +{ + /// + public string ExtendedType { get; set; } + + /// + public Dictionary? ExtendedMetadata { get; set; } + + /// + public string? ExtendedData { get; set; } + + /// + /// This constructor allows event data to be initialized. + /// + /// + public ExtendedCustomBuildEventArgs() : this("undefined") {} + + /// + /// This constructor allows event data to be initialized. + /// + /// Type of . + /// + public ExtendedCustomBuildEventArgs(string type) => ExtendedType = type; + + /// + /// This constructor allows event data to be initialized. + /// + /// Type of . + /// text message + /// help keyword + /// name of sender + public ExtendedCustomBuildEventArgs(string type, string? message, string? helpKeyword, string? senderName) : base(message, helpKeyword, senderName) => ExtendedType = type; + + /// + /// This constructor allows event data to be initialized including timestamp. + /// + /// Type of . + /// text message + /// help keyword + /// name of sender + /// Timestamp when event was created + public ExtendedCustomBuildEventArgs(string type, string? message, string? helpKeyword, string? senderName, DateTime eventTimestamp) : base(message, helpKeyword, senderName, eventTimestamp) => ExtendedType = type; + + /// + /// This constructor allows event data to be initialized including timestamp. + /// + /// Type of . + /// text message + /// help keyword + /// name of sender + /// Timestamp when event was created + /// Message arguments + public ExtendedCustomBuildEventArgs(string type, string? message, string? helpKeyword, string? senderName, DateTime eventTimestamp, params object[]? messageArgs) : base(message, helpKeyword, senderName, eventTimestamp, messageArgs) => ExtendedType = type; + + internal override void WriteToStream(BinaryWriter writer) + { + base.WriteToStream(writer); + writer.WriteExtendedBuildEventData(this); + } + + internal override void CreateFromStream(BinaryReader reader, int version) + { + base.CreateFromStream(reader, version); + reader.ReadExtendedBuildEventData(this); + } +} diff --git a/src/Framework/IExtendedBuildEventArgs.cs b/src/Framework/IExtendedBuildEventArgs.cs new file mode 100644 index 00000000000..97510566550 --- /dev/null +++ b/src/Framework/IExtendedBuildEventArgs.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +namespace Microsoft.Build.Framework; + +/// +/// Interface for Extended EventArgs to allow enriching particular events with extended data. +/// Deriving from EventArgs will be deprecated soon and using Extended EventArgs is recommended for custom Event Args. +/// +public interface IExtendedBuildEventArgs +{ + /// + /// Unique string identifying type of extended data so receiver side knows how to interpret, deserialize and handle . + /// + string ExtendedType { get; set; } + + /// + /// Metadata of . + /// Example usage: + /// - data which needed in custom code to properly routing this message without interpreting/deserializing . + /// - simple extended data can be transferred in form of dictionary key-value per one extended property. + /// + Dictionary? ExtendedMetadata { get; set; } + + /// + /// Transparent data as string. + /// Custom code is responsible to serialize and deserialize this string to structured data - if needed. + /// Custom code can use any serialization they deem safe, though we expect json mostly. In case on binary data, base64 can be used. + /// + string? ExtendedData { get; set; } +} diff --git a/src/MSBuild/OutOfProcTaskHostNode.cs b/src/MSBuild/OutOfProcTaskHostNode.cs index f1f7c3b7ffe..74203401ec1 100644 --- a/src/MSBuild/OutOfProcTaskHostNode.cs +++ b/src/MSBuild/OutOfProcTaskHostNode.cs @@ -1146,7 +1146,9 @@ private void SendBuildEvent(BuildEventArgs e) { if (_nodeEndpoint?.LinkStatus == LinkStatus.Active) { - if (!e.GetType().GetTypeInfo().IsSerializable) + // Types which are not serializable and are not IExtendedBuildEventArgs as + // those always implement custom serialization by WriteToStream and CreateFromStream. + if (!e.GetType().GetTypeInfo().IsSerializable && e is not IExtendedBuildEventArgs) { // log a warning and bail. This will end up re-calling SendBuildEvent, but we know for a fact // that the warning that we constructed is serializable, so everything should be good. diff --git a/src/Shared/BinaryReaderExtensions.cs b/src/Shared/BinaryReaderExtensions.cs index cb3857ff100..6c3372fd6f9 100644 --- a/src/Shared/BinaryReaderExtensions.cs +++ b/src/Shared/BinaryReaderExtensions.cs @@ -81,5 +81,31 @@ public static unsafe Guid ReadGuid(this BinaryReader reader) { return new Guid(reader.ReadBytes(sizeof(Guid))); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadExtendedBuildEventData(this BinaryReader reader, IExtendedBuildEventArgs data) + { + data.ExtendedType = reader.ReadString(); + data.ExtendedData = reader.ReadOptionalString(); + + bool haveMetadata = reader.ReadBoolean(); + if (haveMetadata) + { + data.ExtendedMetadata = new(); + + int count = reader.Read7BitEncodedInt(); + for (int i = 0; i < count; i++) + { + string key = reader.ReadString(); + string? value = reader.ReadOptionalString(); + + data.ExtendedMetadata.Add(key, value); + } + } + else + { + data.ExtendedMetadata = null; + } + } } } diff --git a/src/Shared/BinaryWriterExtensions.cs b/src/Shared/BinaryWriterExtensions.cs index 2a221d573a5..daaf7ebb7c1 100644 --- a/src/Shared/BinaryWriterExtensions.cs +++ b/src/Shared/BinaryWriterExtensions.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; using Microsoft.Build.Framework; @@ -85,5 +86,23 @@ public static void WriteGuid(this BinaryWriter writer, Guid value) } } } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteExtendedBuildEventData(this BinaryWriter writer, IExtendedBuildEventArgs data) + { + writer.Write(data.ExtendedType); + writer.WriteOptionalString(data.ExtendedData); + + writer.Write(data.ExtendedMetadata != null); + if (data.ExtendedMetadata != null) + { + writer.Write7BitEncodedInt(data.ExtendedMetadata.Count); + foreach (KeyValuePair kvp in data.ExtendedMetadata) + { + writer.Write(kvp.Key); + writer.WriteOptionalString(kvp.Value); + } + } + } } } diff --git a/src/Shared/LogMessagePacketBase.cs b/src/Shared/LogMessagePacketBase.cs index b885d081a74..8ba83f7531e 100644 --- a/src/Shared/LogMessagePacketBase.cs +++ b/src/Shared/LogMessagePacketBase.cs @@ -145,6 +145,26 @@ internal enum LoggingEventType : int /// Event is an AssemblyLoadBuildEventArgs /// AssemblyLoadEvent = 21, + + /// + /// Event is + /// + ExtendedCustomEvent = 24, + + /// + /// Event is + /// + ExtendedBuildErrorEvent = 25, + + /// + /// Event is + /// + ExtendedBuildWarningEvent = 26, + + /// + /// Event is + /// + ExtendedBuildMessageEvent = 27, } #endregion @@ -536,6 +556,10 @@ private BuildEventArgs GetBuildEventArgFromId() LoggingEventType.ProjectImportedEvent => new ProjectImportedEventArgs(), LoggingEventType.TargetSkipped => new TargetSkippedEventArgs(), LoggingEventType.Telemetry => new TelemetryEventArgs(), + LoggingEventType.ExtendedCustomEvent => new ExtendedCustomBuildEventArgs(), + LoggingEventType.ExtendedBuildErrorEvent => new ExtendedBuildErrorEventArgs(), + LoggingEventType.ExtendedBuildWarningEvent => new ExtendedBuildWarningEventArgs(), + LoggingEventType.ExtendedBuildMessageEvent => new ExtendedBuildMessageEventArgs(), #endif _ => throw new InternalErrorException("Should not get to the default of GetBuildEventArgFromId ID: " + _eventType) }; @@ -598,6 +622,22 @@ private LoggingEventType GetLoggingEventId(BuildEventArgs eventArg) { return LoggingEventType.AssemblyLoadEvent; } + else if (eventType == typeof(ExtendedCustomBuildEventArgs)) + { + return LoggingEventType.ExtendedCustomEvent; + } + else if (eventType == typeof(ExtendedBuildErrorEventArgs)) + { + return LoggingEventType.ExtendedBuildErrorEvent; + } + else if (eventType == typeof(ExtendedBuildWarningEventArgs)) + { + return LoggingEventType.ExtendedBuildWarningEvent; + } + else if (eventType == typeof(ExtendedBuildMessageEventArgs)) + { + return LoggingEventType.ExtendedBuildMessageEvent; + } #endif else if (eventType == typeof(TargetStartedEventArgs)) { From c09abb476759ebeb3333d8c8734042fe7d4e415c Mon Sep 17 00:00:00 2001 From: Roman Konecny Date: Tue, 20 Jun 2023 10:50:45 +0200 Subject: [PATCH 03/29] Fix log LogMessagePacketBase constructor --- src/Shared/LogMessagePacketBase.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Shared/LogMessagePacketBase.cs b/src/Shared/LogMessagePacketBase.cs index 8ba83f7531e..e8e8604b600 100644 --- a/src/Shared/LogMessagePacketBase.cs +++ b/src/Shared/LogMessagePacketBase.cs @@ -250,8 +250,9 @@ internal LogMessagePacketBase(KeyValuePair? nodeBuildEvent, /// /// Constructor for deserialization /// - protected LogMessagePacketBase(ITranslator translator) + protected LogMessagePacketBase(ITranslator translator, TargetFinishedTranslator targetFinishedTranslator = null) { + _targetFinishedTranslator = targetFinishedTranslator; Translate(translator); } From c64a832987ff0220eaa9561bff348a5ee855e63f Mon Sep 17 00:00:00 2001 From: Roman Konecny Date: Tue, 20 Jun 2023 10:53:42 +0200 Subject: [PATCH 04/29] Issue warning when EventArgs is serialized by BinaryFormatter --- src/Build/BackEnd/Node/OutOfProcNode.cs | 14 ++++++++++++++ src/Deprecated/Engine/Resources/Strings.resx | 3 +++ .../Engine/Resources/xlf/Strings.cs.xlf | 5 +++++ .../Engine/Resources/xlf/Strings.de.xlf | 5 +++++ .../Engine/Resources/xlf/Strings.es.xlf | 5 +++++ .../Engine/Resources/xlf/Strings.fr.xlf | 5 +++++ .../Engine/Resources/xlf/Strings.it.xlf | 5 +++++ .../Engine/Resources/xlf/Strings.ja.xlf | 5 +++++ .../Engine/Resources/xlf/Strings.ko.xlf | 5 +++++ .../Engine/Resources/xlf/Strings.pl.xlf | 5 +++++ .../Engine/Resources/xlf/Strings.pt-BR.xlf | 5 +++++ .../Engine/Resources/xlf/Strings.ru.xlf | 5 +++++ .../Engine/Resources/xlf/Strings.tr.xlf | 5 +++++ .../Engine/Resources/xlf/Strings.zh-Hans.xlf | 5 +++++ .../Engine/Resources/xlf/Strings.zh-Hant.xlf | 5 +++++ src/MSBuild/OutOfProcTaskHostNode.cs | 16 +++++++++++++++- src/Shared/Resources/Strings.shared.resx | 3 +++ src/Shared/Resources/xlf/Strings.shared.cs.xlf | 5 +++++ src/Shared/Resources/xlf/Strings.shared.de.xlf | 5 +++++ src/Shared/Resources/xlf/Strings.shared.es.xlf | 5 +++++ src/Shared/Resources/xlf/Strings.shared.fr.xlf | 5 +++++ src/Shared/Resources/xlf/Strings.shared.it.xlf | 5 +++++ src/Shared/Resources/xlf/Strings.shared.ja.xlf | 5 +++++ src/Shared/Resources/xlf/Strings.shared.ko.xlf | 5 +++++ src/Shared/Resources/xlf/Strings.shared.pl.xlf | 5 +++++ .../Resources/xlf/Strings.shared.pt-BR.xlf | 5 +++++ src/Shared/Resources/xlf/Strings.shared.ru.xlf | 5 +++++ src/Shared/Resources/xlf/Strings.shared.tr.xlf | 5 +++++ .../Resources/xlf/Strings.shared.zh-Hans.xlf | 5 +++++ .../Resources/xlf/Strings.shared.zh-Hant.xlf | 5 +++++ 30 files changed, 165 insertions(+), 1 deletion(-) diff --git a/src/Build/BackEnd/Node/OutOfProcNode.cs b/src/Build/BackEnd/Node/OutOfProcNode.cs index c0c3531ff34..38494bcc8ac 100644 --- a/src/Build/BackEnd/Node/OutOfProcNode.cs +++ b/src/Build/BackEnd/Node/OutOfProcNode.cs @@ -572,6 +572,20 @@ private void SendPacket(INodePacket packet) if (_nodeEndpoint.LinkStatus == LinkStatus.Active) { _nodeEndpoint.SendData(packet); + if (packet is LogMessagePacketBase logMessage) + { + if (logMessage.EventType == LoggingEventType.CustomEvent) + { + BuildEventArgs buildEvent = logMessage.NodeBuildEvent.Value.Value; + + // Serializing unknown CustomEvent which has to use unsecure BinaryFormatter by TranslateDotNet + // Since BinaryFormatter is going to be deprecated, log warning so users can use new Extended*EventArgs instead of custom + // EventArgs derived from existing EventArgs. + _loggingService.LogWarning(buildEvent?.BuildEventContext ?? BuildEventContext.Invalid, null, BuildEventFileInfo.Empty, + "DeprecatedEventSerialization", + buildEvent?.GetType().Name ?? string.Empty); + } + } } } diff --git a/src/Deprecated/Engine/Resources/Strings.resx b/src/Deprecated/Engine/Resources/Strings.resx index a3e9cc5d3ba..f8213271154 100644 --- a/src/Deprecated/Engine/Resources/Strings.resx +++ b/src/Deprecated/Engine/Resources/Strings.resx @@ -1181,6 +1181,9 @@ [default] + + Usage of unsecure BinaryFormatter during serialization of custom event type '{0}'. This will be deprecated soon. Please use Extended*EventArgs instead. More info: https://aka.ms/msbuild/metadata-self-ref +