From 7d9ef9cb0231f437666a525ef817c042977fcd14 Mon Sep 17 00:00:00 2001 From: Corniel Nobel Date: Tue, 4 Mar 2025 19:03:01 +0100 Subject: [PATCH] Expose collected BuildEvents publically (#296) --- src/Buildalyzer/AnalyzerManagerOptions.cs | 6 +- src/Buildalyzer/AnalyzerResults.cs | 4 + src/Buildalyzer/BuildEventArgsCollector.cs | 3 + src/Buildalyzer/IAnalyzerResults.cs | 9 +- src/Buildalyzer/ProjectAnalyzer.cs | 91 +++++++++---------- .../Integration/SimpleProjectsFixture.cs | 10 ++ 6 files changed, 67 insertions(+), 56 deletions(-) diff --git a/src/Buildalyzer/AnalyzerManagerOptions.cs b/src/Buildalyzer/AnalyzerManagerOptions.cs index 3092c6e..f0a3c66 100644 --- a/src/Buildalyzer/AnalyzerManagerOptions.cs +++ b/src/Buildalyzer/AnalyzerManagerOptions.cs @@ -7,15 +7,15 @@ namespace Buildalyzer; public class AnalyzerManagerOptions { - public ILoggerFactory LoggerFactory { get; set; } + public ILoggerFactory? LoggerFactory { get; set; } /// /// A filter that indicates whether a give project should be loaded. /// Return true to load the project, false to filter it out. /// - public Func ProjectFilter { get; set; } + public Func? ProjectFilter { get; set; } - public TextWriter LogWriter + public TextWriter? LogWriter { set { diff --git a/src/Buildalyzer/AnalyzerResults.cs b/src/Buildalyzer/AnalyzerResults.cs index 363d215..6c38d82 100644 --- a/src/Buildalyzer/AnalyzerResults.cs +++ b/src/Buildalyzer/AnalyzerResults.cs @@ -1,4 +1,5 @@ using System.Collections.Concurrent; +using Microsoft.Build.Framework; namespace Buildalyzer; @@ -19,6 +20,9 @@ internal void Add(IEnumerable results, bool overallSuccess) _overallSuccess = _overallSuccess.HasValue ? _overallSuccess.Value && overallSuccess : overallSuccess; } + /// + public ImmutableArray BuildEventArguments { get; set; } = []; + public IAnalyzerResult this[string targetFramework] => _results[targetFramework]; public IEnumerable TargetFrameworks => _results.Keys.OrderBy(e => e, TargetFrameworkComparer.Instance); diff --git a/src/Buildalyzer/BuildEventArgsCollector.cs b/src/Buildalyzer/BuildEventArgsCollector.cs index c3e43e4..a1ae43d 100644 --- a/src/Buildalyzer/BuildEventArgsCollector.cs +++ b/src/Buildalyzer/BuildEventArgsCollector.cs @@ -17,6 +17,9 @@ public BuildEventArgsCollector(EventArgsDispatcher server) /// public int Count => Bag.Count; + /// Indicates that no events has been collected. + public bool IsEmpty => Count == 0; + /// public IEnumerator GetEnumerator() => Bag.GetEnumerator(); diff --git a/src/Buildalyzer/IAnalyzerResults.cs b/src/Buildalyzer/IAnalyzerResults.cs index f2c4e5f..0505c41 100644 --- a/src/Buildalyzer/IAnalyzerResults.cs +++ b/src/Buildalyzer/IAnalyzerResults.cs @@ -1,10 +1,13 @@ +using Microsoft.Build.Framework; + namespace Buildalyzer; -public interface IAnalyzerResults : IEnumerable +public interface IAnalyzerResults : IReadOnlyCollection { - IAnalyzerResult this[string targetFramework] { get; } + /// The collected during the analysis. + ImmutableArray BuildEventArguments { get; } - int Count { get; } + IAnalyzerResult this[string targetFramework] { get; } bool OverallSuccess { get; } diff --git a/src/Buildalyzer/ProjectAnalyzer.cs b/src/Buildalyzer/ProjectAnalyzer.cs index a5429b0..e9f0895 100644 --- a/src/Buildalyzer/ProjectAnalyzer.cs +++ b/src/Buildalyzer/ProjectAnalyzer.cs @@ -6,7 +6,6 @@ using Buildalyzer.Logger; using Buildalyzer.Logging; using Microsoft.Build.Construction; -using Microsoft.Build.Framework; using Microsoft.Build.Logging; using Microsoft.Extensions.Logging; using MsBuildPipeLogger; @@ -148,61 +147,53 @@ public IAnalyzerResults Build(string targetFramework, BuildEnvironment buildEnvi private IAnalyzerResults BuildTargets( BuildEnvironment buildEnvironment, string targetFramework, string[] targetsToBuild, AnalyzerResults results) { - using (CancellationTokenSource cancellation = new CancellationTokenSource()) - { - using var pipeLogger = new AnonymousPipeLoggerServer(cancellation.Token); - bool receivedAnyEvent = false; - - void OnPipeLoggerOnAnyEventRaised(object o, BuildEventArgs buildEventArgs) - { - receivedAnyEvent = true; - } - - pipeLogger.AnyEventRaised += OnPipeLoggerOnAnyEventRaised; + using var cancellation = new CancellationTokenSource(); - using var eventProcessor = new EventProcessor(Manager, this, BuildLoggers, pipeLogger, results != null); + using var pipeLogger = new AnonymousPipeLoggerServer(cancellation.Token); + using var eventCollector = new BuildEventArgsCollector(pipeLogger); + using var eventProcessor = new EventProcessor(Manager, this, BuildLoggers, pipeLogger, true); - // Run MSBuild - int exitCode; - string fileName = GetCommand( - buildEnvironment, - targetFramework, - targetsToBuild, - pipeLogger.GetClientHandle(), - out string arguments); - - using (ProcessRunner processRunner = new ProcessRunner( - fileName, - arguments, - buildEnvironment.WorkingDirectory ?? Path.GetDirectoryName(ProjectFile.Path), - GetEffectiveEnvironmentVariables(buildEnvironment), - Manager.LoggerFactory)) + // Run MSBuild + int exitCode; + string fileName = GetCommand( + buildEnvironment, + targetFramework, + targetsToBuild, + pipeLogger.GetClientHandle(), + out string arguments); + + using var processRunner = new ProcessRunner( + fileName, + arguments, + buildEnvironment.WorkingDirectory ?? Path.GetDirectoryName(ProjectFile.Path)!, + GetEffectiveEnvironmentVariables(buildEnvironment)!, + Manager.LoggerFactory); + + void OnProcessRunnerExited() + { + if (eventCollector.IsEmpty && processRunner.ExitCode != 0) { - void OnProcessRunnerExited() - { - if (!receivedAnyEvent && processRunner.ExitCode != 0) - { - pipeLogger.Dispose(); - } - } - - processRunner.Exited += OnProcessRunnerExited; - processRunner.Start(); - try - { - pipeLogger.ReadAll(); - } - catch (ObjectDisposedException) - { - // Ignore - } - processRunner.WaitForExit(); - exitCode = processRunner.ExitCode; + pipeLogger.Dispose(); } + } - // Collect the results - results?.Add(eventProcessor.Results, exitCode == 0 && eventProcessor.OverallSuccess); + processRunner.Exited += OnProcessRunnerExited; + processRunner.Start(); + try + { + pipeLogger.ReadAll(); + } + catch (ObjectDisposedException) + { + // Ignore } + processRunner.WaitForExit(); + exitCode = processRunner.ExitCode; + results.BuildEventArguments = [.. eventCollector]; + + // Collect the results + results.Add(eventProcessor.Results, exitCode == 0 && eventProcessor.OverallSuccess); + return results; } diff --git a/tests/Buildalyzer.Tests/Integration/SimpleProjectsFixture.cs b/tests/Buildalyzer.Tests/Integration/SimpleProjectsFixture.cs index 2cf48d5..018ab1a 100644 --- a/tests/Buildalyzer.Tests/Integration/SimpleProjectsFixture.cs +++ b/tests/Buildalyzer.Tests/Integration/SimpleProjectsFixture.cs @@ -81,6 +81,16 @@ public void Builds_DesignTime( results.Should().AllSatisfy(r => r.Succeeded.Should().BeTrue()); } + [Test] + public void Collects_BuildEventArguments() + { + using var ctx = Context.ForProject(@"SdkNet6Project\SdkNet6Project.csproj"); + + var results = ctx.Analyzer.Build(new EnvironmentOptions()); + + results.BuildEventArguments.Should().HaveCount(18); + } + [Test] public void BuildsProject( [ValueSource(nameof(Preferences))] EnvironmentPreference preference,