diff --git a/src/Basic.CompilerLog.UnitTests/BinaryLogUtilTests.cs b/src/Basic.CompilerLog.UnitTests/BinaryLogUtilTests.cs index 74e1e18..522be22 100644 --- a/src/Basic.CompilerLog.UnitTests/BinaryLogUtilTests.cs +++ b/src/Basic.CompilerLog.UnitTests/BinaryLogUtilTests.cs @@ -96,34 +96,42 @@ public sealed class MSBuildProjectDataTests [Fact] public void MSBuildProjectDataToString() { - var data = new BinaryLogUtil.MSBuildProjectData(@"example.csproj"); + var evalData = new BinaryLogUtil.MSBuildProjectEvaluationData(@"example.csproj"); + var data = new BinaryLogUtil.MSBuildProjectContextData(@"example.csproj", 100, 1); Assert.NotEmpty(data.ToString()); } } public sealed class CompilationTaskDataTests { - internal BinaryLogUtil.MSBuildProjectData ProjectData { get; } = new BinaryLogUtil.MSBuildProjectData(@"example.csproj"); + internal BinaryLogUtil.MSBuildProjectEvaluationData EvaluationData { get; } + internal BinaryLogUtil.MSBuildProjectContextData ContextData { get; } + + public CompilationTaskDataTests() + { + EvaluationData = new BinaryLogUtil.MSBuildProjectEvaluationData(@"example.csproj"); + ContextData = new(@"example.csproj", 100, 1); + } [Fact] public void TryCreateCompilerCallBadArguments() { - var data = new BinaryLogUtil.CompilationTaskData(ProjectData, 1) + var data = new BinaryLogUtil.CompilationTaskData(1, 1, true) { CommandLineArguments = "dotnet not a compiler call", }; - Assert.Throws(() => data.TryCreateCompilerCall(ownerState: null)); + Assert.Throws(() => data.TryCreateCompilerCall(ContextData.ProjectFile, null, CompilerCallKind.Unknown, ownerState: null)); } [Fact] public void TryCreateCompilerNoArguments() { - var data = new BinaryLogUtil.CompilationTaskData(ProjectData, 1) + var data = new BinaryLogUtil.CompilationTaskData(1, 1, true) { CommandLineArguments = null, }; - Assert.Null(data.TryCreateCompilerCall(null)); + Assert.Null(data.TryCreateCompilerCall(ContextData.ProjectFile, null, CompilerCallKind.Unknown, null)); } } \ No newline at end of file diff --git a/src/Basic.CompilerLog.Util/BinaryLogUtil.cs b/src/Basic.CompilerLog.Util/BinaryLogUtil.cs index 5694df4..c96a3ff 100644 --- a/src/Basic.CompilerLog.Util/BinaryLogUtil.cs +++ b/src/Basic.CompilerLog.Util/BinaryLogUtil.cs @@ -32,39 +32,45 @@ public static class BinaryLogUtil // Oddities observed // - There are project start / stop events that have no evaluation id - internal sealed class MSBuildProjectData + internal sealed class MSBuildProjectContextData(string projectFile, int contextId, int evaluationId) { - private readonly Dictionary _targetMap = new(); - public readonly string ProjectFile; + private readonly Dictionary _taskMap = new(capacity: 4); + private readonly Dictionary _targetMap = new(capacity: 4); + public readonly int ContextId = contextId; + public int EvaluationId = evaluationId; public string? TargetFramework; - public int? EvaluationId; + public readonly string ProjectFile = projectFile; - public MSBuildProjectData(string projectFile) + public bool TryGetTaskData(BuildEventContext context, [NotNullWhen(true)] out CompilationTaskData? data) => + _taskMap.TryGetValue(context.TaskId, out data); + + public void SetCompilerCallKind(int targetId, CompilerCallKind kind) { - ProjectFile = projectFile; + Debug.Assert(targetId != BuildEventContext.InvalidTargetId); + _targetMap[targetId] = kind; } - public bool TryGetTaskData(int targetId, [NotNullWhen(true)] out CompilationTaskData? data) => - _targetMap.TryGetValue(targetId, out data); - - public CompilationTaskData GetOrCreateTaskData(int targetId) + public CompilationTaskData CreateTaskData(BuildEventContext context, bool isCSharp) { - if (!_targetMap.TryGetValue(targetId, out var data)) - { - data = new CompilationTaskData(this, targetId); - _targetMap[targetId] = data; - } - + Debug.Assert(!_taskMap.ContainsKey(context.TaskId)); + var data = new CompilationTaskData(context.TargetId, context.TaskId, isCSharp); + _taskMap[context.TaskId] = data; return data; } - public List GetAllCompilerCalls(object? ownerState) + public List GetAllCompilerCalls(MSBuildProjectEvaluationData? evaluationData, object? ownerState) { + var targetFramework = TargetFramework ?? evaluationData?.TargetFramework; var list = new List(); - foreach (var data in _targetMap.Values) + foreach (var data in _taskMap.Values) { - if (data.TryCreateCompilerCall(ownerState) is { } compilerCall) + if (!_targetMap.TryGetValue(data.TargetId, out var compilerCallKind)) + { + compilerCallKind = CompilerCallKind.Unknown; + } + + if (data.TryCreateCompilerCall(ProjectFile, targetFramework, compilerCallKind, ownerState) is { } compilerCall) { if (compilerCall.Kind == CompilerCallKind.Regular) { @@ -83,27 +89,17 @@ public List GetAllCompilerCalls(object? ownerState) public override string ToString() => $"{Path.GetFileName(ProjectFile)}({TargetFramework})"; } - internal sealed class CompilationTaskData + internal sealed class CompilationTaskData(int targetId, int taskId, bool isCSharp) { - public readonly MSBuildProjectData ProjectData; - public int TargetId; + public readonly int TargetId = targetId; + public readonly int TaskId = taskId; + public readonly bool IsCSharp = isCSharp; public string? CommandLineArguments; - public CompilerCallKind? Kind; - public int? CompileTaskId; - public bool IsCSharp; - - public string ProjectFile => ProjectData.ProjectFile; - public string? TargetFramework => ProjectData.TargetFramework; - public CompilationTaskData(MSBuildProjectData projectData, int targetId) - { - ProjectData = projectData; - TargetId = targetId; - } - - public override string ToString() => $"{ProjectData} {TargetId}"; + [ExcludeFromCodeCoverage] + public override string ToString() => TaskId.ToString(); - internal CompilerCall? TryCreateCompilerCall(object? ownerState) + internal CompilerCall? TryCreateCompilerCall(string projectFile, string? targetFramework, CompilerCallKind kind, object? ownerState) { if (CommandLineArguments is null) { @@ -111,32 +107,26 @@ public CompilationTaskData(MSBuildProjectData projectData, int targetId) return null; } - var kind = Kind ?? CompilerCallKind.Unknown; var (compilerFilePath, args) = IsCSharp ? ParseTaskForCompilerAndArguments(CommandLineArguments, "csc.exe", "csc.dll") : ParseTaskForCompilerAndArguments(CommandLineArguments, "vbc.exe", "vbc.dll"); return new CompilerCall( compilerFilePath, - ProjectFile, + projectFile, kind, - TargetFramework, + targetFramework, isCSharp: IsCSharp, new Lazy>(() => args), ownerState: ownerState); } } - private sealed class MSBuildEvaluationData + internal sealed class MSBuildProjectEvaluationData(string projectFile) { - public string ProjectFile; + public string ProjectFile = projectFile; public string? TargetFramework; - public MSBuildEvaluationData(string projectFile) - { - ProjectFile = projectFile; - } - [ExcludeFromCodeCoverage] public override string ToString() => $"{Path.GetFileName(ProjectFile)}({TargetFramework})"; } @@ -150,8 +140,8 @@ public static List ReadAllCompilerCalls(Stream stream, Func(); var records = BinaryLog.ReadRecords(stream); - var contextMap = new Dictionary(); - var evaluationMap = new Dictionary(); + var contextMap = new Dictionary(); + var evaluationMap = new Dictionary(); foreach (var record in records) { @@ -164,24 +154,17 @@ public static List ReadAllCompilerCalls(Stream stream, Func CompilerCallKind.WpfTemporaryCompile, @@ -216,11 +202,9 @@ data.EvaluationId is { } evaluationId && _ => (CompilerCallKind?)null }; - if (callKind is { } ck && - context.TargetId != BuildEventContext.InvalidTargetId && - contextMap.TryGetValue(context.ProjectContextId, out var data)) + if (callKind is { } ck && contextMap.TryGetValue(context.ProjectContextId, out var contextData)) { - data.GetOrCreateTaskData(context.TargetId).Kind = ck; + contextData.SetCompilerCallKind(context.TargetId, ck); } break; @@ -228,20 +212,17 @@ data.EvaluationId is { } evaluationId && case TaskStartedEventArgs e: { if ((e.TaskName == "Csc" || e.TaskName == "Vbc") && - context.TargetId != BuildEventContext.InvalidTargetId && - contextMap.TryGetValue(context.ProjectContextId, out var data)) + contextMap.TryGetValue(context.ProjectContextId, out var contextData)) { - var taskData = data.GetOrCreateTaskData(context.TargetId); - taskData.IsCSharp = e.TaskName == "Csc"; - taskData.CompileTaskId = context.TaskId; + var isCSharp = e.TaskName == "Csc"; + _ = contextData.CreateTaskData(context, isCSharp); } break; } case TaskCommandLineEventArgs e: { - if (context.TargetId != BuildEventContext.InvalidTargetId && - contextMap.TryGetValue(context.ProjectContextId, out var data) && - data.TryGetTaskData(context.TargetId, out var taskData)) + if (contextMap.TryGetValue(context.ProjectContextId, out var contextData) && + contextData.TryGetTaskData(context, out var taskData)) { taskData.CommandLineArguments = e.CommandLine; } @@ -253,30 +234,20 @@ data.EvaluationId is { } evaluationId && return list; - static int? GetEvaluationId(ProjectStartedEventArgs e) + MSBuildProjectContextData GetOrCreateContextData(BuildEventContext context, string projecFile) { - if (e.BuildEventContext is { EvaluationId: > BuildEventContext.InvalidEvaluationId }) + if (!contextMap.TryGetValue(context.ProjectContextId, out var contextData)) { - return e.BuildEventContext.EvaluationId; + contextData = new MSBuildProjectContextData(projecFile, context.ProjectContextId, context.EvaluationId); + contextMap[context.ProjectContextId] = contextData; } - if (e.ParentProjectBuildEventContext is { EvaluationId: > BuildEventContext.InvalidEvaluationId }) + if (contextData.EvaluationId == BuildEventContext.InvalidEvaluationId) { - return e.ParentProjectBuildEventContext.EvaluationId; + contextData.EvaluationId = context.EvaluationId; } - return null; - } - - MSBuildProjectData GetOrCreateData(BuildEventContext context, string projectFile) - { - if (!contextMap.TryGetValue(context.ProjectContextId, out var data)) - { - data = new MSBuildProjectData(projectFile); - contextMap[context.ProjectContextId] = data; - } - - return data; + return contextData; } void SetTargetFramework(ref string? targetFramework, IEnumerable? rawProperties) @@ -291,6 +262,7 @@ void SetTargetFramework(ref string? targetFramework, IEnumerable? rawProperties) switch (property.Key) { case "TargetFramework": + Debug.Assert(!string.IsNullOrEmpty(property.Value)); targetFramework = property.Value; break; case "TargetFrameworks": diff --git a/src/Basic.CompilerLog/Properties/launchSettings.json b/src/Basic.CompilerLog/Properties/launchSettings.json index 64d94b8..95c555f 100644 --- a/src/Basic.CompilerLog/Properties/launchSettings.json +++ b/src/Basic.CompilerLog/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "CompilerLogger": { "commandName": "Project", - "commandLineArgs": "print -c \"C:\\Users\\jaredpar\\Downloads\\out_no_analyzers.binlog\"", + "commandLineArgs": "print -c \"C:\\Users\\jaredpar\\Downloads\\out_no_analyzers.binlog\" -p MiddleTier.Provider.csproj", "workingDirectory": "C:\\users\\jaredpar\\temp" } } diff --git a/src/Scratch/Scratch.cs b/src/Scratch/Scratch.cs index 1a56316..2716750 100644 --- a/src/Scratch/Scratch.cs +++ b/src/Scratch/Scratch.cs @@ -99,8 +99,11 @@ void DarkArtOfBuild() { - var filePath = @"c:\users\jaredpar\temp\console\msbuild.binlog"; - const string targetProjectFile = @"C:\Users\jaredpar\temp\console\console.csproj"; + var filePath = @"C:\Users\jaredpar\Downloads\out_no_analyzers.binlog"; + const string targetProjectFile = @"D:\a\_work\1\s\Source\Provider\Provider\MiddleTier.Provider.csproj"; + var map = new Dictionary>(); + var targetMap = new Dictionary<(int, int), string>(); + var set = new HashSet(); using var stream = File.OpenRead(filePath); var records = BinaryLog.ReadRecords(stream); foreach (var record in records) @@ -110,41 +113,70 @@ void DarkArtOfBuild() continue; } - var suffix = $"eval: {context.EvaluationId}, context: {context.ProjectContextId}, instance: {context.ProjectInstanceId}"; - switch (record.Args) { case ProjectStartedEventArgs { ProjectFile: targetProjectFile } e: { - Console.WriteLine($"ProjectStarted: {suffix}"); - break; - } - case ProjectFinishedEventArgs {ProjectFile: targetProjectFile } e: - { - Console.WriteLine($"ProjectFinished: {suffix}"); + _ = set.Add(context.ProjectContextId); break; } - case ProjectEvaluationStartedEventArgs { ProjectFile: targetProjectFile } e: - { - Console.WriteLine($"EvaluationStarted: {suffix}"); - break; - } - case ProjectEvaluationFinishedEventArgs { ProjectFile: targetProjectFile } e: + case TaskStartedEventArgs { ProjectFile: targetProjectFile } e: { - Console.WriteLine($"EvaluationFinished: {suffix}"); + if (e.TaskName != "Csc") + { + break; + } + + var key = (context.ProjectContextId, context.TargetId); + if (!targetMap.TryGetValue(key, out var targetName)) + { + targetName = ""; + Console.WriteLine($"Missing target {e.TaskName}"); + break; + } + + var list = GetOrCreate(context.ProjectContextId); + list.Add($"{targetName},{context.TargetId},{context.TaskId}"); break; } - case TaskStartedEventArgs { ProjectFile: targetProjectFile } e: + case TargetStartedEventArgs { ProjectFile: targetProjectFile } e: { - if ((e.TaskName == "Csc" || e.TaskName == "Vbc")) + var key = (context.ProjectContextId, context.TargetId); + if (targetMap.TryGetValue(key, out var target)) { - Console.WriteLine($"CompileStarted: {suffix}"); + Console.WriteLine($"Duplicate target {e.TargetName}"); } + + targetMap[key] = e.TargetName; break; } } } + foreach (var id in set) + { + if (!map.TryGetValue(id, out var list)) + { + continue; + } + + Console.WriteLine(id); + foreach (var item in list) + { + Console.WriteLine($" {item}"); + } + } + + List GetOrCreate(int projectContextId) + { + if (!map.TryGetValue(projectContextId, out var list)) + { + list = new List(); + map[projectContextId] = list; + } + + return list; + } } void ReadAttribute()