-
Notifications
You must be signed in to change notification settings - Fork 256
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Component Debugger - See #726 * Add command line data source: - add data source to obtain command line args - use data source in extension method - extract out shared logic into launchsettingsmanager - clean up VM code * Remove capability provider - Use targets directly instead
- Loading branch information
Showing
13 changed files
with
513 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
src/VisualStudio.Roslyn.SDK/ComponentDebugger/CommandLineArgumentsDataSource.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// 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 file in the project root for more information. | ||
|
||
using System; | ||
using System.Collections.Immutable; | ||
using System.ComponentModel.Composition; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using System.Threading.Tasks.Dataflow; | ||
using Microsoft.VisualStudio.ProjectSystem; | ||
using Microsoft.VisualStudio.ProjectSystem.VS; | ||
|
||
namespace Roslyn.ComponentDebugger | ||
{ | ||
[Export] | ||
[AppliesTo("(" + ProjectCapabilities.CSharp + " | " + ProjectCapabilities.VB + ") & !" + ProjectCapabilities.SharedAssetsProject)] | ||
public class CommandLineArgumentsDataSource : UnconfiguredProjectHostBridge<IProjectVersionedValue<IProjectSubscriptionUpdate>, IProjectVersionedValue<ImmutableArray<string>>, IProjectVersionedValue<ImmutableArray<string>>> | ||
{ | ||
private readonly IActiveConfiguredProjectSubscriptionService _activeProjectSubscriptionService; | ||
|
||
[ImportingConstructor] | ||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "MEF ensures not null")] | ||
public CommandLineArgumentsDataSource(IProjectThreadingService projectThreadingService, IActiveConfiguredProjectSubscriptionService activeProjectSubscriptionService) | ||
: base(projectThreadingService.JoinableTaskContext) | ||
{ | ||
_activeProjectSubscriptionService = activeProjectSubscriptionService; | ||
} | ||
|
||
public async Task<ImmutableArray<string>> GetArgsAsync() | ||
{ | ||
using (JoinableCollection.Join()) | ||
{ | ||
await this.InitializeAsync().ConfigureAwait(true); | ||
return this.AppliedValue?.Value ?? ImmutableArray<string>.Empty; | ||
} | ||
} | ||
|
||
protected override bool BlockInitializeOnFirstAppliedValue => true; | ||
|
||
protected override Task InitializeInnerCoreAsync(CancellationToken cancellationToken) => Task.CompletedTask; | ||
|
||
protected override IDisposable LinkExternalInput(ITargetBlock<IProjectVersionedValue<IProjectSubscriptionUpdate>> targetBlock) | ||
{ | ||
JoinUpstreamDataSources(_activeProjectSubscriptionService.ProjectBuildRuleSource); | ||
return _activeProjectSubscriptionService.ProjectBuildRuleSource.SourceBlock.LinkTo(target: targetBlock, | ||
linkOptions: new DataflowLinkOptions { PropagateCompletion = true }, | ||
initialDataAsNew: true, | ||
suppressVersionOnlyUpdates: true, | ||
ruleNames: Constants.CommandLineArgsRuleName); | ||
} | ||
|
||
protected override Task<IProjectVersionedValue<ImmutableArray<string>>> PreprocessAsync(IProjectVersionedValue<IProjectSubscriptionUpdate> input, IProjectVersionedValue<ImmutableArray<string>>? previousOutput) | ||
{ | ||
if (input is null) | ||
{ | ||
throw new ArgumentNullException(nameof(input)); | ||
} | ||
|
||
var description = input.Value.ProjectChanges[Constants.CommandLineArgsRuleName]; | ||
return Task.FromResult<IProjectVersionedValue<ImmutableArray<string>>>(new ProjectVersionedValue<ImmutableArray<string>>(description.After.Items.Keys.ToImmutableArray(), input.DataSourceVersions)); | ||
} | ||
|
||
protected override Task ApplyAsync(IProjectVersionedValue<ImmutableArray<string>> value) | ||
{ | ||
AppliedValue = value; | ||
return Task.CompletedTask; | ||
} | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
src/VisualStudio.Roslyn.SDK/ComponentDebugger/Constants.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// 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 file in the project root for more information. | ||
|
||
namespace Roslyn.ComponentDebugger | ||
{ | ||
internal static class Constants | ||
{ | ||
public const string RoslynComponentCapability = "RoslynComponent"; | ||
|
||
public const string CommandName = "DebugRoslynComponent"; | ||
|
||
public const string TargetProjectKeyName = "targetProject"; | ||
|
||
public const string CommandLineArgsRuleName = "CompilerCommandLineArgs"; | ||
} | ||
} |
97 changes: 97 additions & 0 deletions
97
src/VisualStudio.Roslyn.SDK/ComponentDebugger/DebugProfileProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
// 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 file in the project root for more information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.ComponentModel.Composition; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Microsoft.VisualStudio; | ||
using Microsoft.VisualStudio.ProjectSystem; | ||
using Microsoft.VisualStudio.ProjectSystem.Debug; | ||
using Microsoft.VisualStudio.ProjectSystem.VS.Debug; | ||
using Microsoft.VisualStudio.Shell; | ||
using Microsoft.VisualStudio.Shell.Interop; | ||
using Microsoft.VisualStudio.Threading; | ||
using Task = System.Threading.Tasks.Task; | ||
|
||
namespace Roslyn.ComponentDebugger | ||
{ | ||
[Export(typeof(IDebugProfileLaunchTargetsProvider))] | ||
[AppliesTo(Constants.RoslynComponentCapability)] | ||
public class DebugProfileProvider : IDebugProfileLaunchTargetsProvider | ||
{ | ||
private readonly ConfiguredProject _configuredProject; | ||
private readonly LaunchSettingsManager _launchSettingsManager; | ||
private readonly IProjectThreadingService _threadingService; | ||
private readonly AsyncLazy<string?> _compilerRoot; | ||
|
||
[ImportingConstructor] | ||
[Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] | ||
public DebugProfileProvider(ConfiguredProject configuredProject, LaunchSettingsManager launchSettingsManager, SVsServiceProvider? serviceProvider, IProjectThreadingService threadingService) | ||
{ | ||
_configuredProject = configuredProject; | ||
_launchSettingsManager = launchSettingsManager; | ||
_threadingService = threadingService; | ||
|
||
_compilerRoot = new AsyncLazy<string?>(() => GetCompilerRootAsync(serviceProvider), _threadingService.JoinableTaskFactory); | ||
} | ||
|
||
public Task OnAfterLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile) => Task.CompletedTask; | ||
|
||
public Task OnBeforeLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile) => Task.CompletedTask; | ||
|
||
public bool SupportsProfile(ILaunchProfile? profile) => Constants.CommandName.Equals(profile?.CommandName, StringComparison.Ordinal); | ||
|
||
public async Task<IReadOnlyList<IDebugLaunchSettings>> QueryDebugTargetsAsync(DebugLaunchOptions launchOptions, ILaunchProfile? profile) | ||
{ | ||
// set up the managed (net fx) debugger to start a process | ||
// https://github.com/dotnet/roslyn-sdk/issues/729 | ||
var settings = new DebugLaunchSettings(launchOptions) | ||
{ | ||
LaunchDebugEngineGuid = Microsoft.VisualStudio.ProjectSystem.Debug.DebuggerEngines.ManagedOnlyEngine, | ||
LaunchOperation = DebugLaunchOperation.CreateProcess | ||
}; | ||
|
||
var compilerRoot = await _compilerRoot.GetValueAsync().ConfigureAwait(true); | ||
if (compilerRoot is object) | ||
{ | ||
// try and get the target project | ||
var targetProjectUnconfigured = await _launchSettingsManager.TryGetProjectForLaunchAsync(profile).ConfigureAwait(true); | ||
if (targetProjectUnconfigured is object) | ||
{ | ||
settings.CurrentDirectory = Path.GetDirectoryName(targetProjectUnconfigured.FullPath); | ||
var compiler = _configuredProject.Capabilities.Contains(ProjectCapabilities.VB) ? "vbc.exe" : "csc.exe"; | ||
settings.Executable = Path.Combine(compilerRoot, compiler); | ||
|
||
// get its compilation args | ||
var args = await targetProjectUnconfigured.GetCompilationArgumentsAsync().ConfigureAwait(true); | ||
|
||
// append the command line args to the debugger launch | ||
settings.Arguments = string.Join(" ", args); | ||
} | ||
} | ||
// https://github.com/dotnet/roslyn-sdk/issues/728 : better error handling | ||
return new IDebugLaunchSettings[] { settings }; | ||
} | ||
|
||
private async Task<string?> GetCompilerRootAsync(SVsServiceProvider? serviceProvider) | ||
{ | ||
await _threadingService.SwitchToUIThread(); | ||
|
||
// https://github.com/dotnet/roslyn-sdk/issues/729 : don't hardcode net fx compiler | ||
var shell = (IVsShell?)serviceProvider?.GetService(typeof(SVsShell)); | ||
if (shell is object | ||
&& shell.GetProperty((int)__VSSPROPID2.VSSPROPID_InstallRootDir, out var rootDirObj) == VSConstants.S_OK | ||
&& rootDirObj is string rootDir) | ||
{ | ||
return Path.Combine(rootDir, "MSBuild", "Current", "Bin", "Roslyn"); | ||
} | ||
|
||
return null; | ||
} | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
src/VisualStudio.Roslyn.SDK/ComponentDebugger/DebuggerOptions.xaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<UserControl x:Class="Roslyn.ComponentDebugger.DebuggerOptions" | ||
x:ClassModifier="internal" | ||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | ||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | ||
Width="Auto" | ||
HorizontalAlignment="Left" | ||
VerticalAlignment="Top"> | ||
<Grid Margin="0,2,0,0"> | ||
<Grid.ColumnDefinitions> | ||
<ColumnDefinition Width="158" /> | ||
<ColumnDefinition Width="350" /> | ||
<ColumnDefinition Width="Auto" /> | ||
</Grid.ColumnDefinitions> | ||
|
||
<!-- https://github.com/dotnet/roslyn-sdk/issues/730 : Localization --> | ||
<Label Margin="4,4,3,5">Target Project:</Label> | ||
<ComboBox Grid.Column="1" Margin="5,7,2,6" ItemsSource="{Binding ProjectNames}" SelectedIndex="{Binding SelectedProjectIndex, Mode=TwoWay}" /> | ||
</Grid> | ||
</UserControl> |
61 changes: 61 additions & 0 deletions
61
src/VisualStudio.Roslyn.SDK/ComponentDebugger/DebuggerOptionsViewModel.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// 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 file in the project root for more information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.ComponentModel; | ||
using System.Linq; | ||
using System.Runtime.CompilerServices; | ||
|
||
namespace Roslyn.ComponentDebugger | ||
{ | ||
internal sealed class DebuggerOptionsViewModel : INotifyPropertyChanged | ||
{ | ||
private readonly Action<int> _indexChanged; | ||
|
||
private IEnumerable<string> _projectNames = ImmutableArray<string>.Empty; | ||
|
||
private int _selectedProjectIndex = -1; | ||
|
||
public event PropertyChangedEventHandler? PropertyChanged; | ||
|
||
public DebuggerOptionsViewModel(Action<int> indexChanged) | ||
{ | ||
_indexChanged = indexChanged; | ||
} | ||
|
||
public IEnumerable<string> ProjectNames | ||
{ | ||
get => _projectNames; | ||
set | ||
{ | ||
if (!_projectNames.SequenceEqual(value)) | ||
{ | ||
_projectNames = value; | ||
NotifyPropertyChanged(); | ||
} | ||
} | ||
} | ||
|
||
public int SelectedProjectIndex | ||
{ | ||
get => _selectedProjectIndex; | ||
set | ||
{ | ||
if (_selectedProjectIndex != value) | ||
{ | ||
_selectedProjectIndex = value; | ||
NotifyPropertyChanged(); | ||
_indexChanged?.Invoke(value); | ||
} | ||
} | ||
} | ||
|
||
private void NotifyPropertyChanged([CallerMemberName]string propertyName = "") | ||
{ | ||
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); | ||
} | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
src/VisualStudio.Roslyn.SDK/ComponentDebugger/LaunchSettingsManager.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// 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 file in the project root for more information. | ||
|
||
using System.ComponentModel.Composition; | ||
using System.Threading.Tasks; | ||
using Microsoft.VisualStudio.ProjectSystem; | ||
using Microsoft.VisualStudio.ProjectSystem.Debug; | ||
|
||
namespace Roslyn.ComponentDebugger | ||
{ | ||
[Export] | ||
public class LaunchSettingsManager | ||
{ | ||
private readonly UnconfiguredProject _owningProject; | ||
private readonly IDebugTokenReplacer _tokenReplacer; | ||
|
||
[ImportingConstructor] | ||
public LaunchSettingsManager(UnconfiguredProject owningProject, IDebugTokenReplacer tokenReplacer) | ||
{ | ||
_owningProject = owningProject; | ||
_tokenReplacer = tokenReplacer; | ||
} | ||
|
||
public async Task<UnconfiguredProject?> TryGetProjectForLaunchAsync(ILaunchProfile? profile) | ||
{ | ||
UnconfiguredProject? targetProject = null; | ||
object? value = null; | ||
profile?.OtherSettings?.TryGetValue(Constants.TargetProjectKeyName, out value); | ||
|
||
if (value is string targetProjectPath) | ||
{ | ||
// expand any variables in the path, and root it based on this project | ||
var replacedProjectPath = await _tokenReplacer.ReplaceTokensInStringAsync(targetProjectPath, true).ConfigureAwait(true); | ||
replacedProjectPath = _owningProject.MakeRooted(replacedProjectPath); | ||
|
||
targetProject = ((IProjectService2)_owningProject.Services.ProjectService).GetLoadedProject(replacedProjectPath); | ||
} | ||
return targetProject; | ||
} | ||
|
||
public void WriteProjectForLaunch(IWritableLaunchProfile profile, UnconfiguredProject targetProject) | ||
{ | ||
if (profile is null) | ||
{ | ||
throw new System.ArgumentNullException(nameof(profile)); | ||
} | ||
|
||
if (targetProject is null) | ||
{ | ||
throw new System.ArgumentNullException(nameof(targetProject)); | ||
} | ||
|
||
var rootedPath = _owningProject.MakeRelative(targetProject.FullPath); | ||
profile.OtherSettings[Constants.TargetProjectKeyName] = rootedPath; | ||
} | ||
|
||
} | ||
} |
Oops, something went wrong.