diff --git a/src/Basic.CompilerLog.UnitTests/BinaryLogReaderTests.cs b/src/Basic.CompilerLog.UnitTests/BinaryLogReaderTests.cs
index 15b8161..2143f58 100644
--- a/src/Basic.CompilerLog.UnitTests/BinaryLogReaderTests.cs
+++ b/src/Basic.CompilerLog.UnitTests/BinaryLogReaderTests.cs
@@ -77,6 +77,19 @@ public void CreateFilePathLogReaderState()
state.Dispose();
}
+ ///
+ /// Make sure the underlying stream is managed properly so we can read the compiler calls twice.
+ ///
+ [Fact]
+ public void ReadAllCompilerCallsTwice()
+ {
+ using var state = new LogReaderState();
+ using var reader = BinaryLogReader.Create(Fixture.Console.Value.BinaryLogPath!, BasicAnalyzerKind.OnDisk, state);
+ Assert.Single(reader.ReadAllCompilerCalls());
+ Assert.Single(reader.ReadAllCompilerCalls());
+ state.Dispose();
+ }
+
[Theory]
[InlineData(BasicAnalyzerKind.InMemory, true)]
[InlineData(BasicAnalyzerKind.OnDisk, true)]
diff --git a/src/Basic.CompilerLog.UnitTests/BinaryLogUtilTests.cs b/src/Basic.CompilerLog.UnitTests/BinaryLogUtilTests.cs
index a416603..74e1e18 100644
--- a/src/Basic.CompilerLog.UnitTests/BinaryLogUtilTests.cs
+++ b/src/Basic.CompilerLog.UnitTests/BinaryLogUtilTests.cs
@@ -27,7 +27,33 @@ public sealed class BinaryLogUtilTests
[InlineData("csc.exe a.cs b.cs", "csc.exe", "a.cs b.cs")]
public void ParseCompilerAndArgumentsCsc(string inputArgs, string? expectedCompilerFilePath, string expectedArgs)
{
- var (actualCompilerFilePath, actualArgs) = BinaryLogUtil.ParseTaskForCompilerAndArguments(ToArray(inputArgs), "csc.exe", "csc.dll");
+ var (actualCompilerFilePath, actualArgs) = BinaryLogUtil.ParseTaskForCompilerAndArguments(inputArgs, "csc.exe", "csc.dll");
+ Assert.Equal(ToArray(expectedArgs), actualArgs);
+ Assert.Equal(expectedCompilerFilePath, actualCompilerFilePath);
+ static string[] ToArray(string arg) => arg.Split(new char[]{' '}, StringSplitOptions.RemoveEmptyEntries);
+ }
+
+ [WindowsTheory]
+ [InlineData(@" C:\Program Files\dotnet\dotnet.exe exec ""C:\Program Files\dotnet\sdk\8.0.301\Roslyn\bincore\csc.dll"" a.cs", @"C:\Program Files\dotnet\sdk\8.0.301\Roslyn\bincore\csc.dll", "a.cs")]
+ [InlineData(@"C:\Program Files\dotnet\dotnet.exe exec ""C:\Program Files\dotnet\sdk\8.0.301\Roslyn\bincore\csc.dll"" a.cs", @"C:\Program Files\dotnet\sdk\8.0.301\Roslyn\bincore\csc.dll", "a.cs")]
+ [InlineData(@"""C:\Program Files\dotnet\dotnet.exe"" exec ""C:\Program Files\dotnet\sdk\8.0.301\Roslyn\bincore\csc.dll"" a.cs", @"C:\Program Files\dotnet\sdk\8.0.301\Roslyn\bincore\csc.dll", "a.cs")]
+ [InlineData(@"'C:\Program Files\dotnet\dotnet.exe' exec ""C:\Program Files\dotnet\sdk\8.0.301\Roslyn\bincore\csc.dll"" a.cs", @"C:\Program Files\dotnet\sdk\8.0.301\Roslyn\bincore\csc.dll", "a.cs")]
+ [InlineData(@"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\Roslyn\csc.exe a.cs b.cs", @"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\Roslyn\csc.exe", "a.cs b.cs")]
+ [InlineData(@"""C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\Roslyn\csc.exe"" a.cs b.cs", @"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\Roslyn\csc.exe", "a.cs b.cs")]
+ public void ParseCompilerAndArgumentsCscWindows(string inputArgs, string? expectedCompilerFilePath, string expectedArgs)
+ {
+ var (actualCompilerFilePath, actualArgs) = BinaryLogUtil.ParseTaskForCompilerAndArguments(inputArgs, "csc.exe", "csc.dll");
+ Assert.Equal(ToArray(expectedArgs), actualArgs);
+ Assert.Equal(expectedCompilerFilePath, actualCompilerFilePath);
+ static string[] ToArray(string arg) => arg.Split(new char[]{' '}, StringSplitOptions.RemoveEmptyEntries);
+ }
+
+ [UnixTheory]
+ [InlineData(@"/dotnet/dotnet exec /dotnet/sdk/bincore/csc.dll a.cs", "/dotnet/sdk/bincore/csc.dll", "a.cs")]
+ [InlineData(@"/dotnet/dotnet exec ""/dotnet/sdk/bincore/csc.dll"" a.cs", "/dotnet/sdk/bincore/csc.dll", "a.cs")]
+ public void ParseCompilerAndArgumentsCscUnix(string inputArgs, string? expectedCompilerFilePath, string expectedArgs)
+ {
+ var (actualCompilerFilePath, actualArgs) = BinaryLogUtil.ParseTaskForCompilerAndArguments(inputArgs, "csc.exe", "csc.dll");
Assert.Equal(ToArray(expectedArgs), actualArgs);
Assert.Equal(expectedCompilerFilePath, actualCompilerFilePath);
static string[] ToArray(string arg) => arg.Split(new char[]{' '}, StringSplitOptions.RemoveEmptyEntries);
@@ -39,21 +65,29 @@ public void ParseCompilerAndArgumentsCsc(string inputArgs, string? expectedCompi
[InlineData("vbc.exe a.cs b.cs", "vbc.exe", "a.cs b.cs")]
public void ParseCompilerAndArgumentsVbc(string inputArgs, string? expectedCompilerFilePath, string expectedArgs)
{
- var (actualCompilerFilePath, actualArgs) = BinaryLogUtil.ParseTaskForCompilerAndArguments(ToArray(inputArgs), "vbc.exe", "vbc.dll");
+ var (actualCompilerFilePath, actualArgs) = BinaryLogUtil.ParseTaskForCompilerAndArguments(inputArgs, "vbc.exe", "vbc.dll");
Assert.Equal(ToArray(expectedArgs), actualArgs);
Assert.Equal(expectedCompilerFilePath, actualCompilerFilePath);
static string[] ToArray(string arg) => arg.Split(new char[]{' '}, StringSplitOptions.RemoveEmptyEntries);
}
-
[Theory]
[InlineData("dotnet not what we expect a.cs")]
[InlineData("dotnet csc2 what we expect a.cs")]
[InlineData("dotnet exec vbc.dll what we expect a.cs")]
+ [InlineData("empty")]
+ [InlineData(" ")]
public void ParseCompilerAndArgumentsBad(string inputArgs)
{
- Assert.Throws(() => BinaryLogUtil.ParseTaskForCompilerAndArguments(ToArray(inputArgs), "csc.exe", "csc.dll"));
- static string[] ToArray(string arg) => arg.Split(new char[]{' '}, StringSplitOptions.RemoveEmptyEntries);
+ Assert.Throws(() => BinaryLogUtil.ParseTaskForCompilerAndArguments(inputArgs, "csc.exe", "csc.dll"));
+ }
+
+ [Fact]
+ public void ParseCompilerAndArgumentsNull()
+ {
+ var (actualCompilerFilePath, actualArgs) = BinaryLogUtil.ParseTaskForCompilerAndArguments(null, "csc.exe", "csc.dll");
+ Assert.Null(actualCompilerFilePath);
+ Assert.Empty(actualArgs);
}
}
diff --git a/src/Basic.CompilerLog.UnitTests/ConditionalFacts.cs b/src/Basic.CompilerLog.UnitTests/ConditionalFacts.cs
index 084d3f5..1903992 100644
--- a/src/Basic.CompilerLog.UnitTests/ConditionalFacts.cs
+++ b/src/Basic.CompilerLog.UnitTests/ConditionalFacts.cs
@@ -14,3 +14,26 @@ public WindowsFactAttribute()
}
}
}
+
+public sealed class WindowsTheoryAttribute : TheoryAttribute
+{
+ public WindowsTheoryAttribute()
+ {
+ if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ Skip = "This test is only supported on Windows";
+ }
+ }
+}
+
+public sealed class UnixTheoryAttribute : TheoryAttribute
+{
+ public UnixTheoryAttribute()
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ Skip = "This test is only supported on Windows";
+ }
+ }
+}
+
diff --git a/src/Basic.CompilerLog.UnitTests/ProgramTests.cs b/src/Basic.CompilerLog.UnitTests/ProgramTests.cs
index c1a4776..415c0ab 100644
--- a/src/Basic.CompilerLog.UnitTests/ProgramTests.cs
+++ b/src/Basic.CompilerLog.UnitTests/ProgramTests.cs
@@ -717,12 +717,23 @@ public void PrintCompilers()
var tuple = reader.ReadAllCompilerAssemblies().Single();
Assert.Contains($"""
Compilers
- {'\t'}File Path: {tuple.CompilerFilePath}
+ {'\t'}File Path: {tuple.FilePath}
{'\t'}Assembly Name: {tuple.AssemblyName}
{'\t'}Commit Hash: {tuple.CommitHash}
""", output);
}
+ ///
+ /// Ensure that print can run without the code being present
+ ///
+ [Fact]
+ public void PrintWithoutProject()
+ {
+ var (exitCode, output) = RunCompLogEx($"print {Fixture.RemovedBinaryLogPath} -c");
+ Assert.Equal(Constants.ExitSuccess, exitCode);
+ Assert.StartsWith("Projects", output);
+ }
+
///
/// Engage the code to find files in the specified directory
///
diff --git a/src/Basic.CompilerLog.UnitTests/SolutionFixture.cs b/src/Basic.CompilerLog.UnitTests/SolutionFixture.cs
index fc84e9a..58c3bea 100644
--- a/src/Basic.CompilerLog.UnitTests/SolutionFixture.cs
+++ b/src/Basic.CompilerLog.UnitTests/SolutionFixture.cs
@@ -40,6 +40,9 @@ public sealed class SolutionFixture : FixtureBase, IDisposable
internal string ConsoleWithDiagnosticsProjectName => Path.GetFileName(ConsoleWithDiagnosticsProjectPath);
+ ///
+ /// The binary log for a project that has been removed from disk
+ ///
internal string RemovedBinaryLogPath { get; }
///
diff --git a/src/Basic.CompilerLog.Util/BinaryLogReader.cs b/src/Basic.CompilerLog.Util/BinaryLogReader.cs
index f6ecfe2..3e177c3 100644
--- a/src/Basic.CompilerLog.Util/BinaryLogReader.cs
+++ b/src/Basic.CompilerLog.Util/BinaryLogReader.cs
@@ -1,4 +1,5 @@
using System.Collections.Immutable;
+using System.Reflection;
using Basic.CompilerLog.Util.Impl;
using MessagePack.Formatters;
using Microsoft.Build.Logging.StructuredLogger;
@@ -82,6 +83,7 @@ public List ReadAllCompilerCalls(Func? predica
{
predicate ??= static _ => true;
+ _stream.Position = 0;
return BinaryLogUtil.ReadAllCompilerCalls(_stream, predicate, ownerState: this);
}
@@ -299,6 +301,38 @@ public List ReadAllReferenceData(CompilerCall compilerCall)
return ReadAllReferenceDataCore(args.MetadataReferences.Select(x => x.Reference), args.MetadataReferences.Length);
}
+ public List ReadAllCompilerAssemblies()
+ {
+ var list = new List<(string CompilerFilePath, AssemblyName AssemblyName)>();
+ var map = new Dictionary(PathUtil.Comparer);
+ foreach (var compilerCall in ReadAllCompilerCalls())
+ {
+ if (compilerCall.CompilerFilePath is string compilerFilePath &&
+ !map.ContainsKey(compilerFilePath))
+ {
+ AssemblyName name;
+ string? commitHash;
+ try
+ {
+ name = AssemblyName.GetAssemblyName(compilerFilePath);
+ commitHash = RoslynUtil.ReadCompilerCommitHash(compilerFilePath);
+ }
+ catch
+ {
+ name = new AssemblyName(Path.GetFileName(compilerFilePath));
+ commitHash = null;
+ }
+
+ map[compilerCall.CompilerFilePath] = (name, commitHash);
+ }
+ }
+
+ return map
+ .OrderBy(x => x.Key, PathUtil.Comparer)
+ .Select(x => new CompilerAssemblyData(x.Key, x.Value.Item1, x.Value.Item2))
+ .ToList();
+ }
+
///
/// Attempt to add all the generated files from generators. When successful the generators
/// don't need to be run when re-hydrating the compilation.
diff --git a/src/Basic.CompilerLog.Util/BinaryLogUtil.cs b/src/Basic.CompilerLog.Util/BinaryLogUtil.cs
index ce09447..6f29d0f 100644
--- a/src/Basic.CompilerLog.Util/BinaryLogUtil.cs
+++ b/src/Basic.CompilerLog.Util/BinaryLogUtil.cs
@@ -1,6 +1,7 @@
using System.Collections;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+using System.Web;
using Microsoft.Build.Framework;
using Microsoft.Build.Logging.StructuredLogger;
using Microsoft.CodeAnalysis;
@@ -92,10 +93,9 @@ public CompilationTaskData(MSBuildProjectData projectData, int targetId)
}
var kind = Kind ?? CompilerCallKind.Unknown;
- var rawArgs = CommandLineParser.SplitCommandLineIntoArguments(CommandLineArguments, removeHashComments: true);
var (compilerFilePath, args) = IsCSharp
- ? ParseTaskForCompilerAndArguments(rawArgs, "csc.exe", "csc.dll")
- : ParseTaskForCompilerAndArguments(rawArgs, "vbc.exe", "vbc.dll");
+ ? ParseTaskForCompilerAndArguments(CommandLineArguments, "csc.exe", "csc.dll")
+ : ParseTaskForCompilerAndArguments(CommandLineArguments, "vbc.exe", "vbc.dll");
return new CompilerCall(
compilerFilePath,
@@ -288,37 +288,52 @@ void SetTargetFramework(ref string? targetFramework, IEnumerable? rawProperties)
/// The argument list is going to include either `dotnet exec csc.dll` or `csc.exe`. Need
/// to skip past that to get to the real command line.
///
- internal static (string? CompilerFilePath, string[] Arguments) ParseTaskForCompilerAndArguments(IEnumerable args, string exeName, string dllName)
+ internal static (string? CompilerFilePath, string[] Arguments) ParseTaskForCompilerAndArguments(string? args, string exeName, string dllName)
{
- using var e = args.GetEnumerator();
+ if (args is null)
+ {
+ return (null, []);
+ }
+
+ var argsStart = 0;
+ var appFilePath = FindApplication(args.AsSpan(), ref argsStart, out bool isDotNet);
+ if (appFilePath.IsEmpty)
+ {
+ throw InvalidCommandLine();
+ }
+
+ var rawArgs = CommandLineParser.SplitCommandLineIntoArguments(args.Substring(argsStart), removeHashComments: true);
+ using var e = rawArgs.GetEnumerator();
// The path to the executable is not escaped like the other command line arguments. Need
// to skip until we see an exec or a path with the exe as the file name.
string? compilerFilePath = null;
- var found = false;
- while (e.MoveNext())
+ if (isDotNet)
{
- if (PathUtil.Comparer.Equals(e.Current, "exec"))
+ // The path to the executable is not escaped like the other command line arguments. Need
+ // to skip until we see an exec or a path with the exe as the file name.
+ while (e.MoveNext())
{
- if (e.MoveNext() && PathUtil.Comparer.Equals(Path.GetFileName(e.Current), dllName))
+ if (PathUtil.Comparer.Equals(e.Current, "exec"))
{
- compilerFilePath = e.Current;
- found = true;
+ if (e.MoveNext() && PathUtil.Comparer.Equals(Path.GetFileName(e.Current), dllName))
+ {
+ compilerFilePath = e.Current;
+ }
+
+ break;
}
- break;
}
- else if (e.Current.EndsWith(exeName, PathUtil.Comparison))
+
+ if (compilerFilePath is null)
{
- compilerFilePath = e.Current;
- found = true;
- break;
+ throw InvalidCommandLine();
}
}
-
- if (!found)
+ else
{
- var cmdLine = string.Join(" ", args);
- throw new InvalidOperationException($"Could not parse command line arguments: {cmdLine}");
+ // Direct call to the compiler so we already have the compiler file path in hand
+ compilerFilePath = appFilePath.Trim('"').ToString();
}
var list = new List();
@@ -328,6 +343,84 @@ internal static (string? CompilerFilePath, string[] Arguments) ParseTaskForCompi
}
return (compilerFilePath, list.ToArray());
+
+ // This search is tricky because there is no attempt by MSBuild to properly quote the
+ ReadOnlySpan FindApplication(ReadOnlySpan args, ref int index, out bool isDotNet)
+ {
+ isDotNet = false;
+ while (index < args.Length && char.IsWhiteSpace(args[index]))
+ {
+ index++;
+ }
+
+ if (index >= args.Length)
+ {
+ return Span.Empty;
+ }
+
+ if (args[index] is '"' or '\'')
+ {
+ // Quote based parsing, just move to the next quote and return.
+ var start = index + 1;
+ var quote = args[index];
+ do
+ {
+ index++;
+ }
+ while (index < args.Length && args[index] != quote);
+
+ index++; // move past the quote
+ var span = args.Slice(start, index - start - 1);
+ isDotNet = CheckDotNet(span);
+ return span;
+ }
+ else
+ {
+ // Move forward until we see a signal that we've reached the compiler
+ // executable.
+ //
+ // Note: Specifically don't need to handle the case of the application ending at the
+ // exact end of the string. There is always at least one argument to the compiler.
+ while (index < args.Length)
+ {
+ if (char.IsWhiteSpace(args[index]))
+ {
+ var span = args.Slice(0, index);
+ if (span.EndsWith(exeName.AsSpan()))
+ {
+ isDotNet = false;
+ return span;
+ }
+
+ if (CheckDotNet(span))
+ {
+ isDotNet = true;
+ return span;
+ }
+
+ if (span.EndsWith(" exec".AsSpan()))
+ {
+ // This can happen when the dotnet host is not called dotnet. Need to back
+ // up to the path before that.
+ index -= 5;
+ span = args.Slice(0, index);
+ isDotNet = true;
+ return span;
+ }
+ }
+
+ index++;
+ }
+ }
+
+ return Span.Empty;
+
+ bool CheckDotNet(ReadOnlySpan span) =>
+ span.EndsWith("dotnet".AsSpan()) ||
+ span.EndsWith("dotnet.exe".AsSpan());
+ }
+
+ Exception InvalidCommandLine() => new InvalidOperationException($"Could not parse command line arguments: {args}");
}
///
diff --git a/src/Basic.CompilerLog.Util/CompilerAssemblyData.cs b/src/Basic.CompilerLog.Util/CompilerAssemblyData.cs
new file mode 100644
index 0000000..2f8ade2
--- /dev/null
+++ b/src/Basic.CompilerLog.Util/CompilerAssemblyData.cs
@@ -0,0 +1,16 @@
+using System.Collections.Immutable;
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
+
+namespace Basic.CompilerLog.Util;
+
+public sealed class CompilerAssemblyData(string filePath, AssemblyName assemblyName, string? commitHash)
+{
+ public string FilePath { get; } = filePath;
+ public AssemblyName AssemblyName { get; } = assemblyName;
+ public string? CommitHash { get; } = commitHash;
+
+ [ExcludeFromCodeCoverage]
+ public override string ToString() => $"{FilePath} {CommitHash}";
+}
+
diff --git a/src/Basic.CompilerLog.Util/CompilerLogReader.cs b/src/Basic.CompilerLog.Util/CompilerLogReader.cs
index c35da60..8f0a0a7 100644
--- a/src/Basic.CompilerLog.Util/CompilerLogReader.cs
+++ b/src/Basic.CompilerLog.Util/CompilerLogReader.cs
@@ -110,10 +110,20 @@ Metadata ReadMetadata()
return metadata;
}
}
- catch (InvalidDataException)
+ catch (Exception ex)
{
+ if (!leaveOpen)
+ {
+ stream.Dispose();
+ }
+
// Happens when this is not a valid zip file
- throw GetInvalidCompilerLogFileException();
+ if (ex is not CompilerLogException)
+ {
+ throw GetInvalidCompilerLogFileException();
+ }
+
+ throw;
}
static Exception GetInvalidCompilerLogFileException() => new CompilerLogException("Provided stream is not a compiler log file");
@@ -234,7 +244,7 @@ public List ReadAllCompilerCalls(Func? predica
return list;
}
- public List<(string CompilerFilePath, AssemblyName AssemblyName, string? CommitHash)> ReadAllCompilerAssemblies()
+ public List ReadAllCompilerAssemblies()
{
var list = new List<(string CompilerFilePath, AssemblyName AssemblyName)>();
var map = new Dictionary(PathUtil.Comparer);
@@ -252,7 +262,7 @@ pack.CompilerAssemblyName is not null &&
return map
.OrderBy(x => x.Key, PathUtil.Comparer)
- .Select(x => (x.Key, x.Value.Item1, x.Value.Item2))
+ .Select(x => new CompilerAssemblyData(x.Key, x.Value.Item1, x.Value.Item2))
.ToList();
}
diff --git a/src/Basic.CompilerLog.Util/ICompilerCallReader.cs b/src/Basic.CompilerLog.Util/ICompilerCallReader.cs
index d2cca01..58d911d 100644
--- a/src/Basic.CompilerLog.Util/ICompilerCallReader.cs
+++ b/src/Basic.CompilerLog.Util/ICompilerCallReader.cs
@@ -1,3 +1,5 @@
+using System.Reflection;
+
namespace Basic.CompilerLog.Util;
public interface ICompilerCallReader : IDisposable
@@ -18,4 +20,9 @@ public interface ICompilerCallReader : IDisposable
/// Read all of the for analyzers passed to the compilation
///
public List ReadAllAnalyzerData(CompilerCall compilerCall);
+
+ ///
+ /// Read all of the compilers used in this build.
+ ///
+ public List ReadAllCompilerAssemblies();
}
\ No newline at end of file
diff --git a/src/Basic.CompilerLog/Program.cs b/src/Basic.CompilerLog/Program.cs
index e783ebe..b6e1b35 100644
--- a/src/Basic.CompilerLog/Program.cs
+++ b/src/Basic.CompilerLog/Program.cs
@@ -165,8 +165,7 @@ int RunPrint(IEnumerable args)
return ExitSuccess;
}
- using var compilerLogStream = GetOrCreateCompilerLogStream(extra);
- using var reader = GetCompilerLogReader(compilerLogStream, leaveOpen: true);
+ using var reader = GetCompilerCallReader(extra, BasicAnalyzerKind.None);
var compilerCalls = reader.ReadAllCompilerCalls(options.FilterCompilerCalls);
WriteLine("Projects");
@@ -180,7 +179,7 @@ int RunPrint(IEnumerable args)
WriteLine("Compilers");
foreach (var tuple in reader.ReadAllCompilerAssemblies())
{
- WriteLine($"\tFile Path: {tuple.CompilerFilePath}");
+ WriteLine($"\tFile Path: {tuple.FilePath}");
WriteLine($"\tAssembly Name: {tuple.AssemblyName}");
WriteLine($"\tCommit Hash: {tuple.CommitHash}");
}
diff --git a/src/Basic.CompilerLog/Properties/launchSettings.json b/src/Basic.CompilerLog/Properties/launchSettings.json
index 9523064..64d94b8 100644
--- a/src/Basic.CompilerLog/Properties/launchSettings.json
+++ b/src/Basic.CompilerLog/Properties/launchSettings.json
@@ -2,7 +2,7 @@
"profiles": {
"CompilerLogger": {
"commandName": "Project",
- "commandLineArgs": "export -o c:\\users\\jaredpar\\temp\\export \"C:\\Users\\jaredpar\\Downloads\\msbuild.complog\"",
+ "commandLineArgs": "print -c \"C:\\Users\\jaredpar\\Downloads\\out_no_analyzers.binlog\"",
"workingDirectory": "C:\\users\\jaredpar\\temp"
}
}
diff --git a/src/Scratch/Scratch.cs b/src/Scratch/Scratch.cs
index 05dcb08..1a56316 100644
--- a/src/Scratch/Scratch.cs
+++ b/src/Scratch/Scratch.cs
@@ -8,6 +8,9 @@
using Basic.CompilerLog;
using Basic.CompilerLog.Util;
using BenchmarkDotNet.Environments;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Logging.StructuredLogger;
+
//using Microsoft.Build.Logging.StructuredLogger;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Classification;
@@ -42,7 +45,8 @@
// Profile();
-ReadAttribute();
+DarkArtOfBuild();
+// ReadAttribute();
// ExportScratch();
// await WorkspaceScratch();
// RoslynScratch();
@@ -93,6 +97,56 @@
}
*/
+void DarkArtOfBuild()
+{
+ var filePath = @"c:\users\jaredpar\temp\console\msbuild.binlog";
+ const string targetProjectFile = @"C:\Users\jaredpar\temp\console\console.csproj";
+ using var stream = File.OpenRead(filePath);
+ var records = BinaryLog.ReadRecords(stream);
+ foreach (var record in records)
+ {
+ if (record.Args is not { BuildEventContext: { } context })
+ {
+ 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}");
+ break;
+ }
+ case ProjectEvaluationStartedEventArgs { ProjectFile: targetProjectFile } e:
+ {
+ Console.WriteLine($"EvaluationStarted: {suffix}");
+ break;
+ }
+ case ProjectEvaluationFinishedEventArgs { ProjectFile: targetProjectFile } e:
+ {
+ Console.WriteLine($"EvaluationFinished: {suffix}");
+ break;
+ }
+ case TaskStartedEventArgs { ProjectFile: targetProjectFile } e:
+ {
+ if ((e.TaskName == "Csc" || e.TaskName == "Vbc"))
+ {
+ Console.WriteLine($"CompileStarted: {suffix}");
+ }
+ break;
+ }
+ }
+ }
+
+}
+
void ReadAttribute()
{
var assemblyPath = @"c:\Program Files\dotnet\sdk\8.0.204\Roslyn\bincore\csc.dll";
@@ -145,7 +199,7 @@ void PrintCompilers(string filePath)
using var reader = CompilerLogReader.Create(filePath);
foreach (var info in reader.ReadAllCompilerAssemblies())
{
- Console.WriteLine(info.CompilerFilePath);
+ Console.WriteLine(info.FilePath);
Console.WriteLine(info.AssemblyName);
}
}
@@ -183,7 +237,7 @@ static void PrintGeneratedFiles()
}
}
-static async Task WorkspaceScratch()
+/*static async Task WorkspaceScratch()
{
var filePath = @"/mnt/c/Users/jaredpar/temp/console/msbuild.complog";
using var reader = SolutionReader.Create(filePath, BasicAnalyzerKind.None);
@@ -199,6 +253,7 @@ static async Task WorkspaceScratch()
}
}
}
+*/
static void ExportScratch()
{