Skip to content

Commit

Permalink
Merge pull request #9555 from LittleLittleCloud/u/implementV2
Browse files Browse the repository at this point in the history
Implement IManagedHotReloadAgent2 and IManagedHotReloadAgent4 for ProjectHotReloadSession
  • Loading branch information
LittleLittleCloud authored Nov 14, 2024
2 parents da8052d + e89e342 commit c1539d2
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 14 deletions.
3 changes: 3 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<VSDebuggerVersion>17.13.0-beta.24561.1</VSDebuggerVersion>
</PropertyGroup>

<!--
Expand Down Expand Up @@ -44,6 +45,8 @@
<PackageVersion Include="Microsoft.ServiceHub.Framework" Version="4.7.37" />
<PackageVersion Include="Microsoft.VisualStudio.ComponentModelHost" Version="17.13.13-preview" />
<PackageVersion Include="Microsoft.VisualStudio.Composition" Version="17.12.18" />
<PackageVersion Include="Microsoft.VisualStudio.Debugger.Contracts" Version="$(VSDebuggerVersion)" />
<PackageVersion Include="Microsoft.VisualStudio.Debugger.UI.Interfaces" Version="$(VSDebuggerVersion)" />
<PackageVersion Include="Microsoft.VisualStudio.Data.Core" Version="17.13.38047-preview.1" />
<PackageVersion Include="Microsoft.VisualStudio.Data.Services" Version="17.13.38055-preview.1" />
<PackageVersion Include="Microsoft.VisualStudio.DataDesign.Common" Version="17.13.38055-preview.1" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE.md file in the project root for more information.

using System.Diagnostics;
using Microsoft.VisualStudio.HotReload.Components.DeltaApplier;

namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload
Expand All @@ -16,4 +17,15 @@ public interface IProjectHotReloadSessionCallback

IDeltaApplier? GetDeltaApplier();
}

