dotnet / msbuild

The Microsoft Build Engine (MSBuild) is the build platform for .NET and Visual Studio.
https://docs.microsoft.com/visualstudio/msbuild/msbuild
MIT License
5.23k stars 1.35k forks source link

[Feature Request]: Expose Static Graph to Central Loggers #8662

Open ryanerdmann opened 1 year ago

ryanerdmann commented 1 year ago

Summary

When building with /graph, MSBuild constructs a dependency graph of the project tree before scheduling builds. This is a powerful concept that enables many new scenarios with MSBuild.

Today, while this ProjectGraph is available to the build engine, it is not exposed via the ILogger-based extensibility model. Currently, loggers are only delivered a textual status message, e.g. "Static graph loaded in 0.906 seconds: 4 nodes, 3 edges".

Exposing the project graph up-front as a first-class concept to the logger would make it possible to do some very cool things (and do so easily), including:

Some of these are of course possible today via other means, even in the logger, but often this requires parsing status messages or project files directly. Exposing the ProjectGraph directly to loggers will help complete the Static Graph vision and enable new extensibility based on this capability.

Background and Motivation

I began thinking about this problem when I started working on an MSBuild logger that would provide progress tracking and help developers understand the impact of their changes on incremental builds. I was hoping to better inform developers by visualizing what sub-trees of the project graph were getting rebuilt after a change.

Having the rich Microsoft.Build.Graph.ProjectGraph type available would have made this almost a trivial task.

Proposed Feature

The high-level proposal is to introduce a new StaticGraphLoadedEventHandler on IEventSource, and deliver the ProjectGraph object to it.

Proposed API

namespace Microsoft.Build.Framework
{
+   public class StaticGraphLoadedEventArgs : BuildEventArgs
+   {
+       public TimeSpan Duration { get; }
+       public object ProjectGraph { get; }
+   }

+   public delegate void StaticGraphLoadedEventHandler(object sender, StaticGraphLoadedEventArgs e);

    public interface IEventSource
    {
+       event StaticGraphLoadedEventHandler GraphLoaded;
    }
}

Note that the StaticGraphLoadedEventArgs.ProjectGraph property is exposed as a System.Object, as the actual ProjectGraph type is in Microsoft.Build.dll. Ideally, the type could be exposed through Microsoft.Build.Framework.dll since it is fundamental to MSBuild, but I also think it's perfectly reasonable for logger implementations to take a dependency on Microsoft.Build.dll without forcing the type directly onto ILogger in Microsoft.Build.Framework.dll.

I would also note that it probably makes the most sense for central loggers to register for the event (rather than distributed loggers). Such a logger would receive the event, cast the event argument to a ProjectGraph, and cache this for the lifetime of the logger. Then, any build events received by the logger could be evaluated in the context of that static graph.

Builds not opting-in to /graph would not receive this event; it is a "value-added" scenario for Static Graph users.

Alternative Designs

No response

AR-May commented 1 year ago

@dfederm could you please take a look?

dfederm commented 1 year ago

At first blush, there would be a pretty big problem with exposing ProjectGraph to loggers, as it's mutable, or at least the ProjectInstances you can get at are.

For this same reason is why I assume project started events don't get the ProjectInstance.

rainersigwald commented 1 year ago

There might be value in returning a structured representation of the graph, even if it differs from the underlying one.