Skip to content

Embed solution node into a graph#14407

Draft
VolPlita wants to merge 1 commit into
dotnet:mainfrom
VolPlita:fix-8170-add-solution-node-graph
Draft

Embed solution node into a graph#14407
VolPlita wants to merge 1 commit into
dotnet:mainfrom
VolPlita:fix-8170-add-solution-node-graph

Conversation

@VolPlita

Copy link
Copy Markdown
Contributor

Fixes #8170 #10657

Context

Graph builds for solutions did not consistently model/execute a solution node as the single entrypoint, and graph-mode binlog behavior around the solution-level execution path needed to be aligned.

Changes Made

  • Added a synthetic solution node in graph construction and set it as the only entry/root node for solution graph builds.
  • Wired synthetic node dependencies to all solution projects so scheduler executes projects first, then solution last.
  • Updated graph target inference/execution plumbing to handle synthetic entry nodes while keeping project node results behavior intact.
  • Suppressed inner solution redispatch in graph mode via IsGraphBuild task conditions so hooks/imports run without rebuilding already-built projects.
  • Updated binlog import collection to include solution path from graph solution execution.
  • Added/updated targeted unit tests for BuildManager, Graph, and BinaryLogger coverage.

Testing

  • .\build.cmd
  • Repro: build a tiny solution with graph mode + binlog and verify solution presence/order in with-graph.binlog
    • & <local-msbuild> .\tiny-graph.sln -graph -bl:with-graph.binlog

Notes

@VolPlita
VolPlita force-pushed the fix-8170-add-solution-node-graph branch from 07153ac to 85c9f04 Compare July 17, 2026 09:37
@VolPlita
VolPlita requested a review from rainersigwald July 17, 2026 09:37
@VolPlita VolPlita self-assigned this Jul 17, 2026

Copilot AI left a comment

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.

Pull request overview

This PR adjusts solution graph builds so they model a .sln as a single synthetic entry/root node, enabling solution-level execution (including before/after.<sln>.targets hooks) while still building solution projects through the static graph scheduler, and ensuring the solution file is captured in graph-mode binlog import collection.

Changes:

  • Add a synthetic solution node during graph construction and make it the sole entry/root node for solution graph builds.
  • Update graph target inference and BuildManager graph scheduling to recognize/execute the synthetic solution node while keeping per-project node results behavior.
  • Extend binary logger import collection and add/adjust unit tests for graph + binlog solution behavior.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/Build/Logging/BinaryLogger/BinaryLogger.cs Adds solution-path import collection during ProjectStarted to embed the .sln in ProjectImports.
src/Build/Graph/ProjectGraph.cs Adjusts target inference to account for entry points that are not buildable project nodes (synthetic solution node).
src/Build/Graph/GraphBuilder.cs Constructs a synthetic solution ProjectGraphNode and wires it to depend on all solution projects.
src/Build/Construction/Solution/SolutionProjectGenerator.cs Conditions solution traversal MSBuild tasks to avoid redispatching projects during graph builds.
src/Build/BackEnd/BuildManager/BuildManager.cs Executes the synthetic solution node in graph scheduling and treats its failure as submission failure while excluding it from per-node results.
src/Build.UnitTests/Graph/ProjectGraph_Tests.cs Updates expectations for solution graphs now having a single .sln entry/root node.
src/Build.UnitTests/Graph/GraphLoadedFromSolution_tests.cs Updates assertions to reflect the synthetic .sln entry/root node model.
src/Build.UnitTests/BinaryLogger_Tests.cs Adds a test asserting graph solution builds embed the solution file into ProjectImports zip.
src/Build.UnitTests/BackEnd/BuildManager_Tests.cs Adds a test covering synthetic solution node execution and solution hook execution in graph mode.

Comment on lines +628 to +632
if (projectArgs.GlobalProperties?.TryGetValue("SolutionPath", out string solutionPath) == true &&
!string.IsNullOrEmpty(solutionPath))
{
projectImportsCollector.AddFile(EscapingUtilities.UnescapeAll(solutionPath));
}
Comment on lines +1463 to +1465
string projectMatchCondition = Strings.WeakIntern($"'%(ProjectReference.Identity)' == '{GetMetaprojectName(project)}'");
string combinedCondition = $"({projectMatchCondition}) and {NotGraphBuildCondition}";
ProjectTaskInstance task = target.AddTask("MSBuild", combinedCondition, String.Empty);
Comment on lines +750 to +753
if (Solution != null && !projectNodeSet.Contains(entryPointNode))
{
return ["Build"];
}
Comment on lines +2319 to +2323
if (syntheticSolutionNodeResult?.OverallResult == BuildResultCode.Failure)
{
graphBuildResult.Exception = syntheticSolutionNodeResult.Exception
?? new InvalidOperationException("Synthetic solution graph node execution failed.");
}
Comment on lines +4562 to +4563
_logger.AssertLogContains("AfterSolutionHookRan");
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this would catch my big problem . . .

@rainersigwald rainersigwald left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  • Repro: build a tiny solution with graph mode + binlog and verify solution presence/order in with-graph.binlog

    • & <local-msbuild> .\tiny-graph.sln -graph -bl:with-graph.binlog

Try this again, please, I think it shows a problem (that the projects aren't actually building):

dotnet msbuild -graph -bl
    info NETSDK1057: You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy
  Console1 net11.0 succeeded (1.3s) → Console1\bin\Debug\net11.0\Console1.dll
  Lib1 net11.0 succeeded (0.2s) → Lib1\bin\Debug\net11.0\Lib1.dll

Build succeeded in 3.2sS:\msbuild\artifacts\bin\bootstrap\core\dotnet.exe msbuild -graph -bl

Build succeeded in 0.0s

if (projectArgs.GlobalProperties?.TryGetValue("SolutionPath", out string solutionPath) == true &&
!string.IsNullOrEmpty(solutionPath))
{
projectImportsCollector.AddFile(EscapingUtilities.UnescapeAll(solutionPath));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hm--this works, but I don't like it. Can you trace why the sln is getting into the non-graph binlog (my guess is as an actual ProjectStartedEventArgs path) and replicate that for the graph path?

private static void AddReferencesBuildTask(ProjectTargetInstance target, string targetToBuild, string outputItem)
{
ProjectTaskInstance task = target.AddTask("MSBuild", String.Empty, String.Empty);
ProjectTaskInstance task = target.AddTask("MSBuild", NotGraphBuildCondition, String.Empty);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Question I'd like you to look at deeply for onboarding: should this be a condition on the target, or on the tasks within? The relevant reference doc is https://learn.microsoft.com/visualstudio/msbuild/target-build-order.

private readonly ProjectGraph.ProjectInstanceFactoryFunc _projectInstanceFactory;
private readonly ProjectGraphMode _graphMode;
private IReadOnlyDictionary<string, IReadOnlyCollection<string>> _solutionDependencies;
private ImmutableDictionary<string, string> _solutionGlobalProperties;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think I understand why these need to be preserved at the graph level, can you expand on that (or avoid it?)?

Comment on lines +168 to +172
syntheticSolutionInstance = new ProjectInstance(
syntheticRootElement,
solutionGlobalProperties,
toolsVersion: null,
_projectCollection);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you get the instance out of the SolutionProjectGenerator instead of reconstructing one here?

Comment on lines +4562 to +4563
_logger.AssertLogContains("AfterSolutionHookRan");
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this would catch my big problem . . .

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.

Graph build of a solution does not embed .sln file in binlog

3 participants