Skip to content

Align SolutionReader workspaces with MSBuildWorkspace conventions - #388

Open
jaredpar wants to merge 2 commits into
mainfrom
jaredpar-workspace-parity-analysis
Open

jaredpar wants to merge 2 commits into
mainfrom
jaredpar-workspace-parity-analysis

Conversation

@jaredpar

@jaredpar jaredpar commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Summary

  • add a development tool that compares MSBuildWorkspace output with workspaces reconstructed from compiler logs
  • align project, assembly, document, folder, additional-file, and multi-target project-reference behavior with common workspace conventions
  • centralize multi-targeted project detection as a public ICompilerCallReader extension
  • add parity tests covering both binary and compiler logs

Testing

  • dotnet build Basic.CompilerLog.slnx -warnaserror --no-restore
  • full net10.0 test suite: 1,064 passed, 6 skipped

Add a development comparer for MSBuildWorkspace and compiler-log workspaces, align SolutionReader naming and document metadata, and centralize multi-target project detection.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0bfbac11-17ce-436f-b287-7443cdc00269
@codecov

codecov Bot commented Sep 2, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.43590% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 95.98%. Comparing base (88e9f1b) to head (9831348).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/Basic.CompilerLog.Util/SolutionReader.cs 92.85% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #388      +/-   ##
==========================================
+ Coverage   95.97%   95.98%   +0.01%     
==========================================
  Files          54       54              
  Lines        6163     6182      +19     
  Branches      709      710       +1     
==========================================
+ Hits         5915     5934      +19     
  Misses        130      130              
  Partials      118      118              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

