Build project before running
This commit is contained in:
@@ -120,7 +120,7 @@ public partial class RoslynAnalysis(ILogger<RoslynAnalysis> logger, BuildService
|
|||||||
using (var ___ = SharpIdeOtel.Source.StartActivity("RestoreSolution"))
|
using (var ___ = SharpIdeOtel.Source.StartActivity("RestoreSolution"))
|
||||||
{
|
{
|
||||||
// MsBuildProjectLoader doesn't do a restore which is absolutely required for resolving PackageReferences, if they have changed. I am guessing it just reads from project.assets.json
|
// MsBuildProjectLoader doesn't do a restore which is absolutely required for resolving PackageReferences, if they have changed. I am guessing it just reads from project.assets.json
|
||||||
await _buildService.MsBuildAsync(_sharpIdeSolutionModel.FilePath, BuildType.Restore, cancellationToken);
|
await _buildService.MsBuildAsync(_sharpIdeSolutionModel.FilePath, BuildType.Restore, BuildStartedFlags.UserFacing, cancellationToken);
|
||||||
}
|
}
|
||||||
using (var ___ = SharpIdeOtel.Source.StartActivity("OpenSolution"))
|
using (var ___ = SharpIdeOtel.Source.StartActivity("OpenSolution"))
|
||||||
{
|
{
|
||||||
@@ -187,7 +187,7 @@ public partial class RoslynAnalysis(ILogger<RoslynAnalysis> logger, BuildService
|
|||||||
Guard.Against.Null(_msBuildProjectLoader, nameof(_msBuildProjectLoader));
|
Guard.Against.Null(_msBuildProjectLoader, nameof(_msBuildProjectLoader));
|
||||||
|
|
||||||
// It is important to note that a Workspace has no concept of MSBuild, nuget packages etc. It is just told about project references and "metadata" references, which are dlls. This is the what MSBuild does - it reads the csproj, and most importantly resolves nuget package references to dlls
|
// It is important to note that a Workspace has no concept of MSBuild, nuget packages etc. It is just told about project references and "metadata" references, which are dlls. This is the what MSBuild does - it reads the csproj, and most importantly resolves nuget package references to dlls
|
||||||
await _buildService.MsBuildAsync(_sharpIdeSolutionModel!.FilePath, BuildType.Restore, cancellationToken);
|
await _buildService.MsBuildAsync(_sharpIdeSolutionModel!.FilePath, BuildType.Restore, BuildStartedFlags.UserFacing, cancellationToken);
|
||||||
var __ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.MSBuildProjectLoader.LoadSolutionInfoAsync");
|
var __ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.MSBuildProjectLoader.LoadSolutionInfoAsync");
|
||||||
// This call is the expensive part - MSBuild is slow. There doesn't seem to be any incrementalism for solutions.
|
// This call is the expensive part - MSBuild is slow. There doesn't seem to be any incrementalism for solutions.
|
||||||
// The best we could do to speed it up is do .LoadProjectInfoAsync for the single project, and somehow munge that into the existing solution
|
// The best we could do to speed it up is do .LoadProjectInfoAsync for the single project, and somehow munge that into the existing solution
|
||||||
@@ -214,7 +214,7 @@ public partial class RoslynAnalysis(ILogger<RoslynAnalysis> logger, BuildService
|
|||||||
Guard.Against.Null(_workspace, nameof(_workspace));
|
Guard.Against.Null(_workspace, nameof(_workspace));
|
||||||
Guard.Against.Null(_msBuildProjectLoader, nameof(_msBuildProjectLoader));
|
Guard.Against.Null(_msBuildProjectLoader, nameof(_msBuildProjectLoader));
|
||||||
|
|
||||||
await _buildService.MsBuildAsync(_sharpIdeSolutionModel!.FilePath, BuildType.Restore, cancellationToken);
|
await _buildService.MsBuildAsync(_sharpIdeSolutionModel!.FilePath, BuildType.Restore, BuildStartedFlags.Internal, cancellationToken);
|
||||||
var __ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(CustomMsBuildProjectLoader)}.{nameof(CustomMsBuildProjectLoader.LoadProjectInfosAsync)}");
|
var __ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(CustomMsBuildProjectLoader)}.{nameof(CustomMsBuildProjectLoader.LoadProjectInfosAsync)}");
|
||||||
|
|
||||||
var thisProject = GetProjectForSharpIdeProjectModel(projectModel);
|
var thisProject = GetProjectForSharpIdeProjectModel(projectModel);
|
||||||
|
|||||||
@@ -16,15 +16,16 @@ public enum BuildType
|
|||||||
Clean,
|
Clean,
|
||||||
Restore
|
Restore
|
||||||
}
|
}
|
||||||
|
public enum BuildStartedFlags { UserFacing = 0, Internal }
|
||||||
public class BuildService(ILogger<BuildService> logger)
|
public class BuildService(ILogger<BuildService> logger)
|
||||||
{
|
{
|
||||||
private readonly ILogger<BuildService> _logger = logger;
|
private readonly ILogger<BuildService> _logger = logger;
|
||||||
|
|
||||||
public EventWrapper<Task> BuildStarted { get; } = new(() => Task.CompletedTask);
|
public EventWrapper<BuildStartedFlags, Task> BuildStarted { get; } = new(_ => Task.CompletedTask);
|
||||||
public EventWrapper<Task> BuildFinished { get; } = new(() => Task.CompletedTask);
|
public EventWrapper<Task> BuildFinished { get; } = new(() => Task.CompletedTask);
|
||||||
public ChannelTextWriter BuildTextWriter { get; } = new ChannelTextWriter();
|
public ChannelTextWriter BuildTextWriter { get; } = new ChannelTextWriter();
|
||||||
private CancellationTokenSource? _cancellationTokenSource;
|
private CancellationTokenSource? _cancellationTokenSource;
|
||||||
public async Task MsBuildAsync(string solutionOrProjectFilePath, BuildType buildType = BuildType.Build, CancellationToken cancellationToken = default)
|
public async Task MsBuildAsync(string solutionOrProjectFilePath, BuildType buildType = BuildType.Build, BuildStartedFlags buildStartedFlags = BuildStartedFlags.UserFacing, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (_cancellationTokenSource is not null) throw new InvalidOperationException("A build is already in progress.");
|
if (_cancellationTokenSource is not null) throw new InvalidOperationException("A build is already in progress.");
|
||||||
_cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
_cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||||
@@ -55,7 +56,7 @@ public class BuildService(ILogger<BuildService> logger)
|
|||||||
hostServices: null,
|
hostServices: null,
|
||||||
flags: BuildRequestDataFlags.None);
|
flags: BuildRequestDataFlags.None);
|
||||||
|
|
||||||
BuildStarted.InvokeParallelFireAndForget();
|
BuildStarted.InvokeParallelFireAndForget(buildStartedFlags);
|
||||||
var timer = Stopwatch.StartNew();
|
var timer = Stopwatch.StartNew();
|
||||||
var buildResult = await BuildManager.DefaultBuildManager.BuildAsync(buildParameters, buildRequest, _cancellationTokenSource.Token).ConfigureAwait(false);
|
var buildResult = await BuildManager.DefaultBuildManager.BuildAsync(buildParameters, buildRequest, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||||
timer.Stop();
|
timer.Stop();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using AsyncReadProcess;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages;
|
using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages;
|
||||||
using SharpIDE.Application.Features.Analysis;
|
using SharpIDE.Application.Features.Analysis;
|
||||||
|
using SharpIDE.Application.Features.Build;
|
||||||
using SharpIDE.Application.Features.Debugging;
|
using SharpIDE.Application.Features.Debugging;
|
||||||
using SharpIDE.Application.Features.Evaluation;
|
using SharpIDE.Application.Features.Evaluation;
|
||||||
using SharpIDE.Application.Features.Events;
|
using SharpIDE.Application.Features.Events;
|
||||||
@@ -14,13 +15,14 @@ using Breakpoint = SharpIDE.Application.Features.Debugging.Breakpoint;
|
|||||||
|
|
||||||
namespace SharpIDE.Application.Features.Run;
|
namespace SharpIDE.Application.Features.Run;
|
||||||
|
|
||||||
public partial class RunService(ILogger<RunService> logger, RoslynAnalysis roslynAnalysis)
|
public partial class RunService(ILogger<RunService> logger, RoslynAnalysis roslynAnalysis, BuildService buildService)
|
||||||
{
|
{
|
||||||
private readonly ConcurrentDictionary<SharpIdeProjectModel, SemaphoreSlim> _projectLocks = [];
|
private readonly ConcurrentDictionary<SharpIdeProjectModel, SemaphoreSlim> _projectLocks = [];
|
||||||
private Debugger? _debugger; // TODO: Support multiple debuggers for multiple running projects
|
private Debugger? _debugger; // TODO: Support multiple debuggers for multiple running projects
|
||||||
|
|
||||||
private readonly ILogger<RunService> _logger = logger;
|
private readonly ILogger<RunService> _logger = logger;
|
||||||
private readonly RoslynAnalysis _roslynAnalysis = roslynAnalysis;
|
private readonly RoslynAnalysis _roslynAnalysis = roslynAnalysis;
|
||||||
|
private readonly BuildService _buildService = buildService;
|
||||||
|
|
||||||
public async Task RunProject(SharpIdeProjectModel project, bool isDebug = false, DebuggerExecutableInfo? debuggerExecutableInfo = null)
|
public async Task RunProject(SharpIdeProjectModel project, bool isDebug = false, DebuggerExecutableInfo? debuggerExecutableInfo = null)
|
||||||
{
|
{
|
||||||
@@ -32,8 +34,10 @@ public partial class RunService(ILogger<RunService> logger, RoslynAnalysis rosly
|
|||||||
var waitResult = await semaphoreSlim.WaitAsync(0).ConfigureAwait(false);
|
var waitResult = await semaphoreSlim.WaitAsync(0).ConfigureAwait(false);
|
||||||
if (waitResult is false) throw new InvalidOperationException($"Project {project.Name} is already running.");
|
if (waitResult is false) throw new InvalidOperationException($"Project {project.Name} is already running.");
|
||||||
if (project.RunningCancellationTokenSource is not null) throw new InvalidOperationException($"Project {project.Name} is already running with a cancellation token source.");
|
if (project.RunningCancellationTokenSource is not null) throw new InvalidOperationException($"Project {project.Name} is already running with a cancellation token source.");
|
||||||
|
|
||||||
project.RunningCancellationTokenSource = new CancellationTokenSource();
|
project.RunningCancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
|
await _buildService.MsBuildAsync(project.FilePath);
|
||||||
|
|
||||||
var launchProfiles = await LaunchSettingsParser.GetLaunchSettingsProfiles(project);
|
var launchProfiles = await LaunchSettingsParser.GetLaunchSettingsProfiles(project);
|
||||||
var launchProfile = launchProfiles.FirstOrDefault();
|
var launchProfile = launchProfiles.FirstOrDefault();
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public partial class BuildPanel : Control
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnBuildStarted()
|
private async Task OnBuildStarted(BuildStartedFlags _)
|
||||||
{
|
{
|
||||||
await this.InvokeAsync(() => _terminal.Clear());
|
await this.InvokeAsync(() => _terminal.Clear());
|
||||||
_buildOutputChannelReader ??= _buildService.BuildTextWriter.ConsoleChannel.Reader;
|
_buildOutputChannelReader ??= _buildService.BuildTextWriter.ConsoleChannel.Reader;
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public partial class TestExplorerPanel : Control
|
|||||||
var solution = _solutionAccessor.SolutionModel!;
|
var solution = _solutionAccessor.SolutionModel!;
|
||||||
if (withBuild)
|
if (withBuild)
|
||||||
{
|
{
|
||||||
await _buildService.MsBuildAsync(solution.FilePath);
|
await _buildService.MsBuildAsync(solution.FilePath, buildStartedFlags: BuildStartedFlags.Internal);
|
||||||
}
|
}
|
||||||
var testNodes = await _testRunnerService.DiscoverTests(solution);
|
var testNodes = await _testRunnerService.DiscoverTests(solution);
|
||||||
var scenes = testNodes.Select(s =>
|
var scenes = testNodes.Select(s =>
|
||||||
@@ -70,7 +70,7 @@ public partial class TestExplorerPanel : Control
|
|||||||
{
|
{
|
||||||
await _solutionAccessor.SolutionReadyTcs.Task;
|
await _solutionAccessor.SolutionReadyTcs.Task;
|
||||||
var solution = _solutionAccessor.SolutionModel!;
|
var solution = _solutionAccessor.SolutionModel!;
|
||||||
await _buildService.MsBuildAsync(solution.FilePath);
|
await _buildService.MsBuildAsync(solution.FilePath, buildStartedFlags: BuildStartedFlags.Internal);
|
||||||
await this.InvokeAsync(() => _testNodesVBoxContainer.QueueFreeChildren());
|
await this.InvokeAsync(() => _testNodesVBoxContainer.QueueFreeChildren());
|
||||||
_testNodeEntryNodes.Clear();
|
_testNodeEntryNodes.Clear();
|
||||||
await _testRunnerService.RunTestsAsync(solution, HandleTestNodeUpdates);
|
await _testRunnerService.RunTestsAsync(solution, HandleTestNodeUpdates);
|
||||||
|
|||||||
@@ -98,10 +98,11 @@ public partial class IdeRoot : Control
|
|||||||
_nodeReadyTcs.SetResult();
|
_nodeReadyTcs.SetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnBuildStarted() => await OnBuildRunningStateChanged(true);
|
private async Task OnBuildStarted(BuildStartedFlags flags) => await OnBuildRunningStateChanged(true, flags);
|
||||||
private async Task OnBuildFinished() => await OnBuildRunningStateChanged(false);
|
private async Task OnBuildFinished() => await OnBuildRunningStateChanged(false);
|
||||||
private async Task OnBuildRunningStateChanged(bool running)
|
private async Task OnBuildRunningStateChanged(bool running, BuildStartedFlags? flags = null)
|
||||||
{
|
{
|
||||||
|
if (running && flags is BuildStartedFlags.UserFacing) GodotGlobalEvents.Instance.BottomPanelTabExternallySelected.InvokeParallelFireAndForget(BottomPanelType.Build);
|
||||||
await this.InvokeAsync(() =>
|
await this.InvokeAsync(() =>
|
||||||
{
|
{
|
||||||
_cancelMsBuildActionButton.Disabled = !running;
|
_cancelMsBuildActionButton.Disabled = !running;
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ClearPreviousOutput()
|
private async Task ClearPreviousOutput(BuildStartedFlags _)
|
||||||
{
|
{
|
||||||
await _terminalDisplayRef.Clear();
|
await _terminalDisplayRef.Clear();
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|||||||
@@ -197,7 +197,7 @@
|
|||||||
{
|
{
|
||||||
if (AppState.IdeSettings.OpenTerminalOnBuildRebuildRestore) SelectBottomPanel(BottomPanelType.Build);
|
if (AppState.IdeSettings.OpenTerminalOnBuildRebuildRestore) SelectBottomPanel(BottomPanelType.Build);
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
await BuildService.MsBuildAsync(_solutionFilePath!, buildType, _cancellationTokenSource.Token);
|
await BuildService.MsBuildAsync(_solutionFilePath!, buildType, BuildStartedFlags.UserFacing, _cancellationTokenSource.Token);
|
||||||
_cancellationTokenSource = null;
|
_cancellationTokenSource = null;
|
||||||
}
|
}
|
||||||
private async Task CancelBuild() => await _cancellationTokenSource!.CancelAsync();
|
private async Task CancelBuild() => await _cancellationTokenSource!.CancelAsync();
|
||||||
|
|||||||
Reference in New Issue
Block a user