internal interface IProjectHotReloadSessionCallback2 : IProjectHotReloadSessionCallback
{
UnconfiguredProject? Project { get; }

Process? Process { get; }

IProjectHotReloadSession? Session { get; }

Task<bool> RestartProjectAsync(bool isRunningUnderDebug, CancellationToken cancellationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

using Microsoft.VisualStudio.Debugger.Contracts.HotReload;
using Microsoft.VisualStudio.HotReload.Components.DeltaApplier;
using static Microsoft.VisualStudio.ProjectSystem.VS.HotReload.ProjectHotReloadSessionManager;

namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload
{
internal class ProjectHotReloadSession : IManagedHotReloadAgent, IProjectHotReloadSession, IProjectHotReloadSessionInternal
internal class ProjectHotReloadSession : IManagedHotReloadAgent, IManagedHotReloadAgent2, IManagedHotReloadAgent4, IProjectHotReloadSession, IProjectHotReloadSessionInternal
{
private readonly string _variant;
private readonly string _runtimeVersion;
Expand All @@ -15,6 +16,9 @@ internal class ProjectHotReloadSession : IManagedHotReloadAgent, IProjectHotRelo
private readonly IProjectHotReloadSessionCallback _callback;

private bool _sessionActive;

// This flag is used to identify Debug|NonDebug cases
private bool _isRunningUnderDebugger;
private IDeltaApplier? _deltaApplier;

public ProjectHotReloadSession(
Expand Down Expand Up @@ -99,6 +103,7 @@ public async Task StartSessionAsync(bool runningUnderDebugger, CancellationToken
),
default);
_sessionActive = true;
_isRunningUnderDebugger = runningUnderDebugger;
EnsureDeltaApplierforSession();
}

Expand All @@ -107,7 +112,6 @@ public async Task StopSessionAsync(CancellationToken cancellationToken)
if (_sessionActive)
{
_sessionActive = false;

await _hotReloadAgentManagerClient.Value.AgentTerminatedAsync(this, cancellationToken);
WriteToOutputWindow(
new HotReloadLogMessage(
Expand Down Expand Up @@ -248,10 +252,15 @@ public async ValueTask RestartAsync(CancellationToken cancellationToken)
),
cancellationToken);

await _callback.RestartProjectAsync(cancellationToken);

// TODO: Should we stop the session here? Or does someone else do it?
// TODO: Should we handle rebuilding here? Or do we expect the callback to handle it?
if (_callback is IProjectHotReloadSessionCallback2 callBack2)
{
await callBack2.RestartProjectAsync(_isRunningUnderDebugger, cancellationToken);
}
else
{
await _callback.RestartProjectAsync(cancellationToken);
}
}

public async ValueTask StopAsync(CancellationToken cancellationToken)
Expand Down Expand Up @@ -288,5 +297,25 @@ private void EnsureDeltaApplierforSession()
?? _deltaApplierCreator.Value.CreateManagedDeltaApplier(_runtimeVersion);
}
}

public ValueTask<int?> GetTargetLocalProcessIdAsync(CancellationToken cancellationToken)
{
if (_callback is IProjectHotReloadSessionCallback2 callback2)
{
return new ValueTask<int?>(callback2.Process?.Id);
}

return new ValueTask<int?>();
}

public ValueTask<string?> GetProjectFullPathAsync(CancellationToken cancellationToken)
{
if (_callback is IProjectHotReloadSessionCallback2 callback2)
{
return new ValueTask<string?>(callback2.Project?.FullPath);
}

return new ValueTask<string?>();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE.md file in the project root for more information.

using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Debugger.Contracts.HotReload;
using Microsoft.VisualStudio.HotReload.Components.DeltaApplier;
using Microsoft.VisualStudio.ProjectSystem.Debug;
using Microsoft.VisualStudio.ProjectSystem.Properties;
using Microsoft.VisualStudio.ProjectSystem.VS.Build;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Threading;

namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload
Expand All @@ -19,6 +23,9 @@ internal class ProjectHotReloadSessionManager : OnceInitializedOnceDisposedAsync
private readonly Lazy<IProjectHotReloadAgent> _projectHotReloadAgent;
private readonly Lazy<IHotReloadDiagnosticOutputService> _hotReloadDiagnosticOutputService;
private readonly Lazy<IProjectHotReloadNotificationService> _projectHotReloadNotificationService;
private readonly IVsService<SVsSolutionBuildManager, IVsSolutionBuildManager2> _vsSolutionBuildManagerService;
private IVsSolutionBuildManager2? _vsSolutionBuildManager2;
private readonly IProjectThreadingService _projectThreadingService;

// Protect the state from concurrent access. For example, our Process.Exited event
// handler may run on one thread while we're still setting up the session on
Expand All @@ -39,15 +46,18 @@ public ProjectHotReloadSessionManager(
IActiveDebugFrameworkServices activeDebugFrameworkServices,
Lazy<IProjectHotReloadAgent> projectHotReloadAgent,
Lazy<IHotReloadDiagnosticOutputService> hotReloadDiagnosticOutputService,
Lazy<IProjectHotReloadNotificationService> projectHotReloadNotificationService)
Lazy<IProjectHotReloadNotificationService> projectHotReloadNotificationService,
IVsService<SVsSolutionBuildManager, IVsSolutionBuildManager2> solutionBuildManagerService)
: base(threadingService.JoinableTaskContext)
{
_project = project;
_projectThreadingService = threadingService;
_projectFaultHandlerService = projectFaultHandlerService;
_activeDebugFrameworkServices = activeDebugFrameworkServices;
_projectHotReloadAgent = projectHotReloadAgent;
_hotReloadDiagnosticOutputService = hotReloadDiagnosticOutputService;
_projectHotReloadNotificationService = projectHotReloadNotificationService;
_vsSolutionBuildManagerService = solutionBuildManagerService;

_semaphore = ReentrantSemaphore.Create(
initialCount: 1,
Expand Down Expand Up @@ -352,10 +362,36 @@ private async Task OnProcessExitedAsync(HotReloadState hotReloadState)
return null;
}

private static Task<bool> RestartProjectAsync(HotReloadState hotReloadState, CancellationToken cancellationToken)
private async Task<bool> RestartProjectAsync(
HotReloadState hotReloadState,
bool isRunningUnderDebug,
CancellationToken cancellationToken)
{
// TODO: Support restarting the project.
return TaskResult.False;
Assumes.NotNull(_project.Services.HostObject);
await _projectThreadingService.SwitchToUIThread();

if (_vsSolutionBuildManager2 is null)
{
_vsSolutionBuildManager2 = await _vsSolutionBuildManagerService.GetValueAsync(cancellationToken);
}

// Step 1: Debug or NonDebug?
uint dbgLaunchFlag = isRunningUnderDebug ? (uint)VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_LAUNCHDEBUG : (uint)VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_LAUNCH;

// Step 2: Build and Launch Debug
var projectVsHierarchy = (IVsHierarchy)_project.Services.HostObject;

var result = _vsSolutionBuildManager2.StartSimpleUpdateProjectConfiguration(
pIVsHierarchyToBuild: projectVsHierarchy,
pIVsHierarchyDependent: null,
pszDependentConfigurationCanonicalName: null,
dwFlags: (uint)VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_BUILD | dbgLaunchFlag,
dwDefQueryResults: (uint)VSSOLNBUILDQUERYRESULTS.VSSBQR_SAVEBEFOREBUILD_QUERY_YES,
fSuppressUI: 0);

ErrorHandler.ThrowOnFailure(result);

return result == HResult.OK;
}

private static Task OnAfterChangesAppliedAsync(HotReloadState hotReloadState, CancellationToken cancellationToken)
Expand Down Expand Up @@ -449,15 +485,16 @@ async Task ApplyHotReloadUpdateInternalAsync()
}
}

