Skip to content

Commit

Permalink
Support parsing the new RAR FoundConflict multiline messages
Browse files Browse the repository at this point in the history
dotnet/msbuild#5798 introduced a new format of logging Results that logged details for "There was a conflict" message in a single multi-line message instead of multiple single-line messages.

Add a mechanism to read the MSBuild version from the binlog and use the new parsing logic if the version is at least 16.9.
  • Loading branch information
KirillOsenkov committed Apr 28, 2021
1 parent 8dde77e commit d1a8bfc
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 17 deletions.
24 changes: 24 additions & 0 deletions src/StructuredLogger.Tests/ItemGroupParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,30 @@ public void ParseThereWasAConflict()
", text);
}

[Fact]
public void ParseThereWasAConflictMultiline()
{
var message = @" References which depend on ""System.IO.Compression.FileSystem, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"" [C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1\System.IO.Compression.FileSystem.dll].
C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1\System.IO.Compression.FileSystem.dll
Project file item includes which caused reference ""C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1\System.IO.Compression.FileSystem.dll"".
C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1\System.IO.Compression.FileSystem.dll
References which depend on ""System.IO.Compression.FileSystem"" [].
Unresolved primary reference with an item include of ""System.IO.Compression.FileSystem"".".NormalizeLineBreaks();
var stringCache = new StringCache();
var parameter = new Parameter();
ItemGroupParser.ParseThereWasAConflict(parameter, message, stringCache);
var text = StringWriter.GetString(parameter).NormalizeLineBreaks();
var expected = @"
References which depend on ""System.IO.Compression.FileSystem, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"" [C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1\System.IO.Compression.FileSystem.dll].
C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1\System.IO.Compression.FileSystem.dll
Project file item includes which caused reference ""C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1\System.IO.Compression.FileSystem.dll"".
C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1\System.IO.Compression.FileSystem.dll
References which depend on ""System.IO.Compression.FileSystem"" [].
Unresolved primary reference with an item include of ""System.IO.Compression.FileSystem"".
".NormalizeLineBreaks();
Assert.Equal(expected, text);
}

[Fact]
public void ParseMultilineMetadata()
{
Expand Down
59 changes: 59 additions & 0 deletions src/StructuredLogger/Construction/ItemGroupParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,5 +183,64 @@ public static BaseNode ParsePropertyOrItemList(string message, string prefix, St

return parameter;
}

public static void ParseThereWasAConflict(TreeNode parent, string message, StringCache stringTable)
{
if (lineSpans == null)
{
lineSpans = new List<Span>(10240);
}

lineSpans.Clear();
message.CollectLineSpans(lineSpans, includeLineBreakInSpan: false);

Item item4 = null;
Item item8 = null;
Item item10 = null;

for (int i = 0; i < lineSpans.Count; i++)
{
var lineSpan = lineSpans[i];
var numberOfLeadingSpaces = TextUtilities.GetNumberOfLeadingSpaces(message, lineSpan);
switch (numberOfLeadingSpaces)
{
case 0:
case 4:
item4 = Add(parent, message, lineSpan, numberOfLeadingSpaces, stringTable);
item8 = null;
item10 = null;
break;
case 8:
item8 = Add(item4, message, lineSpan, numberOfLeadingSpaces, stringTable);
item10 = null;
break;
case 10:
item10 = Add(item8, message, lineSpan, numberOfLeadingSpaces, stringTable);
break;
case 12:
Add(item10, message, lineSpan, numberOfLeadingSpaces, stringTable);
break;
default:
break;
}
}

static Item Add(TreeNode parent, string text, Span span, int spaces, StringCache stringTable)
{
if (spaces >= span.Length || parent == null)
{
return null;
}

string line = text.Substring(span.Start + spaces, span.Length - spaces);

var item = new Item
{
Text = stringTable.Intern(line)
};
parent.AddChild(item);
return item;
}
}
}
}
32 changes: 29 additions & 3 deletions src/StructuredLogger/Construction/MessageProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,17 @@ public void AddMessage(LazyFormattedBuildEventArgs args, string message)
DetailedSummary.AppendLine(message);
return;
}
else if (
buildEventContext.NodeId == -2 &&
buildEventContext.ProjectContextId == -2 &&
buildEventContext.ProjectInstanceId == -1)
{
if (message.StartsWith(Strings.MSBuildVersionPrefix))
{
message = message.Substring(Strings.MSBuildVersionPrefix.Length);
construction.Build.MSBuildVersion = message;
}
}
}

