Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve the None host #50

Merged
merged 13 commits into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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