-
Notifications
You must be signed in to change notification settings - Fork 396
Enable "Run Code Analysis" commands to run FxCop for managed projects #1230
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Threading.Tasks; | ||
using Microsoft.VisualStudio.Packaging; | ||
using Microsoft.VisualStudio.ProjectSystem.Build; | ||
using Microsoft.VisualStudio.ProjectSystem.Input; | ||
using Microsoft.VisualStudio.Shell; | ||
using Microsoft.VisualStudio.Shell.Interop; | ||
using IOleCommandTarget = Microsoft.VisualStudio.OLE.Interop.IOleCommandTarget; | ||
using Task = System.Threading.Tasks.Task; | ||
|
||
namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands | ||
{ | ||
internal abstract class AbstractRunCodeAnalysisCommand : AbstractSingleNodeProjectCommand, IVsUpdateSolutionEvents, IDisposable | ||
{ | ||
private readonly IProjectThreadingService _threadingService; | ||
private readonly IServiceProvider _serviceProvider; | ||
private readonly RunCodeAnalysisBuildPropertyProvider _runCodeAnalysisBuildPropertyProvider; | ||
|
||
private IVsSolutionBuildManager2 _buildManager; | ||
private uint _solutionEventsCookie; | ||
private IVsShell _vsShell; | ||
private bool? _isCodeAnalysisPackageInstalled; | ||
private bool _codeAnalysisPackageLoadAttempted; | ||
private IOleCommandTarget _codeAnalysisCommandTarget; | ||
|
||
protected AbstractRunCodeAnalysisCommand( | ||
UnconfiguredProject unconfiguredProject, | ||
IProjectThreadingService threadingService, | ||
SVsServiceProvider serviceProvider, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We normally do this as |
||
RunCodeAnalysisBuildPropertyProvider runCodeAnalysisBuildPropertyProvider) | ||
{ | ||
Requires.NotNull(unconfiguredProject, nameof(unconfiguredProject)); | ||
Requires.NotNull(threadingService, nameof(threadingService)); | ||
Requires.NotNull(serviceProvider, nameof(serviceProvider)); | ||
Requires.NotNull(serviceProvider, nameof(runCodeAnalysisBuildPropertyProvider)); | ||
|
||
UnconfiguredProject = unconfiguredProject; | ||
_threadingService = threadingService; | ||
_serviceProvider = serviceProvider; | ||
_runCodeAnalysisBuildPropertyProvider = runCodeAnalysisBuildPropertyProvider; | ||
} | ||
|
||
protected UnconfiguredProject UnconfiguredProject { get; } | ||
|
||
protected abstract string GetCommandText(); | ||
protected abstract bool ShouldHandle(IProjectTree node); | ||
protected abstract long CommandId { get; } | ||
|
||
protected async override Task<CommandStatusResult> GetCommandStatusAsync(IProjectTree node, bool focused, string commandText, CommandStatus progressiveStatus) | ||
{ | ||
if (ShouldHandle(node)) | ||
{ | ||
// Enable the command if code analysis is enabled and the build manager is ready to build. | ||
if (await IsCodeAnalysisPackageInstalledAsync().ConfigureAwait(false)) | ||
{ | ||
var commandStatus = await IsReadyToBuildAsync().ConfigureAwait(false) ? CommandStatus.Enabled : CommandStatus.Supported; | ||
return await GetCommandStatusResult.Handled(GetCommandText(), commandStatus).ConfigureAwait(false); | ||
} | ||
} | ||
|
||
return CommandStatusResult.Unhandled; | ||
} | ||
|
||
private async Task<bool> IsCodeAnalysisPackageInstalledAsync() | ||
{ | ||
if (!_isCodeAnalysisPackageInstalled.HasValue) | ||
{ | ||
// Switch to UI thread for querying the build manager service. | ||
await _threadingService.SwitchToUIThread(); | ||
|
||
if (_vsShell == null) | ||
{ | ||
_vsShell = _serviceProvider.GetService<IVsShell, SVsShell>(); | ||
} | ||
|
||
var codeAnalysisGuid = new Guid(ManagedProjectSystemPackage.CodeAnalysisPackageGuid); | ||
ErrorHandler.ThrowOnFailure(_vsShell.IsPackageInstalled(ref codeAnalysisGuid, out int packageInstalled)); | ||
_isCodeAnalysisPackageInstalled = packageInstalled != 0; | ||
} | ||
|
||
return _isCodeAnalysisPackageInstalled.Value; | ||
} | ||
|
||
private async Task EnsureCodeAnalysisPackageLoadedAsync() | ||
{ | ||
if (!_codeAnalysisPackageLoadAttempted) | ||
{ | ||
// Switch to UI thread for querying the build manager service. | ||
await _threadingService.SwitchToUIThread(); | ||
|
||
if (_vsShell == null) | ||
{ | ||
_vsShell = _serviceProvider.GetService<IVsShell, SVsShell>(); | ||
} | ||
|
||
var codeAnalysisGuid = new Guid(ManagedProjectSystemPackage.CodeAnalysisPackageGuid); | ||
_vsShell.IsPackageLoaded(ref codeAnalysisGuid, out IVsPackage package); | ||
if (package == null) | ||
{ | ||
_vsShell.LoadPackage(ref codeAnalysisGuid, out package); | ||
} | ||
|
||
_codeAnalysisCommandTarget = package as IOleCommandTarget; | ||
_codeAnalysisPackageLoadAttempted = true; | ||
} | ||
} | ||
|
||
private async Task<bool> IsReadyToBuildAsync() | ||
{ | ||
// Ensure build manager is initialized. | ||
await EnsureBuildManagerInitializedAsync().ConfigureAwait(true); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why the |
||
|
||
ErrorHandler.ThrowOnFailure(_buildManager.QueryBuildManagerBusy(out int busy)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Switch to UI thread. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps just switch above |
||
return busy == 0; | ||
} | ||
|
||
private async Task EnsureBuildManagerInitializedAsync() | ||
{ | ||
// Switch to UI thread for querying the build manager service. | ||
await _threadingService.SwitchToUIThread(); | ||
|
||
if (_buildManager == null) | ||
{ | ||
_buildManager = _serviceProvider.GetService<IVsSolutionBuildManager2, SVsSolutionBuildManager>(); | ||
|
||
// Register for solution build events. | ||
_buildManager.AdviseUpdateSolutionEvents(this, out _solutionEventsCookie); | ||
} | ||
} | ||
|
||
protected override async Task<bool> TryHandleCommandAsync(IProjectTree node, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) | ||
{ | ||
if (!ShouldHandle(node)) | ||
{ | ||
return false; | ||
} | ||
|
||
if (await IsReadyToBuildAsync().ConfigureAwait(false)) | ||
{ | ||
// Build manager APIs require UI thread access. | ||
await _threadingService.SwitchToUIThread(); | ||
|
||
await EnsureCodeAnalysisPackageLoadedAsync().ConfigureAwait(false); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
// Enable RunCodeAnalysisOnce on this project. | ||
_runCodeAnalysisBuildPropertyProvider.EnableRunCodeAnalysisOnBuild(UnconfiguredProject.FullPath); | ||
|
||
_codeAnalysisCommandTarget?.Exec(VSConstants.VSStd2K, (uint)CommandId, (uint)commandExecuteOptions, variantArgIn, variantArgOut); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
#region IVsUpdateSolutionEvents members | ||
public int UpdateSolution_Begin(ref int pfCancelUpdate) | ||
{ | ||
return VSConstants.S_OK; | ||
} | ||
|
||
public int UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand) | ||
{ | ||
_runCodeAnalysisBuildPropertyProvider.DisableRunCodeAnalysisOnBuild(); | ||
return VSConstants.S_OK; | ||
} | ||
|
||
public int UpdateSolution_Cancel() | ||
{ | ||
_runCodeAnalysisBuildPropertyProvider.DisableRunCodeAnalysisOnBuild(); | ||
return VSConstants.S_OK; | ||
} | ||
|
||
public int UpdateSolution_StartUpdate(ref int pfCancelUpdate) | ||
{ | ||
return VSConstants.S_OK; | ||
} | ||
|
||
public int OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy) | ||
{ | ||
return VSConstants.S_OK; | ||
} | ||
#endregion | ||
|
||
#region IDisposable | ||
public void Dispose() | ||
{ | ||
// Build manager APIs require UI thread access. | ||
_threadingService.ExecuteSynchronously(async () => | ||
{ | ||
await _threadingService.SwitchToUIThread(); | ||
|
||
if (_buildManager != null) | ||
{ | ||
// Unregister solution build events. | ||
_buildManager.UnadviseUpdateSolutionEvents(_solutionEventsCookie); | ||
_buildManager = null; | ||
} | ||
}); | ||
} | ||
#endregion | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.ComponentModel.Composition; | ||
using Microsoft.VisualStudio.Packaging; | ||
using Microsoft.VisualStudio.ProjectSystem.Build; | ||
using Microsoft.VisualStudio.ProjectSystem.Input; | ||
using Microsoft.VisualStudio.Shell; | ||
|
||
namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands | ||
{ | ||
[ProjectCommand(ManagedProjectSystemPackage.VSStd2KCommandSet, ManagedProjectSystemPackage.RunCodeAnalysisProjectContextMenuCmdId)] | ||
[AppliesTo(ProjectCapability.CodeAnalysis)] | ||
internal class RunCodeAnalysisProjectContextMenuCommand : AbstractRunCodeAnalysisCommand | ||
{ | ||
[ImportingConstructor] | ||
public RunCodeAnalysisProjectContextMenuCommand( | ||
UnconfiguredProject unconfiguredProject, | ||
IProjectThreadingService threadingService, | ||
SVsServiceProvider serviceProvider, | ||
RunCodeAnalysisBuildPropertyProvider runCodeAnalysisBuildPropertyProvider) | ||
: base(unconfiguredProject, threadingService, serviceProvider, runCodeAnalysisBuildPropertyProvider) | ||
{ | ||
} | ||
|
||
protected override bool ShouldHandle(IProjectTree node) => node.IsRoot(); | ||
protected override string GetCommandText() => VSResources.RunCodeAnalysisProjectContextMenuCommand; | ||
protected override long CommandId => ManagedProjectSystemPackage.RunCodeAnalysisProjectContextMenuCmdId; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.ComponentModel.Composition; | ||
using System.IO; | ||
using Microsoft.VisualStudio.Packaging; | ||
using Microsoft.VisualStudio.ProjectSystem.Build; | ||
using Microsoft.VisualStudio.ProjectSystem.Input; | ||
using Microsoft.VisualStudio.Shell; | ||
|
||
namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands | ||
{ | ||
[ProjectCommand(ManagedProjectSystemPackage.VSStd2KCommandSet, ManagedProjectSystemPackage.RunCodeAnalysisTopLevelBuildMenuCmdId)] | ||
[AppliesTo(ProjectCapability.CodeAnalysis)] | ||
internal class RunCodeAnalysisTopLevelBuildMenuCommand : AbstractRunCodeAnalysisCommand | ||
{ | ||
[ImportingConstructor] | ||
public RunCodeAnalysisTopLevelBuildMenuCommand( | ||
UnconfiguredProject unconfiguredProject, | ||
IProjectThreadingService threadingService, | ||
SVsServiceProvider serviceProvider, | ||
RunCodeAnalysisBuildPropertyProvider runCodeAnalysisBuildPropertyProvider) | ||
: base(unconfiguredProject, threadingService, serviceProvider, runCodeAnalysisBuildPropertyProvider) | ||
{ | ||
} | ||
|
||
protected override bool ShouldHandle(IProjectTree node) => node.IsRoot(); | ||
protected override string GetCommandText() =>string.Format(VSResources.RunCodeAnalysisTopLevelBuildMenuCommand, Path.GetFileNameWithoutExtension(UnconfiguredProject.FullPath)); | ||
protected override long CommandId => ManagedProjectSystemPackage.RunCodeAnalysisTopLevelBuildMenuCmdId; | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -276,4 +276,10 @@ In order to debug this project, add an executable project to this solution which | |
<data name="XprojMigrationGeneralFailure" xml:space="preserve"> | ||
<value>Failed to migrate XProj project {0}. '{1}' exited with error code {2}.</value> | ||
</data> | ||
<data name="RunCodeAnalysisProjectContextMenuCommand" xml:space="preserve"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this not an existing command with existing text? |
||
<value>Run C&ode Analysis</value> | ||
</data> | ||
<data name="RunCodeAnalysisTopLevelBuildMenuCommand" xml:space="preserve"> | ||
<value>Run Code &Analysis on {0}</value> | ||
</data> | ||
</root> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should already exist: https://github.com/dotnet/roslyn-project-system/blob/master/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/CommandGroup.cs#L12
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, and could you put the CmdId's with the existing ones, https://github.com/dotnet/roslyn-project-system/blob/master/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/VisualStudioStandard2kCommandId.cs