var data = new CompilerCallData(
compilerCall,
compilationName,
assemblyFileName,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In ReadCompilationData we pass compilerCallData.AssemblyFileName (with .dll) to CreateCSharpCompilationData but args.CompilationName (without .dll) to CreateVisualBasicCompilationData, that seems inconsistent.

Comment thread docs/overview.md

```bash
dotnet run --project src/Basic.CompilerLog.WorkspaceComparer -- <project-or-solution> <compiler-log>
```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we reuse projects generated by CompilerLogFixture and test WorkspaceComparer.Compare on them and assert that there are no differences?

if (compilerCall.TargetFramework is not null &&
_multiTargetProjectPaths.Contains(compilerCall.ProjectFilePath))
{
projectName = $"{projectName} ({compilerCall.TargetFramework})";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


var fileName = sourceTextData.FilePath;
var filePath = sourceTextData.FilePath;
var fileName = Path.GetFileName(filePath);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MSBuildWorkspace uses Link metadata too for linked documents.

}

Console.Error.WriteLine($"{differences.Length} difference(s)");
return 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we return non-zero exit code when there are differences?

Keep the extensionless Roslyn assembly name separate from the compiler output file name so workspace output paths remain valid without changing compilation identity.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0bfbac11-17ce-436f-b287-7443cdc00269

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The PR introduces a few concrete operational/performance issues (mixed MSBuild package versions under central pinning, redundant full enumerations, and a comparer tool that always returns success) that should be addressed before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review tier: Lite
Findings: 4 Medium severity

New issues introduced by this change (4)
Severity Finding
Medium severity src/​Basic.CompilerLog.App/​CompilerLogApp.cs — reader.GetMultiTargetedProjectFilePaths(predicate) will call back into the reader and…
Medium severity src/​Basic.CompilerLog.Util/​SolutionReader.cs — reader.GetMultiTargetedProjectFilePaths() re-reads all compiler calls even though compilerCalls…
Medium severity src/​Basic.CompilerLog.WorkspaceComparer/​Program.cs — The comparer prints the number of differences but always returns exit code 0, which makes it hard…
Medium severity src/​Directory.Packages.props — Microsoft.Build.Utilities.Core is pinned to 17.5.0 while other MSBuild packages are pinned to…
What changed in this PR

This PR adds a new workspace comparison development tool and updates SolutionReader/readers so compiler-log reconstructed workspaces more closely match MSBuildWorkspace conventions (project naming, output paths, document/folder handling, additional files, and multi-targeting behavior), backed by new parity tests.

Changes:

  • Add Basic.CompilerLog.WorkspaceComparer to compare MSBuildWorkspace solutions vs SolutionReader solutions and report structural differences.
  • Align SolutionReader’s project/document metadata (names, folders, output file path, reference mapping) with typical workspace expectations, including multi-targeting behavior.
  • Add/extend unit tests and fixtures to cover the new workspace-shape behaviors for both compiler logs and binary logs.
File Description
src/​Directory.Packages.props Adds MSBuild/Roslyn workspace-related package versions for the comparer tool.
src/​Basic.CompilerLog.WorkspaceComparer/​WorkspaceComparer.cs Implements structural comparison of two Roslyn solutions/projects/documents/references/options.
src/​Basic.CompilerLog.WorkspaceComparer/​Program.cs CLI entrypoint to load MSBuild workspace + compiler-log workspace and print diffs.
src/​Basic.CompilerLog.WorkspaceComparer/​Basic.CompilerLog.WorkspaceComparer.csproj New net10.0 dev-tool project and dependencies.
src/​Basic.CompilerLog.Util/​SolutionReader.cs Updates reconstructed workspace shape (names, assembly/output, folders, references) and adds multi-target awareness.
src/​Basic.CompilerLog.Util/​Impl/​BasicAnalyzerHostOnDisk.cs Removes trailing whitespace.
src/​Basic.CompilerLog.Util/​Impl/​BasicAnalyzerHostInMemory.cs Removes trailing whitespace.
src/​Basic.CompilerLog.Util/​Extensions.cs Adds ICompilerCallReader.GetMultiTargetedProjectFilePaths extension for centralized detection.
src/​Basic.CompilerLog.Util/​CompilerLogReader.cs Minor named-argument tweak for CompilerCallState construction.
src/​Basic.CompilerLog.Util/​CompilerCallData.cs Adds internal OutputFileName to preserve output filename (incl. extension) separately from assembly name.
src/​Basic.CompilerLog.Util/​BinaryLogReader.cs Fixes additional-file classification and refines output/assembly naming in CompilerCallData.
src/​Basic.CompilerLog.UnitTests/​SolutionReaderTests.cs Adds parity tests for naming, folders, references, analyzers, metadata reference display, and output path behavior.
src/​Basic.CompilerLog.UnitTests/​CompilerLogFixture.cs Extends fixtures for nested docs, analyzer config, additional files under folders, and a multi-target project reference scenario.
src/​Basic.CompilerLog.UnitTests/​BinaryLogReaderTests.cs Adds test ensuring additional files are classified as AdditionalText.
src/​Basic.CompilerLog.App/​CompilerLogApp.cs Uses centralized multi-target detection extension for naming compiler calls.
docs/​overview.md Documents the new workspace comparer dev tool and usage.
Basic.CompilerLog.slnx Adds the new WorkspaceComparer project to the solution.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 1000 to 1004
var compilerCalls = ReadAllCompilerCalls(reader, predicate);

// Set used to determine if the projects are single or multi-targeted.
var isMultiTargetedMap = new Dictionary<string, bool>(PathUtil.Comparer);
foreach (var compilerCall in compilerCalls)
{
if (compilerCall.Kind == CompilerCallKind.Regular)
{
ref bool isMultiTargeted = ref CollectionsMarshal.GetValueRefOrAddDefault(isMultiTargetedMap, compilerCall.ProjectFilePath, out var exists);
isMultiTargeted = exists;
}
}
var multiTargetedProjectFilePaths = reader.GetMultiTargetedProjectFilePaths(predicate);

// Generate unique names for each compiler call
Comment on lines 31 to +33
var map = new SortedDictionary<int, (CompilerCall, ProjectId)>();
var compilerCalls = reader.ReadAllCompilerCalls();
_multiTargetProjectPaths = reader.GetMultiTargetedProjectFilePaths();
}

Console.Error.WriteLine($"{differences.Length} difference(s)");
return 0;
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="$(_RoslynVersion)" />
<PackageVersion Include="Microsoft.Build.Framework" Version="17.11.48" />
<PackageVersion Include="Microsoft.Build.Locator" Version="1.11.2" />
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.5.0" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants