Skip to content

Commit

Permalink
Improve the None host (#50)
Browse files Browse the repository at this point in the history
* Simpler directory for output

* None now an analyzer

The None option now provides generated files via an `ISourceGenerator`
implementation.

* new diagnostic

* fixed the rest

* test issue

* more debugging

* more

* more

* experiment

* more

* more

* fixed a test bug

* more
  • Loading branch information
jaredpar authored Aug 16, 2023
1 parent 89f5720 commit 51ed519
Show file tree
Hide file tree
Showing 20 changed files with 593 additions and 243 deletions.
94 changes: 65 additions & 29 deletions src/Basic.CompilerLog.UnitTests/CompilerLogFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using System.Threading.Tasks;
using System.Web;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;

namespace Basic.CompilerLog.UnitTests;

Expand All @@ -22,6 +24,8 @@ public sealed class CompilerLogFixture : IDisposable

internal string ConsoleComplogPath { get; }

internal string ConsoleNoGeneratorComplogPath { get; }

internal string ClassLibComplogPath { get; }

internal string ClassLibSignedComplogPath { get; }
Expand All @@ -35,16 +39,31 @@ public sealed class CompilerLogFixture : IDisposable

internal IEnumerable<string> AllComplogs { get; }

public CompilerLogFixture()
/// <summary>
/// Constructor for the primary fixture. To get actual diagnostic messages into the output
/// Add the following to xunit.runner.json to enable "diagnosticMessages": true
/// </summary>
public CompilerLogFixture(IMessageSink messageSink)
{
StorageDirectory = Path.Combine(Path.GetTempPath(), nameof(CompilerLogFixture), Guid.NewGuid().ToString("N"));
ComplogDirectory = Path.Combine(StorageDirectory, "logs");
Directory.CreateDirectory(ComplogDirectory);

var diagnosticBuilder = new StringBuilder();
void RunDotnetCommand(string args, string workingDirectory)
{
diagnosticBuilder.AppendLine($"Running: {args} in {workingDirectory}");
var result = DotnetUtil.Command(args, workingDirectory);
diagnosticBuilder.AppendLine($"Succeeded: {result.Succeeded}");
diagnosticBuilder.AppendLine($"Standard Output: {result.StandardOut}");
diagnosticBuilder.AppendLine($"Standard Error: {result.StandardError}");
Assert.True(result.Succeeded);
}

var allCompLogs = new List<string>();
ConsoleComplogPath = WithBuild("console.complog", static string (string scratchPath) =>
ConsoleComplogPath = WithBuild("console.complog", string (string scratchPath) =>
{
DotnetUtil.CommandOrThrow($"new console --name example --output .", scratchPath);
RunDotnetCommand($"new console --name console --output .", scratchPath);
var projectFileContent = """
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
Expand All @@ -55,7 +74,7 @@ public CompilerLogFixture()
</PropertyGroup>
</Project>
""";
File.WriteAllText(Path.Combine(scratchPath, "example.csproj"), projectFileContent, TestBase.DefaultEncoding);
File.WriteAllText(Path.Combine(scratchPath, "console.csproj"), projectFileContent, TestBase.DefaultEncoding);
var program = """
using System;
using System.Text.RegularExpressions;
Expand All @@ -69,13 +88,20 @@ partial class Util {
}
""";
File.WriteAllText(Path.Combine(scratchPath, "Program.cs"), program, TestBase.DefaultEncoding);
Assert.True(DotnetUtil.Command("build -bl", scratchPath).Succeeded);
RunDotnetCommand("build -bl", scratchPath);
return Path.Combine(scratchPath, "msbuild.binlog");
});

ConsoleNoGeneratorComplogPath = WithBuild("console-no-generator.complog", string (string scratchPath) =>
{
RunDotnetCommand($"new console --name example-no-generator --output .", scratchPath);
RunDotnetCommand("build -bl", scratchPath);
return Path.Combine(scratchPath, "msbuild.binlog");
});

ClassLibComplogPath = WithBuild("classlib.complog", static string (string scratchPath) =>
ClassLibComplogPath = WithBuild("classlib.complog", string (string scratchPath) =>
{
DotnetUtil.CommandOrThrow($"new classlib --name example --output .", scratchPath);
RunDotnetCommand($"new classlib --name classlib --output .", scratchPath);
var projectFileContent = """
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
Expand All @@ -85,7 +111,7 @@ partial class Util {
</PropertyGroup>
</Project>
""";
File.WriteAllText(Path.Combine(scratchPath, "example.csproj"), projectFileContent, TestBase.DefaultEncoding);
File.WriteAllText(Path.Combine(scratchPath, "classlib.csproj"), projectFileContent, TestBase.DefaultEncoding);
var program = """
using System;
using System.Text.RegularExpressions;
Expand All @@ -96,13 +122,13 @@ partial class Util {
}
""";
File.WriteAllText(Path.Combine(scratchPath, "Class1.cs"), program, TestBase.DefaultEncoding);
Assert.True(DotnetUtil.Command("build -bl", scratchPath).Succeeded);
RunDotnetCommand("build -bl", scratchPath);
return Path.Combine(scratchPath, "msbuild.binlog");
});

ClassLibSignedComplogPath = WithBuild("classlibsigned.complog", static string (string scratchPath) =>
ClassLibSignedComplogPath = WithBuild("classlibsigned.complog", string (string scratchPath) =>
{
DotnetUtil.CommandOrThrow($"new classlib --name example --output .", scratchPath);
RunDotnetCommand($"new classlib --name classlibsigned --output .", scratchPath);
var keyFilePath = Path.Combine(scratchPath, "Key.snk");
var projectFileContent = $"""
<Project Sdk="Microsoft.NET.Sdk">
Expand All @@ -114,7 +140,7 @@ partial class Util {
</PropertyGroup>
</Project>
""";
File.WriteAllText(Path.Combine(scratchPath, "example.csproj"), projectFileContent, TestBase.DefaultEncoding);
File.WriteAllText(Path.Combine(scratchPath, "classlibsigned.csproj"), projectFileContent, TestBase.DefaultEncoding);
File.WriteAllBytes(keyFilePath, ResourceLoader.GetResourceBlob("Key.snk"));
var program = """
using System;
Expand All @@ -126,13 +152,13 @@ partial class Util {
}
""";
File.WriteAllText(Path.Combine(scratchPath, "Class1.cs"), program, TestBase.DefaultEncoding);
Assert.True(DotnetUtil.Command("build -bl", scratchPath).Succeeded);
RunDotnetCommand("build -bl", scratchPath);
return Path.Combine(scratchPath, "msbuild.binlog");
});

ClassLibMultiComplogPath = WithBuild("classlibmulti.complog", static string (string scratchPath) =>
ClassLibMultiComplogPath = WithBuild("classlibmulti.complog", string (string scratchPath) =>
{
DotnetUtil.CommandOrThrow($"new classlib --name example --output .", scratchPath);
RunDotnetCommand($"new classlib --name classlibmulti --output .", scratchPath);
var projectFileContent = """
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
Expand All @@ -142,7 +168,7 @@ partial class Util {
</PropertyGroup>
</Project>
""";
File.WriteAllText(Path.Combine(scratchPath, "example.csproj"), projectFileContent, TestBase.DefaultEncoding);
File.WriteAllText(Path.Combine(scratchPath, "classlibmulti.csproj"), projectFileContent, TestBase.DefaultEncoding);
var program = """
using System;
using System.Text.RegularExpressions;
Expand All @@ -152,32 +178,42 @@ partial class Util {
}
""";
File.WriteAllText(Path.Combine(scratchPath, "Class 1.cs"), program, TestBase.DefaultEncoding);
Assert.True(DotnetUtil.Command("build -bl", scratchPath).Succeeded);
RunDotnetCommand("build -bl", scratchPath);
return Path.Combine(scratchPath, "msbuild.binlog");
});

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
WpfAppComplogPath = WithBuild("wpfapp.complog", static string (string scratchPath) =>
WpfAppComplogPath = WithBuild("wpfapp.complog", string (string scratchPath) =>
{
Assert.True(DotnetUtil.Command("new wpf --name example --output .", scratchPath).Succeeded);
Assert.True(DotnetUtil.Command("build -bl", scratchPath).Succeeded);
RunDotnetCommand("new wpf --name wpfapp --output .", scratchPath);
RunDotnetCommand("build -bl", scratchPath);
return Path.Combine(scratchPath, "msbuild.binlog");
});
}

AllComplogs = allCompLogs;
string WithBuild(string name, Func<string, string> action)
{
var scratchPath = Path.Combine(StorageDirectory, "scratch dir");
Directory.CreateDirectory(scratchPath);
var binlogFilePath = action(scratchPath);
var complogFilePath = Path.Combine(ComplogDirectory, name);
var diagnostics = CompilerLogUtil.ConvertBinaryLog(binlogFilePath, complogFilePath);
Assert.Empty(diagnostics);
Directory.Delete(scratchPath, recursive: true);
allCompLogs.Add(complogFilePath);
return complogFilePath;
try
{
var scratchPath = Path.Combine(StorageDirectory, "scratch dir", Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(scratchPath);
RunDotnetCommand("new globaljson --sdk-version 7.0.400", scratchPath);
var binlogFilePath = action(scratchPath);
Assert.True(File.Exists(binlogFilePath));
var complogFilePath = Path.Combine(ComplogDirectory, name);
var diagnostics = CompilerLogUtil.ConvertBinaryLog(binlogFilePath, complogFilePath);
Assert.Empty(diagnostics);
Directory.Delete(scratchPath, recursive: true);
allCompLogs.Add(complogFilePath);
return complogFilePath;
}
catch (Exception ex)
{
messageSink.OnMessage(new DiagnosticMessage(diagnosticBuilder.ToString()));
throw new Exception($"Cannot generate compiler log {name}", ex);
}
}
}

Expand Down
59 changes: 52 additions & 7 deletions src/Basic.CompilerLog.UnitTests/CompilerLogReaderTests.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using Basic.CompilerLog.Util;
using Basic.CompilerLog.Util.Impl;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.ComponentModel.Design.Serialization;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
Expand Down Expand Up @@ -180,10 +182,10 @@ public void AnalyzerLoadCaching(BasicAnalyzerKind kind)

var options = new BasicAnalyzerHostOptions(kind, cacheable: true);
using var reader = CompilerLogReader.Create(Fixture.ConsoleComplogPath, options: options);
var key = reader.ReadRawCompilationData(0).Item2.Analyzers;
var data = reader.ReadRawCompilationData(0).Item2;

var host1 = reader.ReadAnalyzers(key);
var host2 = reader.ReadAnalyzers(key);
var host1 = reader.ReadAnalyzers(data);
var host2 = reader.ReadAnalyzers(data);
Assert.Same(host1, host2);
host1.Dispose();
Assert.True(host1.IsDisposed);
Expand Down Expand Up @@ -263,36 +265,79 @@ public void EmitToMemory()
}

[Fact]
public void NoAnalyzersGeneratedFilesInRaw()
public void NoneHostGeneratedFilesInRaw()
{
using var reader = CompilerLogReader.Create(Fixture.ConsoleComplogPath, BasicAnalyzerHostOptions.None);
var (_, data) = reader.ReadRawCompilationData(0);
Assert.Equal(1, data.Contents.Count(x => x.Kind == RawContentKind.GeneratedText));
}

[Fact]
public void NoAnalyzerGeneratedFilesShouldBeFirst()
public void NoneHostGeneratedFilesShouldBeLast()
{
using var reader = CompilerLogReader.Create(Fixture.ConsoleComplogPath, BasicAnalyzerHostOptions.None);
var data = reader.ReadCompilationData(0);
var tree = data.Compilation.SyntaxTrees.First();
var tree = data.GetCompilationAfterGenerators().SyntaxTrees.Last();
var decls = tree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>().ToList();
Assert.True(decls.Count >= 2);
Assert.Equal("Util", decls[0].Identifier.Text);
Assert.Equal("GetRegex_0", decls[1].Identifier.Text);
}

[Fact]
public void NoAnalyzerShouldHaveNoAnalyzers()
public void NoneHostAddsFakeGeneratorForGeneratedSource()
{
using var reader = CompilerLogReader.Create(Fixture.ConsoleComplogPath, BasicAnalyzerHostOptions.None);
var data = reader.ReadCompilationData(0);
var compilation1 = data.Compilation;
var compilation2 = data.GetCompilationAfterGenerators();
Assert.NotSame(compilation1, compilation2);
Assert.Single(data.AnalyzerReferences);
}

[Fact]
public void NoneHostAddsNoGeneratorIfNoGeneratedSource()
{
using var reader = CompilerLogReader.Create(Fixture.ConsoleNoGeneratorComplogPath, BasicAnalyzerHostOptions.None);
var data = reader.ReadCompilationData(0);
var compilation1 = data.Compilation;
var compilation2 = data.GetCompilationAfterGenerators();
Assert.Same(compilation1, compilation2);
Assert.Empty(data.AnalyzerReferences);
}

[Fact]
public void NoneHostNativePdb()
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return;
}

RunDotNet($"new console --name example --output .");
var projectFileContent = """
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<DebugType>Full</DebugType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
""";
File.WriteAllText(Path.Combine(RootDirectory, "example.csproj"), projectFileContent, DefaultEncoding);
RunDotNet("build -bl");

using var reader = CompilerLogReader.Create(Path.Combine(RootDirectory, "msbuild.binlog"), BasicAnalyzerHostOptions.None);
var rawData = reader.ReadRawCompilationData(0).Item2;
Assert.False(rawData.ReadGeneratedFiles);
var data = reader.ReadCompilationData(0);
var compilation = data.GetCompilationAfterGenerators(out var diagnostics);
Assert.Single(diagnostics);
Assert.Equal(BasicAnalyzerHostNone.CannotReadGeneratedFiles.Id, diagnostics[0].Id);
}

[Fact]
public void KindWpf()
{
Expand Down
4 changes: 2 additions & 2 deletions src/Basic.CompilerLog.UnitTests/ExportUtilTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,8 @@ class C { }
[Fact]
public void ConsoleWithRuleset()
{
RunDotNet($"new console --name example --output .");
File.WriteAllText(Path.Combine(RootDirectory, "example.csproj"),
RunDotNet($"new console --name console-with-ruleset --output .");
File.WriteAllText(Path.Combine(RootDirectory, "console-with-ruleset.csproj"),
"""
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
Expand Down
20 changes: 11 additions & 9 deletions src/Basic.CompilerLog.UnitTests/ProgramTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ public void CreateFullPath()
[Fact]
public void References()
{
Assert.Equal(0, RunCompLog($"ref -o {RootDirectory} {Fixture.ComplogDirectory}"));
Assert.NotEmpty(Directory.EnumerateFiles(Path.Combine(RootDirectory, "example", "refs"), "*.dll"));
Assert.NotEmpty(Directory.EnumerateFiles(Path.Combine(RootDirectory, "example", "analyzers"), "*.dll", SearchOption.AllDirectories));
Assert.Equal(0, RunCompLog($"ref -o {RootDirectory} {Path.Combine(Fixture.ComplogDirectory, "console.complog")}"));
Assert.NotEmpty(Directory.EnumerateFiles(Path.Combine(RootDirectory, "console", "refs"), "*.dll"));
Assert.NotEmpty(Directory.EnumerateFiles(Path.Combine(RootDirectory, "console", "analyzers"), "*.dll", SearchOption.AllDirectories));
}

[Theory]
Expand All @@ -84,15 +84,17 @@ public void ExportHelloWorld(string template)
Assert.True(buildResult.Succeeded);
}

[Fact]
public void EmitConsole()
[Theory]
[InlineData("")]
[InlineData("-none")]
public void EmitConsole(string arg)
{
using var emitDir = new TempDir();
RunCompLog($"emit -o {emitDir.DirectoryPath} {Fixture.ConsoleComplogPath}");
RunCompLog($"emit {arg} -o {emitDir.DirectoryPath} {Fixture.ConsoleComplogPath}");

AssertOutput(@"example\emit\example.dll");
AssertOutput(@"example\emit\example.pdb");
AssertOutput(@"example\emit\ref\example.dll");
AssertOutput(@"console\emit\console.dll");
AssertOutput(@"console\emit\console.pdb");
AssertOutput(@"console\emit\ref\console.dll");

void AssertOutput(string relativePath)
{
Expand Down
Loading

0 comments on commit 51ed519

Please sign in to comment.