/// <summary> /// Write a provided instance of BuildEventArgs to the BinaryWriter /// </summary> public void Write(BuildEventArgs e) { var type = e.GetType().Name; // the cases are ordered by most used first for performance if (e is BuildMessageEventArgs && type != "ProjectImportedEventArgs" && type != "TargetSkippedEventArgs") { Write((BuildMessageEventArgs)e); } else if (e is TaskStartedEventArgs) { Write((TaskStartedEventArgs)e); } else if (e is TaskFinishedEventArgs) { Write((TaskFinishedEventArgs)e); } else if (e is TargetStartedEventArgs) { Write((TargetStartedEventArgs)e); } else if (e is TargetFinishedEventArgs) { Write((TargetFinishedEventArgs)e); } else if (e is BuildErrorEventArgs) { Write((BuildErrorEventArgs)e); } else if (e is BuildWarningEventArgs) { Write((BuildWarningEventArgs)e); } else if (e is ProjectStartedEventArgs) { Write((ProjectStartedEventArgs)e); } else if (e is ProjectFinishedEventArgs) { Write((ProjectFinishedEventArgs)e); } else if (e is BuildStartedEventArgs) { Write((BuildStartedEventArgs)e); } else if (e is BuildFinishedEventArgs) { Write((BuildFinishedEventArgs)e); } else if (e is ProjectEvaluationStartedEventArgs) { Write((ProjectEvaluationStartedEventArgs)e); } else if (e is ProjectEvaluationFinishedEventArgs) { Write((ProjectEvaluationFinishedEventArgs)e); } // The following cases are due to the fact that StructuredLogger.dll // only references MSBuild 14.0 .dlls. The following BuildEventArgs types // were only introduced in MSBuild 15.3 so we can't refer to them statically. // To still provide a good experience to those who are using the BinaryLogger // from StructuredLogger.dll against MSBuild 15.3 or later we need to preserve // these new events, so use reflection to create our "equivalents" of those // and populate them to be binary identical to the originals. Then serialize // our copies so that it's impossible to tell what wrote these. else if (type == "ProjectEvaluationStartedEventArgs") { var evaluationStarted = new ProjectEvaluationStartedEventArgs(e.Message); evaluationStarted.BuildEventContext = e.BuildEventContext; evaluationStarted.ProjectFile = Reflector.GetProjectFileFromEvaluationStarted(e); Write(evaluationStarted); } else if (type == "ProjectEvaluationFinishedEventArgs") { var evaluationFinished = new ProjectEvaluationFinishedEventArgs(e.Message); evaluationFinished.BuildEventContext = e.BuildEventContext; evaluationFinished.ProjectFile = Reflector.GetProjectFileFromEvaluationFinished(e); Write(evaluationFinished); } else if (type == "ProjectImportedEventArgs") { var message = e as BuildMessageEventArgs; var projectImported = new ProjectImportedEventArgs(message.LineNumber, message.ColumnNumber, e.Message); projectImported.BuildEventContext = e.BuildEventContext; projectImported.ProjectFile = message.ProjectFile; projectImported.ImportedProjectFile = Reflector.GetImportedProjectFile(e); projectImported.UnexpandedProject = Reflector.GetUnexpandedProject(e); Write(projectImported); } else if (type == "TargetSkippedEventArgs") { var message = e as BuildMessageEventArgs; var targetSkipped = new TargetSkippedEventArgs(e.Message); targetSkipped.BuildEventContext = e.BuildEventContext; targetSkipped.ProjectFile = message.ProjectFile; targetSkipped.TargetName = Reflector.GetTargetNameFromTargetSkipped(e); targetSkipped.TargetFile = Reflector.GetTargetFileFromTargetSkipped(e); targetSkipped.ParentTarget = Reflector.GetParentTargetFromTargetSkipped(e); targetSkipped.BuildReason = Reflector.GetBuildReasonFromTargetSkipped(e); Write(targetSkipped); } else { // convert all unrecognized objects to message // and just preserve the message var buildMessageEventArgs = new BuildMessageEventArgs( e.Message, e.HelpKeyword, e.SenderName, MessageImportance.Normal, e.Timestamp); buildMessageEventArgs.BuildEventContext = e.BuildEventContext ?? BuildEventContext.Invalid; Write(buildMessageEventArgs); } }
/// <summary> /// Handles a generic BuildMessage event and assigns it to the appropriate logging node. /// </summary> /// <param name="args">The <see cref="BuildMessageEventArgs"/> instance containing the event data.</param> public void AddMessage(LazyFormattedBuildEventArgs args, string message) { message = stringTable.Intern(message); TreeNode node = null; var messageNode = new Message { Text = message, Timestamp = args.Timestamp }; object nodeToAdd = messageNode; if (args.BuildEventContext?.TaskId > 0) { node = construction .GetOrAddProject(args.BuildEventContext.ProjectContextId) .GetTargetById(args.BuildEventContext.TargetId) .GetTaskById(args.BuildEventContext.TaskId); var task = node as Task; if (task != null) { if (task.Name == "ResolveAssemblyReference") { Folder inputs = task.GetOrCreateNodeWithName <Folder>("Inputs"); Folder results = task.FindChild <Folder>("Results"); node = results ?? inputs; if (message.StartsWith(" ")) { message = message.Substring(4); var parameter = node.FindLastChild <Parameter>(); if (parameter != null) { bool thereWasAConflict = parameter.ToString().StartsWith(Strings.ThereWasAConflictPrefix); if (thereWasAConflict) { HandleThereWasAConflict(parameter, message, stringTable); return; } if (!string.IsNullOrWhiteSpace(message)) { node = parameter; if (message.StartsWith(" ")) { message = message.Substring(4); var lastItem = parameter.FindLastChild <Item>(); // only indent if it's not a "For SearchPath..." message - that one needs to be directly under parameter // also don't indent if it's under AssemblyFoldersEx in Results if (lastItem != null && !message.StartsWith(Strings.ForSearchPathPrefix) && !parameter.Name.StartsWith("AssemblyFoldersEx")) { node = lastItem; } } if (!string.IsNullOrEmpty(message)) { var equals = message.IndexOf('='); if (equals != -1) { var kvp = TextUtilities.ParseNameValue(message); node.AddChild(new Metadata { Name = stringTable.Intern(kvp.Key.TrimEnd(space)), Value = stringTable.Intern(kvp.Value.TrimStart(space)) }); } else { node.AddChild(new Item() { Text = stringTable.Intern(message) }); } } } return; } } else { if (results == null) { bool isResult = message.StartsWith(Strings.UnifiedPrimaryReferencePrefix) || message.StartsWith(Strings.PrimaryReferencePrefix) || message.StartsWith(Strings.DependencyPrefix) || message.StartsWith(Strings.UnifiedDependencyPrefix) || message.StartsWith(Strings.AssemblyFoldersExLocation) || message.StartsWith(Strings.ThereWasAConflictPrefix); if (isResult) { results = task.GetOrCreateNodeWithName <Folder>("Results"); node = results; } else { node = inputs; } } else { node = results; } node.GetOrCreateNodeWithName <Parameter>(stringTable.Intern(message.TrimEnd(':'))); return; } } else if (task.Name == "MSBuild") { if (message.StartsWith(Strings.GlobalPropertiesPrefix) || message.StartsWith(Strings.AdditionalPropertiesPrefix) || message.StartsWith(Strings.OverridingGlobalPropertiesPrefix) || message.StartsWith(Strings.RemovingPropertiesPrefix)) { node.GetOrCreateNodeWithName <Folder>(message); return; } node = node.FindLastChild <Folder>(); if (message[0] == ' ' && message[1] == ' ') { message = message.Substring(2); } var kvp = TextUtilities.ParseNameValue(message); if (kvp.Value == "") { nodeToAdd = new Item { Text = stringTable.Intern(kvp.Key) }; } else { nodeToAdd = new Property { Name = stringTable.Intern(kvp.Key), Value = stringTable.Intern(kvp.Value) }; } } } } else if (args.BuildEventContext?.TargetId > 0) { node = construction .GetOrAddProject(args.BuildEventContext.ProjectContextId) .GetTargetById(args.BuildEventContext.TargetId); if (Strings.IsTaskSkipped(message)) { messageNode.IsLowRelevance = true; } } else if (args.BuildEventContext?.ProjectContextId > 0) { var project = construction.GetOrAddProject(args.BuildEventContext.ProjectContextId); node = project; if (Strings.IsTargetSkipped(message)) { var targetName = stringTable.Intern(TextUtilities.ParseQuotedSubstring(message)); if (targetName != null) { node = project.GetOrAddTargetByName(targetName); messageNode.IsLowRelevance = true; } } } else if (Reflector.GetEvaluationId(args.BuildEventContext) is int evaluationId && evaluationId != -1) { var evaluation = construction.EvaluationFolder; var project = evaluation.FindChild <Project>(p => p.Id == evaluationId); node = project; if (node != null && node.FindChild <Message>(message) != null) { // avoid duplicate messages return; } } if (node == null) { node = construction.Build; if (Strings.IsEvaluationMessage(message)) { if (!evaluationMessagesAlreadySeen.Add(message)) { return; } node = construction.EvaluationFolder; } else if (Strings.IsTargetDoesNotExistAndWillBeSkipped(message)) { var folder = construction.EvaluationFolder; node = folder; messageNode.IsLowRelevance = true; } else if (args.BuildEventContext != null && (args.BuildEventContext.NodeId == 0 && args.BuildEventContext.ProjectContextId == 0 && args.BuildEventContext.ProjectInstanceId == 0 && args.BuildEventContext.TargetId == 0 && args.BuildEventContext.TaskId == 0)) { // must be Detailed Build Summary // https://github.com/Microsoft/msbuild/blob/master/src/XMakeBuildEngine/BackEnd/Components/Scheduler/Scheduler.cs#L509 DetailedSummary.AppendLine(message); return; } } node.AddChild(nodeToAdd); }