node.AddChild(nodeToAdd);
Expand All @@ -527,10 +538,19 @@ private bool ProcessRAR(Task task, ref TreeNode node, string message)
var parameter = node?.FindLastChild<Parameter>();
if (parameter != null)
{
bool thereWasAConflict = Strings.IsThereWasAConflictPrefix(parameter.ToString()); //parameter.ToString().StartsWith(Strings.ThereWasAConflictPrefix);
bool thereWasAConflict = Strings.IsThereWasAConflictPrefix(parameter.Name);
if (thereWasAConflict)
{
HandleThereWasAConflict(parameter, message, stringTable);
if (construction.Build.IsMSBuildVersionAtLeast(16, 9))
{
// https://github.com/KirillOsenkov/MSBuildStructuredLog/issues/443
ItemGroupParser.ParseThereWasAConflict(parameter, message, stringTable);
}
else
{
HandleThereWasAConflict(parameter, message, stringTable);
}

return true;
}

Expand Down Expand Up @@ -610,7 +630,13 @@ private bool ProcessRAR(Task task, ref TreeNode node, string message)
node = results;
}

node.GetOrCreateNodeWithName<Parameter>(Intern(message.TrimEnd(':')));
var parameterName = Intern(message.TrimEnd(':'));
var parameter = new Parameter
{
Name = parameterName
};

node.AddChild(parameter);
return true;
}

Expand Down
48 changes: 48 additions & 0 deletions src/StructuredLogger/ObjectModel/Build.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,54 @@ public class Build : TimedNode
public int FileFormatVersion { get; set; }
public byte[] SourceFilesArchive { get; set; }

private string msbuildVersion;
public string MSBuildVersion
{
get => msbuildVersion;
set
{
msbuildVersion = value;
version = null;
ParseMSBuildVersion();
}
}

private Version version;

private void ParseMSBuildVersion()
{
if (msbuildVersion == null)
{
return;
}

msbuildVersion = msbuildVersion.TrimQuotes();

var parts = msbuildVersion.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length < 2)
{
return;
}

if (!int.TryParse(parts[0], out var major) || !int.TryParse(parts[1], out var minor))
{
return;
}

version = new Version(major, minor);
}

public bool IsMSBuildVersionAtLeast(int major, int minor)
{
if (version == null)
{
return false;
}

// avoid allocating a Version instance
return version.Major > major || (version.Major == major && version.Minor >= minor);
}

private Dictionary<string, ArchiveFile> sourceFiles;
public Dictionary<string, ArchiveFile> SourceFiles
{
Expand Down
22 changes: 8 additions & 14 deletions src/StructuredLogger/Strings/Strings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,11 @@ private static void InitializeRegex()
.Replace("{3}", ".*?")
);

ConflictFoundRegex = new Regex(GetString("ResolveAssemblyReference.ConflictFound")
.Replace("{0}", ".*")
.Replace("{1}", ".*")
+ ".*");

TaskParameterMessagePrefix = GetString("TaskParameterPrefix");
OutputItemsMessagePrefix = GetString("OutputItemParameterMessagePrefix");
ItemGroupIncludeMessagePrefix = GetString("ItemGroupIncludeLogMessagePrefix");
Expand Down Expand Up @@ -255,6 +260,7 @@ private static void InitializeRegex()
public static Regex AssemblyFoldersExLocation { get; set; }
public static Regex ConflictReferenceSameSDK { get; set; }
public static Regex ConflictRedistDifferentSDK { get; set; }
public static Regex ConflictFoundRegex { get; set; }
public static Regex ConflictReferenceDifferentSDK { get; set; }
public static Regex AdditionalPropertiesPrefix { get; set; }
public static Regex OverridingGlobalPropertiesPrefix { get; set; }
Expand Down Expand Up @@ -385,25 +391,12 @@ public static Match ProjectWasNotImportedRegex(string message, out string reason

public static bool IsThereWasAConflictPrefix(string message)
{
if (ConflictReferenceSameSDK.IsMatch(message))
{
return true;
}

if (ConflictRedistDifferentSDK.IsMatch(message))
{
return true;
}

if (ConflictReferenceDifferentSDK.IsMatch(message))
if (ConflictFoundRegex.IsMatch(message))
{
return true;
}

return false;
//GetSDKReferenceFiles.ConflictReferenceSameSDK $:$ There was a conflict between two references with the same file name resolved within the "{0}" SDK. Choosing "{1}" over "{2}" because it was resolved first.
//GetSDKReferenceFiles.ConflictRedistDifferentSDK $:$ There was a conflict between two files from the redist folder files going to the same target path "{0}" between the "{1}" and "{2}" SDKs. Choosing "{3}" over "{4}" because it was resolved first.
//GetSDKReferenceFiles.ConflictReferenceDifferentSDK $:$ There was a conflict between two references with the same file name between the "{0}" and "{1}" SDKs. Choosing "{2}" over "{3}" because it was resolved first.
}

public static string PropertyGroupMessagePrefix { get; set; }
Expand Down Expand Up @@ -472,6 +465,7 @@ public static bool IsThereWasAConflictPrefix(string message)
public static string Duration => "Duration";
public static string Note => "Note";
public static string DoubleWrites => "DoubleWrites";
public static string MSBuildVersionPrefix => "MSBuild version = ";

public static string GetPropertyName(string message)
{
Expand Down
Loading

0 comments on commit d1a8bfc

Please sign in to comment.