-
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.
Merge pull request #726 from chsienki/ComponentDebugger
- Loading branch information
Showing
14 changed files
with
473 additions
and
1 deletion.
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
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
43 changes: 43 additions & 0 deletions
43
src/VisualStudio.Roslyn.SDK/ComponentDebugger/CapabilityProvider.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,43 @@ | ||
// 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.Collections.Immutable; | ||
using System.ComponentModel.Composition; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.VisualStudio.ProjectSystem; | ||
|
||
namespace Roslyn.ComponentDebugger | ||
{ | ||
[Export(ExportContractNames.Scopes.ConfiguredProject, typeof(IProjectCapabilitiesProvider))] | ||
[AppliesTo(ProjectCapabilities.CSharp + " | " + ProjectCapabilities.VB)] | ||
public class CapabilityProvider : ConfiguredProjectCapabilitiesProviderBase | ||
{ | ||
private readonly IProjectSnapshotService snapshotService; | ||
|
||
[ImportingConstructor] | ||
[System.Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] | ||
public CapabilityProvider(ConfiguredProject configuredProject, IProjectSnapshotService snapshotService) | ||
: base(nameof(CapabilityProvider), configuredProject) | ||
{ | ||
this.snapshotService = snapshotService; | ||
} | ||
|
||
protected override async Task<ImmutableHashSet<string>> GetCapabilitiesAsync(CancellationToken cancellationToken) | ||
{ | ||
// an alternative design could be to have 'IsRoslynComponent' just define the <Capability... directly in the managed.core targets | ||
// but that would require a specific roslyn version to work, this allows it to be backwards compatible with older SDKs | ||
var caps = Empty.CapabilitiesSet; | ||
|
||
var snapshot = await snapshotService.GetLatestVersionAsync(ConfiguredProject, cancellationToken: cancellationToken).ConfigureAwait(false); | ||
var isRoslynComponentProperty = snapshot.Value.ProjectInstance.GetPropertyValue(Constants.RoslynComponentPropertyName); | ||
var isComponent = string.Compare(isRoslynComponentProperty.Trim(), "true", System.StringComparison.OrdinalIgnoreCase) == 0; | ||
if (isComponent) | ||
{ | ||
caps = caps.Add(Constants.RoslynComponentCapability); | ||
} | ||
return caps; | ||
} | ||
} | ||
} |
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 RoslynComponentPropertyName = "IsRoslynComponent"; | ||
|
||
public const string RoslynComponentCapability = "RoslynComponent"; | ||
|
||
public const string CommandName = "DebugRoslynComponent"; | ||
|
||
public const string TargetProjectPropertyName = "targetProject"; | ||
} | ||
} |
109 changes: 109 additions & 0 deletions
109
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,109 @@ | ||
// 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.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 IDebugTokenReplacer _tokenReplacer; | ||
private readonly AsyncLazy<string> _compilerRoot; | ||
|
||
[ImportingConstructor] | ||
[Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] | ||
public DebugProfileProvider(ConfiguredProject configuredProject, IDebugTokenReplacer tokenReplacer, SVsServiceProvider? serviceProvider) | ||
{ | ||
_configuredProject = configuredProject; | ||
_tokenReplacer = tokenReplacer; | ||
_compilerRoot = new AsyncLazy<string>(() => GetCompilerRootAsync(serviceProvider), ThreadHelper.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 | ||
}; | ||
|
||
// try and get the target project | ||
var targetProjectUnconfigured = await TryGetTargetProjectAsync(profile).ConfigureAwait(false); | ||
if (targetProjectUnconfigured is object) | ||
{ | ||
settings.CurrentDirectory = Path.GetDirectoryName(targetProjectUnconfigured.FullPath); | ||
var compiler = _configuredProject.Capabilities.Contains(ProjectCapabilities.VB) ? "vbc.exe" : "csc.exe"; | ||
var compilerRoot = await _compilerRoot.GetValueAsync().ConfigureAwait(false); | ||
settings.Executable = Path.Combine(compilerRoot, compiler); | ||
|
||
// try and get the configured version of the target project | ||
var targetProject = await targetProjectUnconfigured.GetSuggestedConfiguredProjectAsync().ConfigureAwait(false); | ||
if (targetProject is object) | ||
{ | ||
// get its compilation args | ||
var args = await targetProject.GetCompilationArgumentsAsync().ConfigureAwait(false); | ||
|
||
// 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 static async Task<string> GetCompilerRootAsync(SVsServiceProvider? serviceProvider) | ||
{ | ||
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); | ||
|
||
// https://github.com/dotnet/roslyn-sdk/issues/729 | ||
object rootDir = string.Empty; | ||
var shell = (IVsShell?)serviceProvider?.GetService(typeof(SVsShell)); | ||
shell?.GetProperty((int)__VSSPROPID2.VSSPROPID_InstallRootDir, out rootDir); | ||
return Path.Combine((string)rootDir, "MSBuild", "Current", "Bin", "Roslyn"); | ||
} | ||
|
||
private async Task<UnconfiguredProject?> TryGetTargetProjectAsync(ILaunchProfile? profile) | ||
{ | ||
UnconfiguredProject? targetProject = null; | ||
object? value = null; | ||
profile?.OtherSettings?.TryGetValue(Constants.TargetProjectPropertyName, 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(false); | ||
replacedProjectPath = _configuredProject.UnconfiguredProject.MakeRooted(replacedProjectPath); | ||
|
||
targetProject = _configuredProject.Services.ProjectService.LoadedUnconfiguredProjects.SingleOrDefault(p => p.FullPath == replacedProjectPath); | ||
} | ||
|
||
return targetProject; | ||
} | ||
} | ||
} |
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> |
19 changes: 19 additions & 0 deletions
19
src/VisualStudio.Roslyn.SDK/ComponentDebugger/DebuggerOptions.xaml.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,19 @@ | ||
// 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.Windows.Controls; | ||
|
||
namespace Roslyn.ComponentDebugger | ||
{ | ||
/// <summary> | ||
/// Interaction logic for DebuggerOptions.xaml | ||
/// </summary> | ||
internal sealed partial class DebuggerOptions : UserControl | ||
{ | ||
public DebuggerOptions() | ||
{ | ||
InitializeComponent(); | ||
} | ||
} | ||
} |
74 changes: 74 additions & 0 deletions
74
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,74 @@ | ||
// 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.IO; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using Microsoft.VisualStudio.ProjectSystem; | ||
using Microsoft.VisualStudio.ProjectSystem.Debug; | ||
|
||
namespace Roslyn.ComponentDebugger | ||
{ | ||
internal class DebuggerOptionsViewModel : INotifyPropertyChanged | ||
{ | ||
private IWritableLaunchProfile? _launchProfile; | ||
|
||
private readonly ImmutableArray<ConfiguredProject> _targetProjects; | ||
|
||
private readonly IEnumerable<string> _targetProjectNames; | ||
|
||
public event PropertyChangedEventHandler? PropertyChanged; | ||
|
||
public DebuggerOptionsViewModel(ImmutableArray<ConfiguredProject> targetProjects) | ||
{ | ||
_targetProjects = targetProjects; | ||
_targetProjectNames = _targetProjects.Select(t => Path.GetFileNameWithoutExtension(t.UnconfiguredProject.FullPath)); | ||
} | ||
|
||
public IEnumerable<string> ProjectNames { get => _targetProjectNames; } | ||
|
||
public IWritableLaunchProfile? LaunchProfile | ||
{ | ||
get => _launchProfile; | ||
set | ||
{ | ||
_launchProfile = value; | ||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedProjectIndex))); | ||
} | ||
} | ||
|
||
public int SelectedProjectIndex | ||
{ | ||
get | ||
{ | ||
if (LaunchProfile?.OtherSettings.ContainsKey(Constants.TargetProjectPropertyName) == true) | ||
{ | ||
var target = LaunchProfile.OtherSettings[Constants.TargetProjectPropertyName].ToString(); | ||
for (var i = 0; i < _targetProjects.Length; i++) | ||
{ | ||
if (_targetProjects[i].UnconfiguredProject.FullPath.Equals(target, StringComparison.OrdinalIgnoreCase)) | ||
{ | ||
return i; | ||
} | ||
} | ||
} | ||
return -1; | ||
} | ||
set | ||
{ | ||
if (LaunchProfile is object) | ||
{ | ||
var newTargetProject = _targetProjects[value].UnconfiguredProject; | ||
LaunchProfile.OtherSettings[Constants.TargetProjectPropertyName] = newTargetProject.FullPath; | ||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedProjectIndex))); | ||
} | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.