private class HotReloadState : IProjectHotReloadSessionCallback
private class HotReloadState : IProjectHotReloadSessionCallback2
{
private readonly ProjectHotReloadSessionManager _sessionManager;

public Process? Process { get; set; }
public IProjectHotReloadSession? Session { get; set; }

// TODO: Support restarting the session.
public bool SupportsRestart => false;
public bool SupportsRestart => true;

public UnconfiguredProject? Project => _sessionManager._project;

public HotReloadState(ProjectHotReloadSessionManager sessionManager)
{
Expand All @@ -481,7 +518,12 @@ public Task<bool> StopProjectAsync(CancellationToken cancellationToken)

public Task<bool> RestartProjectAsync(CancellationToken cancellationToken)
{
return ProjectHotReloadSessionManager.RestartProjectAsync(this, cancellationToken);
return TaskResult.False;
}

public Task<bool> RestartProjectAsync(bool isRunningUnderDebug, CancellationToken cancellationToken)
{
return _sessionManager.RestartProjectAsync(this, isRunningUnderDebug, cancellationToken);
}

public IDeltaApplier? GetDeltaApplier()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE.md file in the project root for more information.

using Microsoft.VisualStudio.ProjectSystem.Debug;
using Microsoft.VisualStudio.ProjectSystem.VS.Build;
using Microsoft.VisualStudio.Shell.Interop;

namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload
{
Expand Down Expand Up @@ -169,14 +171,17 @@ private static ProjectHotReloadSessionManager CreateHotReloadSessionManager(Conf
.ImplementGetConfiguredProjectForActiveFrameworkAsync(activeConfiguredProject)
.Object;

var iVSSolutionBuildManagerServiceMock = new Mock<IVsService<SVsSolutionBuildManager, IVsSolutionBuildManager2>>();

var manager = new ProjectHotReloadSessionManager(
UnconfiguredProjectFactory.Create(),
IProjectThreadingServiceFactory.Create(),
IProjectFaultHandlerServiceFactory.Create(),
activeDebugFrameworkServices,
new Lazy<IProjectHotReloadAgent>(() => IProjectHotReloadAgentFactory.Create()),
new Lazy<IHotReloadDiagnosticOutputService>(() => IHotReloadDiagnosticOutputServiceFactory.Create(outputServiceCallback)),
new Lazy<IProjectHotReloadNotificationService>(() => IProjectHotReloadNotificationServiceFactory.Create()));
new Lazy<IProjectHotReloadNotificationService>(() => IProjectHotReloadNotificationServiceFactory.Create()),
iVSSolutionBuildManagerServiceMock.Object);

return manager;
}
Expand Down

0 comments on commit c1539d2

Please sign in to comment.