src/Build/Logging/BinaryLogger/BuildEventArgsWriter.cs (481 lines of code) (raw):
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Build.Framework;
namespace Microsoft.Build.Logging
{
/// <summary>
/// Serializes BuildEventArgs-derived objects into a provided BinaryWriter
/// </summary>
internal class BuildEventArgsWriter
{
private readonly BinaryWriter binaryWriter;
/// <summary>
/// Initializes a new instance of BuildEventArgsWriter with a BinaryWriter
/// </summary>
/// <param name="binaryWriter">A BinaryWriter to write the BuildEventArgs instances to</param>
public BuildEventArgsWriter(BinaryWriter binaryWriter)
{
this.binaryWriter = binaryWriter;
}
/// <summary>
/// Write a provided instance of BuildEventArgs to the BinaryWriter
/// </summary>
public void Write(BuildEventArgs e)
{
// the cases are ordered by most used first for performance
if (e is BuildMessageEventArgs)
{
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
{
// convert all unrecognized objects to message
// and just preserve the message
var buildMessageEventArgs = new BuildMessageEventArgs(
e.Message,
e.HelpKeyword,
e.SenderName,
MessageImportance.Normal);
Write(buildMessageEventArgs);
}
}
private void Write(BuildStartedEventArgs e)
{
Write(BinaryLogRecordKind.BuildStarted);
WriteBuildEventArgsFields(e);
Write(e.BuildEnvironment);
}
private void Write(BuildFinishedEventArgs e)
{
Write(BinaryLogRecordKind.BuildFinished);
WriteBuildEventArgsFields(e);
Write(e.Succeeded);
}
private void Write(ProjectStartedEventArgs e)
{
Write(BinaryLogRecordKind.ProjectStarted);
WriteBuildEventArgsFields(e);
if (e.ParentProjectBuildEventContext == null)
{
Write(false);
}
else
{
Write(true);
Write(e.ParentProjectBuildEventContext);
}
WriteOptionalString(e.ProjectFile);
Write(e.ProjectId);
Write(e.TargetNames);
WriteOptionalString(e.ToolsVersion);
WriteProperties(e.Properties);
WriteItems(e.Items);
}
private void Write(ProjectFinishedEventArgs e)
{
Write(BinaryLogRecordKind.ProjectFinished);
WriteBuildEventArgsFields(e);
WriteOptionalString(e.ProjectFile);
Write(e.Succeeded);
}
private void Write(TargetStartedEventArgs e)
{
Write(BinaryLogRecordKind.TargetStarted);
WriteBuildEventArgsFields(e);
WriteOptionalString(e.TargetName);
WriteOptionalString(e.ProjectFile);
WriteOptionalString(e.TargetFile);
WriteOptionalString(e.ParentTarget);
}
private void Write(TargetFinishedEventArgs e)
{
Write(BinaryLogRecordKind.TargetFinished);
WriteBuildEventArgsFields(e);
Write(e.Succeeded);
WriteOptionalString(e.ProjectFile);
WriteOptionalString(e.TargetFile);
WriteOptionalString(e.TargetName);
WriteItemList(e.TargetOutputs);
}
private void Write(TaskStartedEventArgs e)
{
Write(BinaryLogRecordKind.TaskStarted);
WriteBuildEventArgsFields(e);
WriteOptionalString(e.TaskName);
WriteOptionalString(e.ProjectFile);
WriteOptionalString(e.TaskFile);
}
private void Write(TaskFinishedEventArgs e)
{
Write(BinaryLogRecordKind.TaskFinished);
WriteBuildEventArgsFields(e);
Write(e.Succeeded);
WriteOptionalString(e.TaskName);
WriteOptionalString(e.ProjectFile);
WriteOptionalString(e.TaskFile);
}
private void Write(BuildErrorEventArgs e)
{
Write(BinaryLogRecordKind.Error);
WriteBuildEventArgsFields(e);
WriteOptionalString(e.Subcategory);
WriteOptionalString(e.Code);
WriteOptionalString(e.File);
WriteOptionalString(e.ProjectFile);
Write(e.LineNumber);
Write(e.ColumnNumber);
Write(e.EndLineNumber);
Write(e.EndColumnNumber);
}
private void Write(BuildWarningEventArgs e)
{
Write(BinaryLogRecordKind.Warning);
WriteBuildEventArgsFields(e);
WriteOptionalString(e.Subcategory);
WriteOptionalString(e.Code);
WriteOptionalString(e.File);
WriteOptionalString(e.ProjectFile);
Write(e.LineNumber);
Write(e.ColumnNumber);
Write(e.EndLineNumber);
Write(e.EndColumnNumber);
}
private void Write(BuildMessageEventArgs e)
{
if (e is CriticalBuildMessageEventArgs)
{
Write((CriticalBuildMessageEventArgs)e);
return;
}
else if (e is TaskCommandLineEventArgs)
{
Write((TaskCommandLineEventArgs)e);
return;
}
Write(BinaryLogRecordKind.Message);
WriteMessageFields(e);
}
private void Write(CriticalBuildMessageEventArgs e)
{
Write(BinaryLogRecordKind.CriticalBuildMessage);
WriteMessageFields(e);
}
private void Write(TaskCommandLineEventArgs e)
{
Write(BinaryLogRecordKind.TaskCommandLine);
WriteMessageFields(e);
WriteOptionalString(e.CommandLine);
WriteOptionalString(e.TaskName);
}
private void WriteBuildEventArgsFields(BuildEventArgs e)
{
var flags = GetBuildEventArgsFieldFlags(e);
Write((int)flags);
WriteBaseFields(e, flags);
}
private void WriteBaseFields(BuildEventArgs e, BuildEventArgsFieldFlags flags)
{
if ((flags & BuildEventArgsFieldFlags.Message) != 0)
{
Write(e.Message);
}
if ((flags & BuildEventArgsFieldFlags.BuildEventContext) != 0)
{
Write(e.BuildEventContext);
}
if ((flags & BuildEventArgsFieldFlags.ThreadId) != 0)
{
Write(e.ThreadId);
}
if ((flags & BuildEventArgsFieldFlags.HelpHeyword) != 0)
{
Write(e.HelpKeyword);
}
if ((flags & BuildEventArgsFieldFlags.SenderName) != 0)
{
Write(e.SenderName);
}
if ((flags & BuildEventArgsFieldFlags.Timestamp) != 0)
{
Write(e.Timestamp);
}
}
private void WriteMessageFields(BuildMessageEventArgs e)
{
var flags = GetBuildEventArgsFieldFlags(e);
flags = GetMessageFlags(e, flags);
Write((int)flags);
WriteBaseFields(e, flags);
if ((flags & BuildEventArgsFieldFlags.Subcategory) != 0)
{
Write(e.Subcategory);
}
if ((flags & BuildEventArgsFieldFlags.Code) != 0)
{
Write(e.Code);
}
if ((flags & BuildEventArgsFieldFlags.File) != 0)
{
Write(e.File);
}
if ((flags & BuildEventArgsFieldFlags.ProjectFile) != 0)
{
Write(e.ProjectFile);
}
if ((flags & BuildEventArgsFieldFlags.LineNumber) != 0)
{
Write(e.LineNumber);
}
if ((flags & BuildEventArgsFieldFlags.ColumnNumber) != 0)
{
Write(e.ColumnNumber);
}
if ((flags & BuildEventArgsFieldFlags.EndLineNumber) != 0)
{
Write(e.EndLineNumber);
}
if ((flags & BuildEventArgsFieldFlags.EndColumnNumber) != 0)
{
Write(e.EndColumnNumber);
}
Write((int)e.Importance);
}
private static BuildEventArgsFieldFlags GetMessageFlags(BuildMessageEventArgs e, BuildEventArgsFieldFlags flags)
{
if (e.Subcategory != null)
{
flags |= BuildEventArgsFieldFlags.Subcategory;
}
if (e.Code != null)
{
flags |= BuildEventArgsFieldFlags.Code;
}
if (e.File != null)
{
flags |= BuildEventArgsFieldFlags.File;
}
if (e.ProjectFile != null)
{
flags |= BuildEventArgsFieldFlags.ProjectFile;
}
if (e.LineNumber != 0)
{
flags |= BuildEventArgsFieldFlags.LineNumber;
}
if (e.ColumnNumber != 0)
{
flags |= BuildEventArgsFieldFlags.ColumnNumber;
}
if (e.EndLineNumber != 0)
{
flags |= BuildEventArgsFieldFlags.EndLineNumber;
}
if (e.EndColumnNumber != 0)
{
flags |= BuildEventArgsFieldFlags.EndColumnNumber;
}
return flags;
}
private static BuildEventArgsFieldFlags GetBuildEventArgsFieldFlags(BuildEventArgs e)
{
var flags = BuildEventArgsFieldFlags.None;
if (e.BuildEventContext != null)
{
flags |= BuildEventArgsFieldFlags.BuildEventContext;
}
if (e.HelpKeyword != null)
{
flags |= BuildEventArgsFieldFlags.HelpHeyword;
}
if (!string.IsNullOrEmpty(e.Message))
{
flags |= BuildEventArgsFieldFlags.Message;
}
// no need to waste space for the default sender name
if (e.SenderName != null && e.SenderName != "MSBuild")
{
flags |= BuildEventArgsFieldFlags.SenderName;
}
if (e.ThreadId > 0)
{
flags |= BuildEventArgsFieldFlags.ThreadId;
}
if (e.Timestamp != default(DateTime))
{
flags |= BuildEventArgsFieldFlags.Timestamp;
}
return flags;
}
private void WriteItemList(IEnumerable items)
{
var taskItems = items as IEnumerable<ITaskItem>;
if (taskItems != null)
{
Write(taskItems.Count());
foreach (var item in taskItems)
{
Write(item);
}
return;
}
Write(0);
}
private void WriteItems(IEnumerable items)
{
if (items == null)
{
Write(0);
return;
}
var entries = items.OfType<DictionaryEntry>()
.Where(e => e.Key is string && e.Value is ITaskItem)
.ToArray();
Write(entries.Length);
foreach (DictionaryEntry entry in entries)
{
string key = entry.Key as string;
ITaskItem item = entry.Value as ITaskItem;
Write(key);
Write(item);
}
}
private void Write(ITaskItem item)
{
Write(item.ItemSpec);
IDictionary customMetadata = item.CloneCustomMetadata();
Write(customMetadata.Count);
foreach (string metadataName in customMetadata.Keys)
{
Write(metadataName);
Write(item.GetMetadata(metadataName));
}
}
private void WriteProperties(IEnumerable properties)
{
if (properties == null)
{
Write(0);
return;
}
// there are no guarantees that the properties iterator won't change, so
// take a snapshot and work with the readonly copy
var propertiesArray = properties.OfType<DictionaryEntry>().ToArray();
Write(propertiesArray.Length);
foreach (DictionaryEntry entry in propertiesArray)
{
if (entry.Key is string && entry.Value is string)
{
Write((string)entry.Key);
Write((string)entry.Value);
}
else
{
// to keep the count accurate
Write("");
Write("");
}
}
}
private void Write(BuildEventContext buildEventContext)
{
Write(buildEventContext.NodeId);
Write(buildEventContext.ProjectContextId);
Write(buildEventContext.TargetId);
Write(buildEventContext.TaskId);
Write(buildEventContext.SubmissionId);
Write(buildEventContext.ProjectInstanceId);
}
private void Write<TKey, TValue>(IEnumerable<KeyValuePair<TKey, TValue>> keyValuePairs)
{
if (keyValuePairs != null && keyValuePairs.Any())
{
Write(keyValuePairs.Count());
foreach (var kvp in keyValuePairs)
{
Write(kvp.Key.ToString());
Write(kvp.Value.ToString());
}
}
else
{
Write(false);
}
}
private void Write(BinaryLogRecordKind kind)
{
Write((int)kind);
}
private void Write(int value)
{
Write7BitEncodedInt(binaryWriter, value);
}
private void Write7BitEncodedInt(BinaryWriter writer, int value)
{
// Write out an int 7 bits at a time. The high bit of the byte,
// when on, tells reader to continue reading more bytes.
uint v = (uint)value; // support negative numbers
while (v >= 0x80)
{
writer.Write((byte)(v | 0x80));
v >>= 7;
}
writer.Write((byte)v);
}
private void Write(bool boolean)
{
binaryWriter.Write(boolean);
}
private void Write(string text)
{
if (text != null)
{
binaryWriter.Write(text);
}
else
{
binaryWriter.Write(false);
}
}
private void WriteOptionalString(string text)
{
if (text == null)
{
Write(false);
}
else
{
Write(true);
Write(text);
}
}
private void Write(DateTime timestamp)
{
binaryWriter.Write(timestamp.Ticks);
Write((int)timestamp.Kind);
}
}
}