Skip to content

Commit

Permalink
Abstract out reading reference and analyzers (#124)
Browse files Browse the repository at this point in the history
* Abstract out reading reference and analyzers

Both log types now support it

* more

* more
  • Loading branch information
jaredpar committed May 1, 2024
1 parent bf6bd51 commit a6d2007
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 55 deletions.
19 changes: 19 additions & 0 deletions src/Basic.CompilerLog.UnitTests/PathNormalizationUtilTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public sealed class PathNormalizationUtilTests
[Theory]
[InlineData(@"c:\", "/code/")]
[InlineData(@"c:\\", "/code/")]
[InlineData(@"c:\\\", "/code/")]
[InlineData(@"c:\src\blah.cs", "/code/src/blah.cs")]
[InlineData(@"c:\src\..\blah.cs", "/code/src/../blah.cs")]
[InlineData(null, null)]
Expand All @@ -19,6 +20,15 @@ public void WindowsToUnixNormalize(string? path, string? expected)
Assert.Equal(expected, actual);
}

[Theory]
[InlineData(@"example.cs", "/code/example.cs")]
[InlineData(@"a.cs", "/code/a.cs")]
public void WindowsToUnixRootFileName(string fileName, string? expected)
{
var actual = PathNormalizationUtil.WindowsToUnix.RootFileName(fileName);
Assert.Equal(expected, actual);
}

[Theory]
[InlineData("/", @"c:\code\")]
[InlineData("/example", @"c:\code\example")]
Expand All @@ -32,6 +42,15 @@ public void UnixToWindowsNormalize(string? path, string? expected)
Assert.Equal(expected, actual);
}

[Theory]
[InlineData(@"example.cs", @"c:\code\example.cs")]
[InlineData(@"a.cs", @"c:\code\a.cs")]
public void UnixToWindowsRootFileName(string fileName, string? expected)
{
var actual = PathNormalizationUtil.UnixToWindows.RootFileName(fileName);
Assert.Equal(expected, actual);
}

[Theory]
[InlineData(@"c:\", true)]
[InlineData(@"c:", true)]
Expand Down
50 changes: 30 additions & 20 deletions src/Basic.CompilerLog.UnitTests/ProgramTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,19 @@ private void AssertCompilerCallReader(Action<ICompilerCallReader> action)
_assertCompilerCallReader = action;
}

private void AssertCorrectReader(ICompilerCallReader reader, string logFilePath)
{
var isBinlog = Path.GetExtension(logFilePath) == ".binlog";
if (isBinlog)
{
Assert.IsType<BinaryLogReader>(reader);
}
else
{
Assert.IsType<CompilerLogReader>(reader);
}
}

public (int ExitCode, string Output) RunCompLogEx(string args, string? currentDirectory = null)
{
try
Expand Down Expand Up @@ -103,11 +116,15 @@ private void RunWithBoth(Action<string> action)
}

[Fact]
public void AnalyzersSimple()
public void AnalyzersBoth()
{
var (exitCode, output) = RunCompLogEx($"analyzers {Fixture.SolutionBinaryLogPath} -p console.csproj");
Assert.Equal(Constants.ExitSuccess, exitCode);
Assert.Contains("Microsoft.CodeAnalysis.NetAnalyzers.dll", output);
RunWithBoth(void (string logPath) =>
{
AssertCompilerCallReader(void (ICompilerCallReader reader) => AssertCorrectReader(reader, logPath));
var (exitCode, output) = RunCompLogEx($"analyzers {logPath} -p console.csproj");
Assert.Equal(Constants.ExitSuccess, exitCode);
Assert.Contains("Microsoft.CodeAnalysis.NetAnalyzers.dll", output);
});
}

[Fact]
Expand All @@ -118,12 +135,16 @@ public void AnalyzersHelp()
Assert.StartsWith("complog analyzers [OPTIONS]", output);
}

/// <summary>
/// The analyzers can still be listed if the project file is deleted as long as the
/// analyzers are still on disk
/// </summary>
[Fact]
public void AnalyzersError()
public void AnalyzersProjectFilesDeleted()
{
var (exitCode, output) = RunCompLogEx($"analyzers {Fixture.RemovedBinaryLogPath}");
Assert.NotEqual(Constants.ExitSuccess, exitCode);
Assert.StartsWith("Unexpected error", output);
Assert.Equal(Constants.ExitSuccess, exitCode);
Assert.Contains("CSharp.NetAnalyzers.dll", output);
}

[Fact]
Expand Down Expand Up @@ -243,6 +264,7 @@ public void References()
{
RunWithBoth(logPath =>
{
AssertCompilerCallReader(reader => AssertCorrectReader(reader, logPath));
Assert.Equal(Constants.ExitSuccess, RunCompLog($"ref -o {RootDirectory} {logPath}"));
Assert.NotEmpty(Directory.EnumerateFiles(Path.Combine(RootDirectory, "console", "refs"), "*.dll"));
Assert.NotEmpty(Directory.EnumerateFiles(Path.Combine(RootDirectory, "console", "analyzers"), "*.dll", SearchOption.AllDirectories));
Expand Down Expand Up @@ -569,19 +591,7 @@ public void ReplayWithBothLogs()
{
RunWithBoth(void (string logFilePath) =>
{
var isBinlog = Path.GetExtension(logFilePath) == ".binlog";
AssertCompilerCallReader(void (ICompilerCallReader reader) =>
{
if (isBinlog)
{
Assert.IsType<BinaryLogReader>(reader);
}
else
{
Assert.IsType<CompilerLogReader>(reader);
}
});
AssertCompilerCallReader(void (ICompilerCallReader reader) => AssertCorrectReader(reader, logFilePath));
RunCompLog($"replay {logFilePath}");
});
}
Expand Down
45 changes: 38 additions & 7 deletions src/Basic.CompilerLog.Util/BinaryLogReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.VisualBasic;

Expand Down Expand Up @@ -169,13 +170,7 @@ VisualBasicCompilationData GetVisualBasic()

BasicAnalyzerHost CreateAnalyzerHost()
{
var list = new List<RawAnalyzerData>(args.AnalyzerReferences.Length);
foreach (var analyzer in args.AnalyzerReferences)
{
var data = new RawAnalyzerData(RoslynUtil.GetMvid(analyzer.FilePath), analyzer.FilePath);
list.Add(data);
}

var list = ReadAllRawAnalyzerData(args);
return LogReaderState.GetOrCreate(
BasicAnalyzerKind,
list,
Expand Down Expand Up @@ -296,6 +291,42 @@ EmitData GetEmitData()
}
}

public List<ReferenceData> ReadAllAnalyzerData(CompilerCall compilerCall)
{
CheckOwnership(compilerCall);
var args = ReadCommandLineArguments(compilerCall);
return ReadAllReferenceDataCore(args.AnalyzerReferences.Select(x => x.FilePath), args.AnalyzerReferences.Length);
}

public List<ReferenceData> ReadAllReferenceData(CompilerCall compilerCall)
{
CheckOwnership(compilerCall);
var args = ReadCommandLineArguments(compilerCall);
return ReadAllReferenceDataCore(args.MetadataReferences.Select(x => x.Reference), args.MetadataReferences.Length);
}

private List<ReferenceData> ReadAllReferenceDataCore(IEnumerable<string> filePaths, int count)
{
var list = new List<ReferenceData>(capacity: count);
foreach (var filePath in filePaths)
{
var data = new ReferenceData(filePath, RoslynUtil.GetMvid(filePath), File.ReadAllBytes(filePath));
list.Add(data);
}
return list;
}

private List<RawAnalyzerData> ReadAllRawAnalyzerData(CommandLineArguments args)
{
var list = new List<RawAnalyzerData>(args.AnalyzerReferences.Length);
foreach (var analyzer in args.AnalyzerReferences)
{
var data = new RawAnalyzerData(RoslynUtil.GetMvid(analyzer.FilePath), analyzer.FilePath);
list.Add(data);
}
return list;
}

private void CheckOwnership(CompilerCall compilerCall)
{
if (compilerCall.OwnerState is BinaryLogReader reader && object.ReferenceEquals(reader, this))
Expand Down
24 changes: 13 additions & 11 deletions src/Basic.CompilerLog.Util/CompilerLogReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.VisualBasic;
using Microsoft.CodeAnalysis.VisualBasic.Syntax;
Expand Down Expand Up @@ -163,7 +164,7 @@ private RawCompilationData ReadRawCompilationDataCore(int index, CompilationInfo

var references = dataPack
.References
.Select(x => new RawReferenceData(x.Mvid, x.Aliases, x.EmbedInteropTypes))
.Select(x => new RawReferenceData(x.Mvid, x.Aliases, x.EmbedInteropTypes, x.FilePath))
.ToList();
var analyzers = dataPack
.Analyzers
Expand Down Expand Up @@ -477,31 +478,32 @@ internal int GetIndex(CompilerCall compilerCall)
throw new ArgumentException($"The provided {nameof(CompilerCall)} is not from this instance");
}

public List<(string FileName, byte[] ImageBytes)> ReadReferenceFileInfo(CompilerCall compilerCall)
public List<ReferenceData> ReadAllReferenceData(CompilerCall compilerCall)
{
var index = GetIndex(compilerCall);
var (_, rawCompilationData) = ReadRawCompilationData(index);
var list = new List<(string, byte[])>(rawCompilationData.References.Count);
var list = new List<ReferenceData>(rawCompilationData.References.Count);
foreach (var referenceData in rawCompilationData.References)
{
list.Add((
GetMetadataReferenceFileName(referenceData.Mvid),
GetAssemblyBytes(referenceData.Mvid)));
var filePath = referenceData.FilePath is string fp
? fp
: PathNormalizationUtil.RootFileName(GetMetadataReferenceFileName(referenceData.Mvid));
var data = new ReferenceData(filePath, referenceData.Mvid, GetAssemblyBytes(referenceData.Mvid));
list.Add(data);
}

return list;
}

public List<(string FilePath, byte[] ImageBytes)> ReadAnalyzerFileInfo(CompilerCall compilerCall)
public List<ReferenceData> ReadAllAnalyzerData(CompilerCall compilerCall)
{
var index = GetIndex(compilerCall);
var (_, rawCompilationData) = ReadRawCompilationData(index);
var list = new List<(string, byte[])>(rawCompilationData.Analyzers.Count);
var list = new List<ReferenceData>(rawCompilationData.Analyzers.Count);
foreach (var analyzerData in rawCompilationData.Analyzers)
{
list.Add((
analyzerData.FilePath,
GetAssemblyBytes(analyzerData.Mvid)));
var data = new ReferenceData(analyzerData.FilePath, analyzerData.Mvid, GetAssemblyBytes(analyzerData.Mvid));
list.Add(data);
}

return list;
Expand Down
10 changes: 10 additions & 0 deletions src/Basic.CompilerLog.Util/ICompilerCallReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,14 @@ public interface ICompilerCallReader : IDisposable
public List<CompilerCall> ReadAllCompilerCalls(Func<CompilerCall, bool>? predicate = null);
public List<CompilationData> ReadAllCompilationData(Func<CompilerCall, bool>? predicate = null);
public CompilationData ReadCompilationData(CompilerCall compilerCall);

/// <summary>
/// Read all of the <see cref="ReferenceData"/> for references passed to the compilation
/// </summary>
public List<ReferenceData> ReadAllReferenceData(CompilerCall compilerCall);

/// <summary>
/// Read all of the <see cref="ReferenceData"/> for analyzers passed to the compilation
/// </summary>
public List<ReferenceData> ReadAllAnalyzerData(CompilerCall compilerCall);
}
31 changes: 29 additions & 2 deletions src/Basic.CompilerLog.Util/PathNormalizationUtil.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@

using System.Buffers;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;

namespace Basic.CompilerLog.Util;

internal abstract class PathNormalizationUtil
{
internal const string WindowsRoot = @"c:\code\";
internal const string UnixRoot = @"/code/";

internal const int MaxPathLength = 520;
internal static PathNormalizationUtil Empty { get; } = new EmtpyNormalizationUtil();
internal static PathNormalizationUtil WindowsToUnix { get; } = new WindowsToUnixNormalizationUtil(@"/code");
internal static PathNormalizationUtil UnixToWindows { get; } = new UnixToWindowsNormalizationUtil(@"c:\code\");
internal static PathNormalizationUtil WindowsToUnix { get; } = new WindowsToUnixNormalizationUtil(UnixRoot);
internal static PathNormalizationUtil UnixToWindows { get; } = new UnixToWindowsNormalizationUtil(WindowsRoot);

/// <summary>
/// Is the path rooted in the "from" platform
Expand All @@ -25,6 +30,11 @@ internal abstract class PathNormalizationUtil
/// <returns></returns>
[return: NotNullIfNotNull("path")]
internal abstract string? NormalizePath(string? path);

/// <summary>
/// Make the file name an absolute path by putting it under the root
/// </summary>
internal abstract string RootFileName(string fileName);
}

/// <summary>
Expand Down Expand Up @@ -53,9 +63,17 @@ internal override bool IsPathRooted([NotNullWhen(true)] string? path) =>
int pathIndex = 0;
if (IsPathRooted(path))
{
Debug.Assert(Root[Root.Length-1]=='/');

Root.AsSpan().CopyTo(array.AsSpan());
arrayIndex += Root.Length;
pathIndex += 2;

// Move past any extra slashes after the c: portion of the path.
while (pathIndex < path.Length && path[pathIndex] == '\\')
{
pathIndex++;
}
}

while (pathIndex < path.Length)
Expand All @@ -79,6 +97,8 @@ internal override bool IsPathRooted([NotNullWhen(true)] string? path) =>
ArrayPool<char>.Shared.Return(array);
return normalizedPath;
}

internal override string RootFileName(string fileName)=> Root + fileName;
}

file sealed class UnixToWindowsNormalizationUtil(string root) : PathNormalizationUtil
Expand All @@ -103,6 +123,7 @@ internal override bool IsPathRooted([NotNullWhen(true)] string? path) =>
int pathIndex = 0;
if (IsPathRooted(path))
{
Debug.Assert(Root[Root.Length-1]=='\\');
Root.AsSpan().CopyTo(array.AsSpan());
arrayIndex += Root.Length;
pathIndex += 1;
Expand All @@ -125,6 +146,8 @@ internal override bool IsPathRooted([NotNullWhen(true)] string? path) =>
ArrayPool<char>.Shared.Return(array);
return normalizedPath;
}

internal override string RootFileName(string fileName)=> Root + fileName;
}

/// <summary>
Expand All @@ -133,6 +156,10 @@ internal override bool IsPathRooted([NotNullWhen(true)] string? path) =>
/// </summary>
file sealed class EmtpyNormalizationUtil : PathNormalizationUtil
{
internal string Root { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? WindowsRoot : UnixRoot;

internal override bool IsPathRooted(string? path) => Path.IsPathRooted(path);
internal override string? NormalizePath(string? path) => path;

internal override string RootFileName(string fileName)=> Root + fileName;
}
4 changes: 3 additions & 1 deletion src/Basic.CompilerLog.Util/RawCompilationData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,14 @@ internal readonly struct RawReferenceData
internal readonly Guid Mvid;
internal readonly ImmutableArray<string> Aliases;
internal readonly bool EmbedInteropTypes;
internal readonly string? FilePath;

internal RawReferenceData(Guid mvid, ImmutableArray<string> aliases, bool embedInteropTypes)
internal RawReferenceData(Guid mvid, ImmutableArray<string> aliases, bool embedInteropTypes, string? filePath)
{
Mvid = mvid;
Aliases = aliases;
EmbedInteropTypes = embedInteropTypes;
FilePath = filePath;
}
}

Expand Down
Loading

0 comments on commit a6d2007

Please sign in to comment.