From f51de2ce3558ae63a4eae275ce42ed1ce6bfa373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Srdjan=20Jov=C4=8Di=C4=87?= Date: Thu, 29 Oct 2020 09:21:44 -0700 Subject: [PATCH] Asynchronize access to IVsHierarchy. (#3701) * Asynchronize access to IVsHierarchy. * Reverting changes to VsTemplateWizard.cs * Reverting changes to VsFrameworkCompatibilityTests.cs * Fixing build break. * Fixing public API in InternalAPITestHook. * ProjectType: Using HashSet instead of arrays. * LegacyPackageReferenceRestoreUtilityTests: Avoid duplicate calls. * Fix CpsPackageReferenceProjectProvider.cs after mismerge on rebase. * Remove unused GetAllEnvDTEProjects method. * Ensure OnEnvDTEProjectAdded/Renamed is executed in entirety before continuing. * Fix whitespace. Co-authored-by: Srdjan Jovcic --- .../NuGetPowerShellBaseCommand.cs | 6 +- .../Handlers/ProjectRetargetingHandler.cs | 10 +- .../IDE/VSSolutionManager.cs | 42 ++++---- .../VsCoreProjectSystemReferenceReader.cs | 2 +- ...VsManagedLanguagesProjectSystemServices.cs | 40 ++++---- .../ProjectSystems/CpsProjectSystem.cs | 26 +++-- .../ProjectSystems/VsMSBuildProjectSystem.cs | 5 +- .../Projects/CpsPackageReferenceProject.cs | 2 +- .../Projects/LegacyPackageReferenceProject.cs | 29 +++--- .../Projects/MSBuildNuGetProjectProvider.cs | 2 +- .../Projects/VsProjectAdapter.cs | 53 +++++----- .../Projects/VsProjectAdapterProvider.cs | 2 +- .../Utility/EnvDTEProjectUtility.cs | 91 +++++++++-------- .../Utility/EnvDTESolutionUtility.cs | 16 +-- .../Utility/NuGetProjectUpgradeUtility.cs | 10 +- .../Utility/ProjectRetargetingUtility.cs | 4 +- .../Utility/RuntimeHelpers.cs | 14 +-- .../SolutionRestoreJob.cs | 18 +++- src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs | 21 ++-- .../IDE/EnvDteProjectExtensions.cs | 42 ++++---- .../IDE/VsHierarchyItem.cs | 93 +++++++++--------- .../IDE/VsHierarchyUtility.cs | 67 ++++++------- .../IVsProjectAdapter.cs | 6 +- .../NuGet.VisualStudio.Common/ProjectType.cs | 91 +++++++++++++++++ .../SupportedProjectTypes.cs | 92 ----------------- .../Extensibility/VsPackageInstaller.cs | 2 +- .../Extensibility/VsPathContextProvider.cs | 10 +- test/EndToEnd/API.Test.dll | Bin 0 -> 92160 bytes .../LegacyPackageReferenceProjectTests.cs | 22 ++--- ...gacyPackageReferenceRestoreUtilityTests.cs | 42 ++++---- .../ProjectSystems/TestVSProjectAdapter.cs | 17 +--- .../API.Test/InternalAPITestHook.cs | 11 ++- 32 files changed, 434 insertions(+), 454 deletions(-) create mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/ProjectType.cs delete mode 100644 src/NuGet.Clients/NuGet.VisualStudio.Common/SupportedProjectTypes.cs create mode 100644 test/EndToEnd/API.Test.dll diff --git a/src/NuGet.Clients/NuGet.PackageManagement.PowerShellCmdlets/NuGetPowerShellBaseCommand.cs b/src/NuGet.Clients/NuGet.PackageManagement.PowerShellCmdlets/NuGetPowerShellBaseCommand.cs index 73bf87ce4..0b5917d81 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.PowerShellCmdlets/NuGetPowerShellBaseCommand.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.PowerShellCmdlets/NuGetPowerShellBaseCommand.cs @@ -793,7 +793,7 @@ namespace NuGet.PackageManagement.PowerShellCmdlets { var notFoundException = new ItemNotFoundException( - String.Format( + string.Format( CultureInfo.CurrentCulture, Resources.Cmdlet_ProjectNotFound, projectName)); @@ -831,7 +831,7 @@ namespace NuGet.PackageManagement.PowerShellCmdlets [SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Justification = "This exception is passed to PowerShell. We really don't care about the type of exception here.")] protected void WriteError(string message) { - if (!String.IsNullOrEmpty(message)) + if (!string.IsNullOrEmpty(message)) { WriteError(new Exception(message)); } @@ -1043,7 +1043,7 @@ namespace NuGet.PackageManagement.PowerShellCmdlets if (path != null) { string command = "& " + PathUtility.EscapePSPath(path) + " $__rootPath $__toolsPath $__package $__project"; - LogCore(MessageLevel.Info, String.Format(CultureInfo.CurrentCulture, Resources.Cmdlet_ExecutingScript, path)); + LogCore(MessageLevel.Info, string.Format(CultureInfo.CurrentCulture, Resources.Cmdlet_ExecutingScript, path)); InvokeCommand.InvokeScript(command, false, PipelineResultTypes.Error, null, null); } diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Handlers/ProjectRetargetingHandler.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Handlers/ProjectRetargetingHandler.cs index 556caa2f2..465e16168 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Handlers/ProjectRetargetingHandler.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Handlers/ProjectRetargetingHandler.cs @@ -149,8 +149,8 @@ namespace NuGet.PackageManagement.VisualStudio var packageReferencesToBeReinstalled = ProjectRetargetingUtility.GetPackageReferencesMarkedForReinstallation(nuGetProject); if (packageReferencesToBeReinstalled.Count > 0) { - Debug.Assert(ProjectRetargetingUtility.IsNuGetInUse(project)); - var projectHierarchy = project.ToVsHierarchy(); + Debug.Assert(await ProjectRetargetingUtility.IsNuGetInUseAsync(project)); + var projectHierarchy = await project.ToVsHierarchyAsync(); ShowRetargetingErrorTask(packageReferencesToBeReinstalled.Select(p => p.PackageIdentity.Id), projectHierarchy, TaskErrorCategory.Warning, TaskPriority.Normal); } } @@ -275,9 +275,9 @@ namespace NuGet.PackageManagement.VisualStudio if (packagesToBeReinstalled.Count > 0) { // By asserting that NuGet is in use, we are also asserting that NuGet.VisualStudio.dll is already loaded - // Hence, it is okay to call project.ToVsHierarchy() - Debug.Assert(ProjectRetargetingUtility.IsNuGetInUse(project)); - var projectHierarchy = project.ToVsHierarchy(); + // Hence, it is okay to call project.ToVsHierarchyAsync() + Debug.Assert(await ProjectRetargetingUtility.IsNuGetInUseAsync(project)); + var projectHierarchy = await project.ToVsHierarchyAsync(); ShowRetargetingErrorTask(packagesToBeReinstalled.Select(p => p.Id), projectHierarchy, TaskErrorCategory.Error, TaskPriority.High); } // NuGet/Home#4833 Baseline diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs index bf80343f4..991de3adb 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/IDE/VSSolutionManager.cs @@ -366,9 +366,16 @@ namespace NuGet.PackageManagement.VisualStudio // first check with DTE, and if we find any supported project, then return immediately. var dte = await _asyncServiceProvider.GetDTEAsync(); - var isSupported = EnvDTESolutionUtility.GetAllEnvDTEProjects(dte) - .Where(EnvDTEProjectUtility.IsSupported) - .Any(); + var isSupported = false; + + foreach (Project project in await EnvDTESolutionUtility.GetAllEnvDTEProjectsAsync(dte)) + { + if (await EnvDTEProjectUtility.IsSupportedAsync(project)) + { + isSupported = true; + break; + } + } return isSupported; } @@ -540,15 +547,13 @@ namespace NuGet.PackageManagement.VisualStudio private void OnEnvDTEProjectRenamed(Project envDTEProject, string oldName) { - NuGetUIThreadHelper.JoinableTaskFactory.RunAsync(async () => + NuGetUIThreadHelper.JoinableTaskFactory.Run(async () => { - await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - if (!string.IsNullOrEmpty(oldName) && await IsSolutionOpenAsync() && _solutionOpenedRaised) { await EnsureNuGetAndVsProjectAdapterCacheAsync(); - if (EnvDTEProjectUtility.IsSupported(envDTEProject)) + if (await EnvDTEProjectUtility.IsSupportedAsync(envDTEProject)) { RemoveVsProjectAdapterFromCache(oldName); @@ -565,11 +570,11 @@ namespace NuGet.PackageManagement.VisualStudio AfterNuGetProjectRenamed?.Invoke(this, new NuGetProjectEventArgs(nuGetProject)); } - else if (EnvDTEProjectUtility.IsSolutionFolder(envDTEProject)) + else if (await EnvDTEProjectUtility.IsSolutionFolderAsync(envDTEProject)) { // In the case where a solution directory was changed, project FullNames are unchanged. // We only need to invalidate the projects under the current tree so as to sync the CustomUniqueNames. - foreach (var item in EnvDTEProjectUtility.GetSupportedChildProjects(envDTEProject)) + foreach (var item in await EnvDTEProjectUtility.GetSupportedChildProjectsAsync(envDTEProject)) { RemoveVsProjectAdapterFromCache(item.FullName); @@ -596,12 +601,10 @@ namespace NuGet.PackageManagement.VisualStudio private void OnEnvDTEProjectAdded(Project envDTEProject) { - NuGetUIThreadHelper.JoinableTaskFactory.RunAsync(async () => + NuGetUIThreadHelper.JoinableTaskFactory.Run(async () => { - await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - if (await IsSolutionOpenAsync() - && EnvDTEProjectUtility.IsSupported(envDTEProject) + && await EnvDTEProjectUtility.IsSupportedAsync(envDTEProject) && !EnvDTEProjectUtility.IsParentProjectExplicitlyUnsupported(envDTEProject) && _solutionOpenedRaised) { @@ -659,9 +662,14 @@ namespace NuGet.PackageManagement.VisualStudio { var dte = await _asyncServiceProvider.GetDTEAsync(); - var supportedProjects = EnvDTESolutionUtility - .GetAllEnvDTEProjects(dte) - .Where(EnvDTEProjectUtility.IsSupported); + var supportedProjects = new List(); + foreach (Project project in await EnvDTESolutionUtility.GetAllEnvDTEProjectsAsync(dte)) + { + if (await EnvDTEProjectUtility.IsSupportedAsync(project)) + { + supportedProjects.Add(project); + } + } foreach (var project in supportedProjects) { @@ -697,7 +705,7 @@ namespace NuGet.PackageManagement.VisualStudio private async Task AddVsProjectAdapterToCacheAsync(IVsProjectAdapter vsProjectAdapter) { - if (!vsProjectAdapter.IsSupported) + if (!await vsProjectAdapter.IsSupportedAsync()) { return; } diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/ProjectServices/VsCoreProjectSystemReferenceReader.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/ProjectServices/VsCoreProjectSystemReferenceReader.cs index 7891c6790..ba2304328 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/ProjectServices/VsCoreProjectSystemReferenceReader.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/ProjectServices/VsCoreProjectSystemReferenceReader.cs @@ -74,7 +74,7 @@ namespace NuGet.PackageManagement.VisualStudio continue; } - if (EnvDTEProjectUtility.HasUnsupportedProjectCapability(reference3.SourceProject)) + if (await EnvDTEProjectUtility.HasUnsupportedProjectCapabilityAsync(reference3.SourceProject)) { // Skip this shared project continue; diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/ProjectServices/VsManagedLanguagesProjectSystemServices.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/ProjectServices/VsManagedLanguagesProjectSystemServices.cs index d76bb1a2a..3c8a7be3f 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/ProjectServices/VsManagedLanguagesProjectSystemServices.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/ProjectServices/VsManagedLanguagesProjectSystemServices.cs @@ -31,7 +31,7 @@ namespace NuGet.PackageManagement.VisualStudio , IProjectSystemReferencesReader , IProjectSystemReferencesService { - private static readonly Array _referenceMetadata; + private static readonly Array ReferenceMetadata; private readonly IVsProjectAdapter _vsProjectAdapter; private readonly IVsProjectThreadingService _threadingService; @@ -59,13 +59,13 @@ namespace NuGet.PackageManagement.VisualStudio static VsManagedLanguagesProjectSystemServices() { - _referenceMetadata = Array.CreateInstance(typeof(string), 6); - _referenceMetadata.SetValue(ProjectItemProperties.IncludeAssets, 0); - _referenceMetadata.SetValue(ProjectItemProperties.ExcludeAssets, 1); - _referenceMetadata.SetValue(ProjectItemProperties.PrivateAssets, 2); - _referenceMetadata.SetValue(ProjectItemProperties.NoWarn, 3); - _referenceMetadata.SetValue(ProjectItemProperties.GeneratePathProperty, 4); - _referenceMetadata.SetValue(ProjectItemProperties.Aliases, 5); + ReferenceMetadata = Array.CreateInstance(typeof(string), 6); + ReferenceMetadata.SetValue(ProjectItemProperties.IncludeAssets, 0); + ReferenceMetadata.SetValue(ProjectItemProperties.ExcludeAssets, 1); + ReferenceMetadata.SetValue(ProjectItemProperties.PrivateAssets, 2); + ReferenceMetadata.SetValue(ProjectItemProperties.NoWarn, 3); + ReferenceMetadata.SetValue(ProjectItemProperties.GeneratePathProperty, 4); + ReferenceMetadata.SetValue(ProjectItemProperties.Aliases, 5); } public VsManagedLanguagesProjectSystemServices( @@ -108,7 +108,7 @@ namespace NuGet.PackageManagement.VisualStudio { if (AsVSProject4.PackageReferences.TryGetReference( installedPackage, - _referenceMetadata, + ReferenceMetadata, out var version, out var metadataElements, out var metadataValues)) @@ -139,23 +139,23 @@ namespace NuGet.PackageManagement.VisualStudio return new ProjectRestoreReference[] { }; } - var references = AsVSProject4.References - .Cast() - .Where(r => r.SourceProject != null && EnvDTEProjectUtility.IsSupported(r.SourceProject)) - .Select(reference => + var references = new List(); + foreach (Reference6 r in AsVSProject4.References.Cast()) + { + if (r.SourceProject != null && await EnvDTEProjectUtility.IsSupportedAsync(r.SourceProject)) { Array metadataElements; Array metadataValues; - reference.GetMetadata(_referenceMetadata, out metadataElements, out metadataValues); + r.GetMetadata(ReferenceMetadata, out metadataElements, out metadataValues); - return new ProjectReference( - uniqueName: reference.SourceProject.FullName, + references.Add(ToProjectRestoreReference(new ProjectReference( + uniqueName: r.SourceProject.FullName, metadataElements: metadataElements, - metadataValues: metadataValues); - }) - .Select(ToProjectRestoreReference); + metadataValues: metadataValues))); + } + } - return references.ToList(); + return references; } private static ProjectRestoreReference ToProjectRestoreReference(ProjectReference item) diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/ProjectSystems/CpsProjectSystem.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/ProjectSystems/CpsProjectSystem.cs index a39ef70a9..780370169 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/ProjectSystems/CpsProjectSystem.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/ProjectSystems/CpsProjectSystem.cs @@ -31,13 +31,11 @@ namespace NuGet.PackageManagement.VisualStudio } NuGetUIThreadHelper.JoinableTaskFactory.Run(async delegate - { - await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - - var root = VsProjectAdapter.ProjectDirectory; - var relativeTargetPath = PathUtility.GetRelativePath(PathUtility.EnsureTrailingSlash(root), targetFullPath); - await AddImportStatementAsync(location, relativeTargetPath); - }); + { + var root = await VsProjectAdapter.GetProjectDirectoryAsync(); + var relativeTargetPath = PathUtility.GetRelativePath(PathUtility.EnsureTrailingSlash(root), targetFullPath); + await AddImportStatementAsync(location, relativeTargetPath); + }); } [MethodImpl(MethodImplOptions.NoInlining)] @@ -62,14 +60,12 @@ namespace NuGet.PackageManagement.VisualStudio } NuGetUIThreadHelper.JoinableTaskFactory.Run(async delegate - { - await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - - var root = VsProjectAdapter.ProjectDirectory; - // For VS 2012 or above, the operation has to be done inside the Writer lock - var relativeTargetPath = PathUtility.GetRelativePath(PathUtility.EnsureTrailingSlash(root), targetFullPath); - await RemoveImportStatementAsync(relativeTargetPath); - }); + { + var root = await VsProjectAdapter.GetProjectDirectoryAsync(); + // For VS 2012 or above, the operation has to be done inside the Writer lock + var relativeTargetPath = PathUtility.GetRelativePath(PathUtility.EnsureTrailingSlash(root), targetFullPath); + await RemoveImportStatementAsync(relativeTargetPath); + }); } // IMPORTANT: The NoInlining is required to prevent CLR from loading VisualStudio12.dll assembly while running diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/ProjectSystems/VsMSBuildProjectSystem.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/ProjectSystems/VsMSBuildProjectSystem.cs index 5a204e7e8..c06f0e1b9 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/ProjectSystems/VsMSBuildProjectSystem.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/ProjectSystems/VsMSBuildProjectSystem.cs @@ -72,8 +72,7 @@ namespace NuGet.PackageManagement.VisualStudio { NuGetUIThreadHelper.JoinableTaskFactory.Run(async delegate { - await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - _projectFullPath = VsProjectAdapter.ProjectDirectory; + _projectFullPath = await VsProjectAdapter.GetProjectDirectoryAsync(); }); } @@ -465,7 +464,7 @@ namespace NuGet.PackageManagement.VisualStudio { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - var containsFile = await EnvDTEProjectUtility.ContainsFile(VsProjectAdapter.Project, path); + var containsFile = await EnvDTEProjectUtility.ContainsFileAsync(VsProjectAdapter.Project, path); return containsFile; }); } diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/CpsPackageReferenceProject.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/CpsPackageReferenceProject.cs index a09de3748..10b437b0e 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/CpsPackageReferenceProject.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/CpsPackageReferenceProject.cs @@ -207,7 +207,7 @@ namespace NuGet.PackageManagement.VisualStudio foreach (var projectReference in frameworkInfo.ProjectReferences) { - if (SupportedProjectTypes.IsSupportedProjectExtension(projectReference.ProjectPath)) + if (ProjectType.IsSupportedProjectExtension(projectReference.ProjectPath)) { projectReferences.Add(projectReference); } diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProject.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProject.cs index 53c3edeee..63a4b9d2b 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProject.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProject.cs @@ -82,8 +82,7 @@ namespace NuGet.PackageManagement.VisualStudio public override async Task GetCacheFilePathAsync() { - await _threadingService.JoinableTaskFactory.SwitchToMainThreadAsync(); - return NoOpRestoreUtilities.GetProjectCacheFilePath(cacheRoot: GetMSBuildProjectExtensionsPath()); + return NoOpRestoreUtilities.GetProjectCacheFilePath(cacheRoot: await GetMSBuildProjectExtensionsPathAsync()); } public override async Task GetAssetsFilePathOrNullAsync() @@ -93,10 +92,7 @@ namespace NuGet.PackageManagement.VisualStudio private async Task GetAssetsFilePathAsync(bool shouldThrow) { - await _threadingService.JoinableTaskFactory.SwitchToMainThreadAsync(); - - var msbuildProjectExtensionsPath = GetMSBuildProjectExtensionsPath(shouldThrow); - + var msbuildProjectExtensionsPath = await GetMSBuildProjectExtensionsPathAsync(shouldThrow); if (msbuildProjectExtensionsPath == null) { return null; @@ -204,9 +200,9 @@ namespace NuGet.PackageManagement.VisualStudio { await _threadingService.JoinableTaskFactory.SwitchToMainThreadAsync(); - EnvDTEProjectUtility.EnsureCheckedOutIfExists(_vsProjectAdapter.Project, _vsProjectAdapter.ProjectDirectory, filePath); + EnvDTEProjectUtility.EnsureCheckedOutIfExists(_vsProjectAdapter.Project, await _vsProjectAdapter.GetProjectDirectoryAsync(), filePath); - var isFileExistsInProject = await EnvDTEProjectUtility.ContainsFile(_vsProjectAdapter.Project, filePath); + var isFileExistsInProject = await EnvDTEProjectUtility.ContainsFileAsync(_vsProjectAdapter.Project, filePath); if (!isFileExistsInProject) { @@ -221,18 +217,19 @@ namespace NuGet.PackageManagement.VisualStudio var folderPath = Path.GetDirectoryName(filePath); var fullPath = filePath; - if (filePath.Contains(_vsProjectAdapter.ProjectDirectory)) + string projectDirectory = await _vsProjectAdapter.GetProjectDirectoryAsync(); + if (filePath.Contains(projectDirectory)) { // folderPath should always be relative to ProjectDirectory so if filePath already contains // ProjectDirectory then get a relative path and construct folderPath to get the appropriate // ProjectItems from dte where you have to add this file. - var relativeLockFilePath = FileSystemUtility.GetRelativePath(_vsProjectAdapter.ProjectDirectory, filePath); + var relativeLockFilePath = FileSystemUtility.GetRelativePath(projectDirectory, filePath); folderPath = Path.GetDirectoryName(relativeLockFilePath); } else { // get the fullPath wrt ProjectDirectory - fullPath = FileSystemUtility.GetFullPath(_vsProjectAdapter.ProjectDirectory, filePath); + fullPath = FileSystemUtility.GetFullPath(projectDirectory, filePath); } var container = await EnvDTEProjectUtility.GetProjectItemsAsync(_vsProjectAdapter.Project, folderPath, createIfNotExists: true); @@ -250,11 +247,11 @@ namespace NuGet.PackageManagement.VisualStudio #endregion - private string GetMSBuildProjectExtensionsPath(bool shouldThrow = true) + private async Task GetMSBuildProjectExtensionsPathAsync(bool shouldThrow = true) { - ThreadHelper.ThrowIfNotOnUIThread(); + await _threadingService.JoinableTaskFactory.SwitchToMainThreadAsync(); - var msbuildProjectExtensionsPath = _vsProjectAdapter.MSBuildProjectExtensionsPath; + var msbuildProjectExtensionsPath = await _vsProjectAdapter.GetMSBuildProjectExtensionsPathAsync(); if (string.IsNullOrEmpty(msbuildProjectExtensionsPath)) { @@ -263,7 +260,7 @@ namespace NuGet.PackageManagement.VisualStudio throw new InvalidDataException(string.Format( Strings.MSBuildPropertyNotFound, ProjectBuildProperties.MSBuildProjectExtensionsPath, - _vsProjectAdapter.ProjectDirectory)); + await _vsProjectAdapter.GetProjectDirectoryAsync())); } return null; @@ -437,7 +434,7 @@ namespace NuGet.PackageManagement.VisualStudio RestoreMetadata = new ProjectRestoreMetadata { ProjectStyle = ProjectStyle.PackageReference, - OutputPath = GetMSBuildProjectExtensionsPath(), + OutputPath = await GetMSBuildProjectExtensionsPathAsync(), ProjectPath = _projectFullPath, ProjectName = projectName, ProjectUniqueName = _projectFullPath, diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/MSBuildNuGetProjectProvider.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/MSBuildNuGetProjectProvider.cs index 293fd4850..9e5f18a01 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/MSBuildNuGetProjectProvider.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/MSBuildNuGetProjectProvider.cs @@ -69,7 +69,7 @@ namespace NuGet.PackageManagement.VisualStudio var folderNuGetProjectFullPath = context.PackagesPathFactory(); // Project folder path is the packages config folder path - var packagesConfigFolderPath = vsProjectAdapter.ProjectDirectory; + var packagesConfigFolderPath = await vsProjectAdapter.GetProjectDirectoryAsync(); return new VsMSBuildNuGetProject( vsProjectAdapter, diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/VsProjectAdapter.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/VsProjectAdapter.cs index bee0a4c38..9e1dab377 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/VsProjectAdapter.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/VsProjectAdapter.cs @@ -35,19 +35,16 @@ namespace NuGet.PackageManagement.VisualStudio #region Properties - public string MSBuildProjectExtensionsPath + public async Task GetMSBuildProjectExtensionsPathAsync() { - get + var msbuildProjectExtensionsPath = BuildProperties.GetPropertyValue(ProjectBuildProperties.MSBuildProjectExtensionsPath); + + if (string.IsNullOrEmpty(msbuildProjectExtensionsPath)) { - var msbuildProjectExtensionsPath = BuildProperties.GetPropertyValue(ProjectBuildProperties.MSBuildProjectExtensionsPath); - - if (string.IsNullOrEmpty(msbuildProjectExtensionsPath)) - { - return null; - } - - return Path.Combine(ProjectDirectory, msbuildProjectExtensionsPath); + return null; } + + return Path.Combine(await GetProjectDirectoryAsync(), msbuildProjectExtensionsPath); } public string RestorePackagesPath @@ -101,18 +98,15 @@ namespace NuGet.PackageManagement.VisualStudio public string FullName => ProjectNames.FullName; - public string ProjectDirectory + public async Task GetProjectDirectoryAsync() { - get + if (!IsDeferred) { - if (!IsDeferred) - { - return Project.GetFullPath(); - } - else - { - return Path.GetDirectoryName(FullProjectPath); - } + return await Project.GetFullPathAsync(); + } + else + { + return Path.GetDirectoryName(FullProjectPath); } } @@ -126,17 +120,14 @@ namespace NuGet.PackageManagement.VisualStudio } } - public bool IsSupported + public async Task IsSupportedAsync() { - get + if (!IsDeferred) { - if (!IsDeferred) - { - return EnvDTEProjectUtility.IsSupported(Project); - } - - return VsHierarchyUtility.IsSupported(VsHierarchy, _projectTypeGuid); + return await EnvDTEProjectUtility.IsSupportedAsync(Project); } + + return await VsHierarchyUtility.IsSupportedAsync(VsHierarchy, _projectTypeGuid); } public string PackageTargetFallback @@ -246,7 +237,7 @@ namespace NuGet.PackageManagement.VisualStudio { if (!IsDeferred) { - return Project.GetProjectTypeGuids(); + return await Project.GetProjectTypeGuidsAsync(); } else { @@ -284,7 +275,7 @@ namespace NuGet.PackageManagement.VisualStudio if (!IsDeferred) { if (Project.Kind != null - && SupportedProjectTypes.IsSupportedForAddingReferences(Project.Kind)) + && ProjectType.IsSupportedForAddingReferences(Project.Kind)) { return EnvDTEProjectUtility.GetReferencedProjects(Project).Select(p => p.UniqueName); } @@ -294,7 +285,7 @@ namespace NuGet.PackageManagement.VisualStudio else { var projectTypeGuids = await GetProjectTypeGuidsAsync(); - if (projectTypeGuids.All(SupportedProjectTypes.IsSupportedForAddingReferences)) + if (projectTypeGuids.All(ProjectType.IsSupportedForAddingReferences)) { return await _workspaceService.GetProjectReferencesAsync(FullProjectPath); } diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/VsProjectAdapterProvider.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/VsProjectAdapterProvider.cs index 6b27a0624..3bfcfd197 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/VsProjectAdapterProvider.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/VsProjectAdapterProvider.cs @@ -68,7 +68,7 @@ namespace NuGet.PackageManagement.VisualStudio // switch to main thread and use services we know must be done on main thread. await _threadingService.JoinableTaskFactory.SwitchToMainThreadAsync(); - var vsHierarchyItem = VsHierarchyItem.FromDteProject(dteProject); + var vsHierarchyItem = await VsHierarchyItem.FromDteProjectAsync(dteProject); Func loadDteProject = _ => dteProject; var buildStorageProperty = vsHierarchyItem.VsHierarchy as IVsBuildPropertyStorage; diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/EnvDTEProjectUtility.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/EnvDTEProjectUtility.cs index 967aafdf6..9ee8f5907 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/EnvDTEProjectUtility.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/EnvDTEProjectUtility.cs @@ -45,17 +45,14 @@ namespace NuGet.PackageManagement.VisualStudio #region Get Project Information - internal static bool IsSolutionFolder(EnvDTE.Project envDTEProject) + internal static async Task IsSolutionFolderAsync(EnvDTE.Project envDTEProject) { - ThreadHelper.ThrowIfNotOnUIThread(); - + await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); return envDTEProject.Kind != null && envDTEProject.Kind.Equals(VsProjectTypes.VsProjectItemKindSolutionFolder, StringComparison.OrdinalIgnoreCase); } - public static async Task ContainsFile(EnvDTE.Project envDTEProject, string path) + public static async Task ContainsFileAsync(EnvDTE.Project envDTEProject, string path) { - ThreadHelper.ThrowIfNotOnUIThread(); - if (string.Equals(envDTEProject.Kind, VsProjectTypes.WixProjectTypeGuid, StringComparison.OrdinalIgnoreCase) || string.Equals(envDTEProject.Kind, VsProjectTypes.NemerleProjectTypeGuid, StringComparison.OrdinalIgnoreCase) @@ -73,7 +70,10 @@ namespace NuGet.PackageManagement.VisualStudio var item = await GetProjectItemAsync(envDTEProject, path); return item != null; } - var vsProject = (IVsProject)envDTEProject.ToVsHierarchy(); + + await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + + var vsProject = (IVsProject)(await envDTEProject.ToVsHierarchyAsync()); if (vsProject == null) { return false; @@ -82,7 +82,7 @@ namespace NuGet.PackageManagement.VisualStudio int pFound; uint itemId; - if (IsProjectCapabilityCompliant(envDTEProject)) + if (await IsProjectCapabilityCompliantAsync(envDTEProject)) { // REVIEW: We want to revisit this after RTM - the code in this if statement should be applied to every project type. // We're checking for VSDOCUMENTPRIORITY.DP_Standard here to see if the file is included in the project. @@ -99,7 +99,7 @@ namespace NuGet.PackageManagement.VisualStudio // Get the ProjectItems for a folder path public static async Task GetProjectItemsAsync(EnvDTE.Project envDTEProject, string folderPath, bool createIfNotExists) { - ThreadHelper.ThrowIfNotOnUIThread(); + await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); if (string.IsNullOrEmpty(folderPath)) { @@ -113,7 +113,7 @@ namespace NuGet.PackageManagement.VisualStudio // Both types have the ProjectItems property that we want to access. object cursor = envDTEProject; - var fullPath = envDTEProject.GetFullPath(); + var fullPath = await envDTEProject.GetFullPathAsync(); var folderRelativePath = string.Empty; foreach (var part in pathParts) @@ -204,7 +204,7 @@ namespace NuGet.PackageManagement.VisualStudio // Execute command to include the existing folder into project. Must do this on UI thread. await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - var projectHierarchy = (IVsUIHierarchy)envDTEProject.ToVsHierarchy(); + var projectHierarchy = (IVsUIHierarchy)(await envDTEProject.ToVsHierarchyAsync()); uint itemId; var hr = projectHierarchy.ParseCanonicalName(folderRelativePath, out itemId); @@ -373,48 +373,45 @@ namespace NuGet.PackageManagement.VisualStudio return references; } - public static bool IsSupported(EnvDTE.Project envDTEProject) + public static async Task IsSupportedAsync(EnvDTE.Project envDTEProject) { Assumes.Present(envDTEProject); - ThreadHelper.ThrowIfNotOnUIThread(); - if (SupportsProjectKPackageManager(envDTEProject)) { return true; } - if (IsProjectCapabilityCompliant(envDTEProject)) + if (await IsProjectCapabilityCompliantAsync(envDTEProject)) { return true; } - return envDTEProject.Kind != null && SupportedProjectTypes.IsSupported(envDTEProject.Kind) && !HasUnsupportedProjectCapability(envDTEProject); + await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + + return envDTEProject.Kind != null && ProjectType.IsSupported(envDTEProject.Kind) && !await HasUnsupportedProjectCapabilityAsync(envDTEProject); } - private static bool IsProjectCapabilityCompliant(EnvDTE.Project envDTEProject) + private static async Task IsProjectCapabilityCompliantAsync(EnvDTE.Project envDTEProject) { - ThreadHelper.ThrowIfNotOnUIThread(); - Debug.Assert(envDTEProject != null); - - var hierarchy = envDTEProject.ToVsHierarchy(); - - return VsHierarchyUtility.IsProjectCapabilityCompliant(hierarchy); + var hierarchy = await envDTEProject.ToVsHierarchyAsync(); + return await VsHierarchyUtility.IsProjectCapabilityCompliantAsync(hierarchy); } public async static Task GetNuGetProjectAsync(EnvDTE.Project project, ISolutionManager solutionManager) { - ThreadHelper.ThrowIfNotOnUIThread(); - Debug.Assert(project != null); Debug.Assert(solutionManager != null); + await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + var nuGetProject = await solutionManager.GetNuGetProjectAsync(project.Name); if (nuGetProject == null) { nuGetProject = await solutionManager.GetNuGetProjectAsync(project.UniqueName); } + return nuGetProject; } @@ -432,15 +429,14 @@ namespace NuGet.PackageManagement.VisualStudio /// Recursively retrieves all supported child projects of a virtual folder. /// /// The root container project - internal static IEnumerable GetSupportedChildProjects(EnvDTE.Project envDTEProject) + internal static async Task> GetSupportedChildProjectsAsync(EnvDTE.Project envDTEProject) { - ThreadHelper.ThrowIfNotOnUIThread(); - - if (!IsSolutionFolder(envDTEProject)) + if (!await IsSolutionFolderAsync(envDTEProject)) { - yield break; + return Array.Empty(); } + var supportedChildProjects = new List(); var containerProjects = new Queue(); containerProjects.Enqueue(envDTEProject); @@ -453,21 +449,23 @@ namespace NuGet.PackageManagement.VisualStudio if (nestedProject == null) { } - else if (IsSupported(nestedProject)) + else if (await IsSupportedAsync(nestedProject)) { - yield return nestedProject; + supportedChildProjects.Add(nestedProject); } - else if (IsSolutionFolder(nestedProject)) + else if (await IsSolutionFolderAsync(nestedProject)) { containerProjects.Enqueue(nestedProject); } } } + + return supportedChildProjects; } - internal static HashSet GetAssemblyClosure(EnvDTE.Project envDTEProject, IDictionary> visitedProjects) + internal static async Task> GetAssemblyClosureAsync(EnvDTE.Project envDTEProject, IDictionary> visitedProjects) { - ThreadHelper.ThrowIfNotOnUIThread(); + await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); HashSet assemblies; if (visitedProjects.TryGetValue(envDTEProject.UniqueName, out assemblies)) @@ -478,24 +476,24 @@ namespace NuGet.PackageManagement.VisualStudio assemblies = new HashSet(PathComparer.Default); visitedProjects.Add(envDTEProject.UniqueName, assemblies); - var localProjectAssemblies = GetLocalProjectAssemblies(envDTEProject); + var localProjectAssemblies = await GetLocalProjectAssembliesAsync(envDTEProject); CollectionsUtility.AddRange(assemblies, localProjectAssemblies); var referencedProjects = GetReferencedProjects(envDTEProject); foreach (var project in referencedProjects) { - var assemblyClosure = GetAssemblyClosure(project, visitedProjects); + var assemblyClosure = await GetAssemblyClosureAsync(project, visitedProjects); CollectionsUtility.AddRange(assemblies, assemblyClosure); } return assemblies; } - private static HashSet GetLocalProjectAssemblies(EnvDTE.Project envDTEProject) + private static async Task> GetLocalProjectAssembliesAsync(EnvDTE.Project envDTEProject) { if (envDTEProject.IsWebSite()) { - var websiteLocalAssemblies = GetWebsiteLocalAssemblies(envDTEProject); + var websiteLocalAssemblies = await GetWebsiteLocalAssembliesAsync(envDTEProject); return websiteLocalAssemblies; } @@ -531,7 +529,7 @@ namespace NuGet.PackageManagement.VisualStudio return assemblies; } - private static HashSet GetWebsiteLocalAssemblies(EnvDTE.Project envDTEProject) + private static async Task> GetWebsiteLocalAssembliesAsync(EnvDTE.Project envDTEProject) { var assemblies = new HashSet(PathComparer.Default); var references = GetAssemblyReferences(envDTEProject); @@ -550,7 +548,7 @@ namespace NuGet.PackageManagement.VisualStudio // For website projects, we always add .refresh files that point to the corresponding binaries in packages. In the event of bin deployed assemblies that are also GACed, // the ReferenceKind is not AssemblyReferenceBin. Consequently, we work around this by looking for any additional assembly declarations specified via .refresh files. - var envDTEProjectPath = envDTEProject.GetFullPath(); + var envDTEProjectPath = await envDTEProject.GetFullPathAsync(); CollectionsUtility.AddRange(assemblies, RefreshFileUtility.ResolveRefreshPaths(envDTEProjectPath)); return assemblies; @@ -636,7 +634,7 @@ namespace NuGet.PackageManagement.VisualStudio { ThreadHelper.ThrowIfNotOnUIThread(); - return envDTEProject.Kind == null || SupportedProjectTypes.IsUnsupported(envDTEProject.Kind); + return envDTEProject.Kind == null || ProjectType.IsUnsupported(envDTEProject.Kind); } public static bool IsParentProjectExplicitlyUnsupported(EnvDTE.Project envDTEProject) @@ -710,9 +708,9 @@ namespace NuGet.PackageManagement.VisualStudio { var projectNameConfig = ProjectJsonPathUtilities.GetProjectConfigWithProjectName(project.Name); - var containsProjectJson = await ContainsFile(project, projectNameConfig); + var containsProjectJson = await ContainsFileAsync(project, projectNameConfig); - var containsProjectNameJson = await ContainsFile( + var containsProjectNameJson = await ContainsFileAsync( project, ProjectJsonPathUtilities.ProjectConfigFileName); @@ -722,10 +720,9 @@ namespace NuGet.PackageManagement.VisualStudio /// /// Check if the project has an unsupported project capability, such as, "SharedAssetsProject" /// - public static bool HasUnsupportedProjectCapability(EnvDTE.Project envDTEProject) + public static async Task HasUnsupportedProjectCapabilityAsync(EnvDTE.Project envDTEProject) { - var hier = envDTEProject.ToVsHierarchy(); - + var hier = await envDTEProject.ToVsHierarchyAsync(); return VsHierarchyUtility.HasUnsupportedProjectCapability(hier); } diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/EnvDTESolutionUtility.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/EnvDTESolutionUtility.cs index 09f647f45..3335de5ba 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/EnvDTESolutionUtility.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/EnvDTESolutionUtility.cs @@ -6,25 +6,11 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using NuGet.VisualStudio; -using Task = System.Threading.Tasks.Task; namespace NuGet.PackageManagement.VisualStudio { public static class EnvDTESolutionUtility { - /// - /// Get the list of all supported projects in the current solution. This method - /// recursively iterates through all projects. - /// - public static IEnumerable GetAllEnvDTEProjects(EnvDTE.DTE dte) - { - return NuGetUIThreadHelper.JoinableTaskFactory.Run(async delegate - { - var result = await GetAllEnvDTEProjectsAsync(dte); - return result; - }); - } - public static async Task> GetAllEnvDTEProjectsAsync(EnvDTE.DTE dte) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -50,7 +36,7 @@ namespace NuGet.PackageManagement.VisualStudio { var envDTEProject = envDTEProjects.Pop(); - if (EnvDTEProjectUtility.IsSupported(envDTEProject)) + if (await EnvDTEProjectUtility.IsSupportedAsync(envDTEProject)) { resultantEnvDTEProjects.Add(envDTEProject); } diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/NuGetProjectUpgradeUtility.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/NuGetProjectUpgradeUtility.cs index 3f830e448..d86d29db3 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/NuGetProjectUpgradeUtility.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/NuGetProjectUpgradeUtility.cs @@ -75,12 +75,12 @@ namespace NuGet.PackageManagement.VisualStudio envDTEProject = vsmsBuildNuGetProjectSystem.VsProjectAdapter.Project; } - if (!EnvDTEProjectUtility.IsSupported(envDTEProject)) + if (!await EnvDTEProjectUtility.IsSupportedAsync(envDTEProject)) { return false; } - return IsProjectPackageReferenceCompatible(envDTEProject); + return await IsProjectPackageReferenceCompatibleAsync(envDTEProject); } private static async Task GetNuGetProject(Project envDTEProject) @@ -92,9 +92,9 @@ namespace NuGet.PackageManagement.VisualStudio return nuGetProject; } - private static bool IsProjectPackageReferenceCompatible(Project project) + private static async Task IsProjectPackageReferenceCompatibleAsync(Project project) { - var projectGuids = project.GetProjectTypeGuids(); + var projectGuids = await project.GetProjectTypeGuidsAsync(); if (projectGuids.Any(t => UnupgradeableProjectTypes.Contains(t))) { @@ -103,7 +103,7 @@ namespace NuGet.PackageManagement.VisualStudio // Project is supported language, and not an unsupported type return UpgradeableProjectTypes.Contains(project.Kind) && - projectGuids.All(projectTypeGuid => !SupportedProjectTypes.IsUnsupported(projectTypeGuid)); + projectGuids.All(projectTypeGuid => !ProjectType.IsUnsupported(projectTypeGuid)); } public static bool IsPackagesConfigSelected(IVsMonitorSelection vsMonitorSelection) diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/ProjectRetargetingUtility.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/ProjectRetargetingUtility.cs index d18285162..e09cdb6a7 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/ProjectRetargetingUtility.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/ProjectRetargetingUtility.cs @@ -256,9 +256,9 @@ namespace NuGet.PackageManagement.VisualStudio /// Determines if NuGet is used in the project. Currently, it is determined by checking if packages.config is part of the project /// /// The project which is checked to see if NuGet is used in it - public static bool IsNuGetInUse(Project project) + public static async Task IsNuGetInUseAsync(Project project) { - return EnvDTEProjectUtility.IsSupported(project) && File.Exists(project.GetPackagesConfigFullPath()); + return await EnvDTEProjectUtility.IsSupportedAsync(project) && File.Exists(await project.GetPackagesConfigFullPathAsync()); } } } diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/RuntimeHelpers.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/RuntimeHelpers.cs index 63ad27d13..190da2053 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/RuntimeHelpers.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/RuntimeHelpers.cs @@ -31,7 +31,7 @@ namespace NuGet.PackageManagement.VisualStudio await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); // Keep track of visited projects - if (SupportsBindingRedirects(vsProjectAdapter.Project)) + if (await SupportsBindingRedirectsAsync(vsProjectAdapter.Project)) { // Get the dependentEnvDTEProjectsDictionary once here, so that, it is not called for every single project var dependentProjectsDictionary = await vsSolutionManager.GetDependentProjectsDictionaryAsync(); @@ -45,10 +45,10 @@ namespace NuGet.PackageManagement.VisualStudio } } - private static bool SupportsBindingRedirects(EnvDTE.Project Project) + private static async Task SupportsBindingRedirectsAsync(EnvDTE.Project Project) { - return (Project.Kind != null && SupportedProjectTypes.IsSupportedForBindingRedirects(Project.Kind)) - && !Project.IsWindowsStoreApp(); + return (Project.Kind != null && ProjectType.IsSupportedForBindingRedirects(Project.Kind)) + && !await Project.IsWindowsStoreAppAsync(); } private static Task AddBindingRedirectsAsync( @@ -87,7 +87,7 @@ namespace NuGet.PackageManagement.VisualStudio return; } - if (SupportsBindingRedirects(vsProjectAdapter.Project)) + if (await SupportsBindingRedirectsAsync(vsProjectAdapter.Project)) { await AddBindingRedirectsAsync(vsSolutionManager, vsProjectAdapter, domain, projectAssembliesCache, frameworkMultiTargeting, nuGetProjectContext); } @@ -142,7 +142,7 @@ namespace NuGet.PackageManagement.VisualStudio return redirects; } - IEnumerable assemblies = EnvDTEProjectUtility.GetAssemblyClosure(vsProjectAdapter.Project, projectAssembliesCache); + IEnumerable assemblies = await EnvDTEProjectUtility.GetAssemblyClosureAsync(vsProjectAdapter.Project, projectAssembliesCache); redirects = BindingRedirectResolver.GetBindingRedirects(assemblies, domain); if (frameworkMultiTargeting != null) @@ -152,7 +152,7 @@ namespace NuGet.PackageManagement.VisualStudio } // Create a binding redirect manager over the configuration - var manager = new BindingRedirectManager(vsProjectAdapter.Project.GetConfigurationFile(), msBuildNuGetProjectSystem); + var manager = new BindingRedirectManager(await vsProjectAdapter.Project.GetConfigurationFileAsync(), msBuildNuGetProjectSystem); // Add the redirects manager.AddBindingRedirects(redirects); diff --git a/src/NuGet.Clients/NuGet.SolutionRestoreManager/SolutionRestoreJob.cs b/src/NuGet.Clients/NuGet.SolutionRestoreManager/SolutionRestoreJob.cs index c07ca193e..c97ffa554 100644 --- a/src/NuGet.Clients/NuGet.SolutionRestoreManager/SolutionRestoreJob.cs +++ b/src/NuGet.Clients/NuGet.SolutionRestoreManager/SolutionRestoreJob.cs @@ -722,10 +722,20 @@ namespace NuGet.SolutionRestoreManager var dte = await _asyncServiceProvider.GetDTEAsync(); var projects = dte.Solution.Projects; - return projects - .OfType() - .Select(p => new ProjectInfo(p.GetFullPath(), p.Name)) - .Any(p => p.CheckPackagesConfig()); + + var succeeded = false; + + foreach (var p in projects.OfType()) + { + var pi = new ProjectInfo(await p.GetFullPathAsync(), p.Name); + if (pi.CheckPackagesConfig()) + { + succeeded = true; + break; + } + } + + return succeeded; }); } diff --git a/src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs b/src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs index 213ab598e..a1dc084a0 100644 --- a/src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs +++ b/src/NuGet.Clients/NuGet.Tools/NuGetPackage.cs @@ -427,7 +427,7 @@ namespace NuGetVSExtension { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - var vsProject = project.ToVsHierarchy(); + var vsProject = await project.ToVsHierarchyAsync(); var documentName = project.FullName; // Find existing hierarchy and item id of the document window if it's @@ -639,7 +639,7 @@ namespace NuGetVSExtension && !project.IsUnloaded() && - EnvDTEProjectUtility.IsSupported(project)) + await EnvDTEProjectUtility.IsSupportedAsync(project)) { var windowFrame = await FindExistingWindowFrameAsync(project); if (windowFrame == null) @@ -907,7 +907,7 @@ namespace NuGetVSExtension } command.Visible = GetIsSolutionOpen() && await IsPackagesConfigBasedProjectAsync(); - command.Enabled = !isConsoleBusy && IsSolutionExistsAndNotDebuggingAndNotBuilding() && HasActiveLoadedSupportedProject; + command.Enabled = !isConsoleBusy && IsSolutionExistsAndNotDebuggingAndNotBuilding() && await HasActiveLoadedSupportedProjectAsync(); }); } @@ -932,7 +932,7 @@ namespace NuGetVSExtension } command.Visible = GetIsSolutionOpen() && IsPackagesConfigSelected(); - command.Enabled = !isConsoleBusy && IsSolutionExistsAndNotDebuggingAndNotBuilding() && HasActiveLoadedSupportedProject; + command.Enabled = !isConsoleBusy && IsSolutionExistsAndNotDebuggingAndNotBuilding() && await HasActiveLoadedSupportedProjectAsync(); }); } @@ -1003,7 +1003,7 @@ namespace NuGetVSExtension command.Enabled = IsSolutionExistsAndNotDebuggingAndNotBuilding() && !isConsoleBusy && - HasActiveLoadedSupportedProject; + await HasActiveLoadedSupportedProjectAsync(); }); } @@ -1081,14 +1081,11 @@ namespace NuGetVSExtension /// Gets whether the current IDE has an active, supported and non-unloaded project, which is a precondition for /// showing the Add Library Package Reference dialog /// - private bool HasActiveLoadedSupportedProject + private async Task HasActiveLoadedSupportedProjectAsync() { - get - { - var project = VsMonitorSelection.GetActiveProject(); - return project != null && !project.IsUnloaded() - && EnvDTEProjectUtility.IsSupported(project); - } + var project = VsMonitorSelection.GetActiveProject(); + return project != null && !project.IsUnloaded() + && await EnvDTEProjectUtility.IsSupportedAsync(project); } private bool ShouldMEFBeInitialized() diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/IDE/EnvDteProjectExtensions.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/IDE/EnvDteProjectExtensions.cs index 6c54f3e7e..86d4b806b 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/IDE/EnvDteProjectExtensions.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/IDE/EnvDteProjectExtensions.cs @@ -23,14 +23,14 @@ namespace NuGet.VisualStudio { public static class EnvDteProjectExtensions { - public static IVsHierarchy ToVsHierarchy(this EnvDTE.Project project) + public static async Task ToVsHierarchyAsync(this EnvDTE.Project project) { - ThreadHelper.ThrowIfNotOnUIThread(); + await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); IVsHierarchy hierarchy; // Get the vs solution - var solution = ServiceLocator.GetInstance(); + var solution = await ServiceLocator.GetInstanceAsync(); int hr = solution.GetProjectOfUniqueName(project.GetUniqueName(), out hierarchy); if (hr != VSConstants.S_OK) @@ -41,14 +41,14 @@ namespace NuGet.VisualStudio return hierarchy; } - public static string[] GetProjectTypeGuids(this EnvDTE.Project project) + public static async Task GetProjectTypeGuidsAsync(this EnvDTE.Project project) { Verify.ArgumentIsNotNull(project, nameof(project)); - ThreadHelper.ThrowIfNotOnUIThread(); + await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); // Get the vs hierarchy as an IVsAggregatableProject to get the project type guids - IVsHierarchy hierarchy = ToVsHierarchy(project); + IVsHierarchy hierarchy = await ToVsHierarchyAsync(project); string[] projectTypeGuids = VsHierarchyUtility.GetProjectTypeGuids(hierarchy, project.Kind); return projectTypeGuids; @@ -105,9 +105,9 @@ namespace NuGet.VisualStudio /// /// The project. /// The full path of the project directory. - public static string GetFullPath(this EnvDTE.Project envDTEProject) + public static async Task GetFullPathAsync(this EnvDTE.Project envDTEProject) { - ThreadHelper.ThrowIfNotOnUIThread(); + await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); Debug.Assert(envDTEProject != null); if (IsUnloaded(envDTEProject)) @@ -125,7 +125,7 @@ namespace NuGet.VisualStudio // until we can find one containing the full path. // For website projects, always read FullPath from properties list - if (IsWebProject(envDTEProject)) + if (await IsWebProjectAsync(envDTEProject)) { // FullPath var fullProjectPath = GetPropertyValue(envDTEProject, FullPath); @@ -203,12 +203,12 @@ namespace NuGet.VisualStudio /// Returns the full path of the packages config file associated with the project. /// /// The project. - public static string GetPackagesConfigFullPath(this EnvDTE.Project envDTEProject) + public static async Task GetPackagesConfigFullPathAsync(this EnvDTE.Project envDTEProject) { - ThreadHelper.ThrowIfNotOnUIThread(); + await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); Debug.Assert(envDTEProject != null); - var projectDirectory = GetFullPath(envDTEProject); + var projectDirectory = await GetFullPathAsync(envDTEProject); var packagesConfigFullPath = Path.Combine( projectDirectory ?? string.Empty, @@ -324,11 +324,11 @@ namespace NuGet.VisualStudio } // TODO: Return null for library projects - public static string GetConfigurationFile(this EnvDTE.Project envDTEProject) + public static async Task GetConfigurationFileAsync(this EnvDTE.Project envDTEProject) { - ThreadHelper.ThrowIfNotOnUIThread(); + await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - return IsWebProject(envDTEProject) ? WebConfig : AppConfig; + return await IsWebProjectAsync(envDTEProject) ? WebConfig : AppConfig; } private class PathComparer : IEqualityComparer @@ -412,10 +412,10 @@ namespace NuGet.VisualStudio "Windows Phone OS 7.1".Equals(GetPropertyValue(envDTEProject, xnaPropertyValue), StringComparison.OrdinalIgnoreCase); } - private static bool IsWebProject(this EnvDTE.Project envDTEProject) + private static async Task IsWebProjectAsync(this EnvDTE.Project envDTEProject) { - ThreadHelper.ThrowIfNotOnUIThread(); - string[] types = envDTEProject.GetProjectTypeGuids(); + await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + string[] types = await envDTEProject.GetProjectTypeGuidsAsync(); return types.Contains(VsProjectTypes.WebSiteProjectTypeGuid, StringComparer.OrdinalIgnoreCase) || types.Contains(VsProjectTypes.WebApplicationProjectTypeGuid, StringComparer.OrdinalIgnoreCase); } @@ -427,10 +427,10 @@ namespace NuGet.VisualStudio return envDTEProject.Kind != null && envDTEProject.Kind.Equals(VsProjectTypes.WebSiteProjectTypeGuid, StringComparison.OrdinalIgnoreCase); } - public static bool IsWindowsStoreApp(this EnvDTE.Project envDTEProject) + public static async Task IsWindowsStoreAppAsync(this EnvDTE.Project envDTEProject) { - ThreadHelper.ThrowIfNotOnUIThread(); - string[] types = envDTEProject.GetProjectTypeGuids(); + await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + string[] types = await envDTEProject.GetProjectTypeGuidsAsync(); return types.Contains(VsProjectTypes.WindowsStoreProjectTypeGuid, StringComparer.OrdinalIgnoreCase); } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/IDE/VsHierarchyItem.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/IDE/VsHierarchyItem.cs index ab07f9f81..9928a3e3f 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/IDE/VsHierarchyItem.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/IDE/VsHierarchyItem.cs @@ -2,8 +2,10 @@ // 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; using Microsoft.VisualStudio; +using Microsoft.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; @@ -17,7 +19,7 @@ namespace NuGet.VisualStudio private readonly uint _vsitemid; private readonly IVsHierarchy _hierarchy; - internal delegate int ProcessItemDelegate(VsHierarchyItem item, object callerObject, out object newCallerObject); + internal delegate Task<(int processReturn, object newCallerObject)> ProcessItemDelegateAsync(VsHierarchyItem item, object callerObject); public IVsHierarchy VsHierarchy => _hierarchy; @@ -32,11 +34,10 @@ namespace NuGet.VisualStudio { } - public static VsHierarchyItem FromDteProject(EnvDTE.Project project) + public static async Task FromDteProjectAsync(EnvDTE.Project project) { - ThreadHelper.ThrowIfNotOnUIThread(); Assumes.Present(project); - return new VsHierarchyItem(project.ToVsHierarchy()); + return new VsHierarchyItem(await project.ToVsHierarchyAsync()); } public static VsHierarchyItem FromVsHierarchy(IVsHierarchy project) @@ -59,51 +60,49 @@ namespace NuGet.VisualStudio internal uint VsItemID => _vsitemid; - internal bool IsExpandable() + internal async Task IsExpandableAsync() { - ThreadHelper.ThrowIfNotOnUIThread(); - var o = GetProperty(__VSHPROPID.VSHPROPID_Expandable); + var o = await GetPropertyAsync(__VSHPROPID.VSHPROPID_Expandable); if (o is bool) { return (bool)o; } + return (o is int) && (int)o != 0; } - private object GetProperty(__VSHPROPID propid) + private async Task GetPropertyAsync(__VSHPROPID propid) { - ThreadHelper.ThrowIfNotOnUIThread(); - if (TryGetProperty((int)propid, out var value)) - { - return value; - } - - return null; + (bool _, object value) = await TryGetPropertyAsync((int)propid); + return value; } - private bool TryGetProperty(int propid, out object value) + private async Task<(bool found, object value)> TryGetPropertyAsync(int propid) { - ThreadHelper.ThrowIfNotOnUIThread(); - - value = null; try { if (_hierarchy != null) { - _hierarchy.GetProperty(_vsitemid, propid, out value); + await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + _hierarchy.GetProperty(_vsitemid, propid, out object value); + return (true, value); + } + } + catch (Exception e) + { + if (e.IsCritical()) + { + throw; } - return true; - } - catch - { - return false; + // Ignore. } + + return (false, null); } - internal int WalkDepthFirst(bool fVisible, ProcessItemDelegate processCallback, object callerObject) + internal async Task WalkDepthFirstAsync(bool fVisible, ProcessItemDelegateAsync processCallbackAsync, object callerObject) { - ThreadHelper.ThrowIfNotOnUIThread(); // // TODO Need to see what to do if this is a sub project // @@ -111,13 +110,12 @@ namespace NuGet.VisualStudio // Guid nodeGuid; // if (hier.GetGuidProperty(_vsitemid, (int)__VSHPROPID.VSHPROPID_TypeGuid, out nodeGuid) != 0) - if (processCallback == null) + if (processCallbackAsync == null) { return 0; } - object newCallerObject; - int processReturn = processCallback(this, callerObject, out newCallerObject); + (int processReturn, object newCallerObject) = await processCallbackAsync(this, callerObject); if (processReturn != 0) { // Callback says to skip (1) or stop (-1) @@ -132,44 +130,43 @@ namespace NuGet.VisualStudio } // Walk children if there are any - if (IsExpandable()) + if (await IsExpandableAsync()) { - VsHierarchyItem child = GetFirstChild(fVisible); + VsHierarchyItem child = await GetFirstChildAsync(fVisible); while (child != null) { - object isNonMemberItemValue = child.GetProperty(__VSHPROPID.VSHPROPID_IsNonMemberItem); + object isNonMemberItemValue = await child.GetPropertyAsync(__VSHPROPID.VSHPROPID_IsNonMemberItem); // Some project systems (e.g. F#) don't support querying for the VSHPROPID_IsNonMemberItem property. // In that case, we treat this child as belonging to the project bool isMemberOfProject = isNonMemberItemValue == null || (bool)isNonMemberItemValue == false; if (isMemberOfProject) { - int returnVal = child.WalkDepthFirst(fVisible, processCallback, newCallerObject); + int returnVal = await child.WalkDepthFirstAsync(fVisible, processCallbackAsync, newCallerObject); if (returnVal == -1) { return returnVal; } } - child = child.GetNextSibling(fVisible); + child = await child.GetNextSiblingAsync(fVisible); } } return 0; } - internal VsHierarchyItem GetNextSibling(bool fVisible) + internal async Task GetNextSiblingAsync(bool fVisible) { - ThreadHelper.ThrowIfNotOnUIThread(); - uint childId = GetNextSiblingId(fVisible); + uint childId = await GetNextSiblingIdAsync(fVisible); if (childId != VSConstants.VSITEMID_NIL) { return new VsHierarchyItem(_hierarchy, childId); } + return null; } - internal uint GetNextSiblingId(bool fVisible) + internal async Task GetNextSiblingIdAsync(bool fVisible) { - ThreadHelper.ThrowIfNotOnUIThread(); - object o = GetProperty(fVisible ? __VSHPROPID.VSHPROPID_NextVisibleSibling : __VSHPROPID.VSHPROPID_NextSibling); + object o = await GetPropertyAsync(fVisible ? __VSHPROPID.VSHPROPID_NextVisibleSibling : __VSHPROPID.VSHPROPID_NextSibling); if (o is int) { @@ -179,33 +176,35 @@ namespace NuGet.VisualStudio { return (uint)o; } + return VSConstants.VSITEMID_NIL; } - internal VsHierarchyItem GetFirstChild(bool fVisible) + internal async Task GetFirstChildAsync(bool fVisible) { - ThreadHelper.ThrowIfNotOnUIThread(); - uint childId = GetFirstChildId(fVisible); + uint childId = await GetFirstChildIdAsync(fVisible); if (childId != VSConstants.VSITEMID_NIL) { return new VsHierarchyItem(_hierarchy, childId); } + return null; } - internal uint GetFirstChildId(bool fVisible) + internal async Task GetFirstChildIdAsync(bool fVisible) { - ThreadHelper.ThrowIfNotOnUIThread(); - object o = GetProperty(fVisible ? __VSHPROPID.VSHPROPID_FirstVisibleChild : __VSHPROPID.VSHPROPID_FirstChild); + object o = await GetPropertyAsync(fVisible ? __VSHPROPID.VSHPROPID_FirstVisibleChild : __VSHPROPID.VSHPROPID_FirstChild); if (o is int) { return unchecked((uint)((int)o)); } + if (o is uint) { return (uint)o; } + return VSConstants.VSITEMID_NIL; } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/IDE/VsHierarchyUtility.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/IDE/VsHierarchyUtility.cs index bcf516bfb..05469d04e 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/IDE/VsHierarchyUtility.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/IDE/VsHierarchyUtility.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Threading.Tasks; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; @@ -31,25 +32,23 @@ namespace NuGet.VisualStudio return projectPath; } - public static bool IsSupported(IVsHierarchy hierarchy, string projectTypeGuid) + public static async Task IsSupportedAsync(IVsHierarchy hierarchy, string projectTypeGuid) { - ThreadHelper.ThrowIfNotOnUIThread(); - - if (IsProjectCapabilityCompliant(hierarchy)) + if (await IsProjectCapabilityCompliantAsync(hierarchy)) { return true; } - return !string.IsNullOrEmpty(projectTypeGuid) && SupportedProjectTypes.IsSupported(projectTypeGuid) && !HasUnsupportedProjectCapability(hierarchy); + return !string.IsNullOrEmpty(projectTypeGuid) && ProjectType.IsSupported(projectTypeGuid) && !HasUnsupportedProjectCapability(hierarchy); } /// Check if this project appears to support NuGet. /// IVsHierarchy representing the project in the solution. /// True if NuGet should enable this project, false if NuGet should ignore the project. /// The project may be packages.config or PackageReference. This method does not tell you which. - public static bool IsProjectCapabilityCompliant(IVsHierarchy hierarchy) + public static async Task IsProjectCapabilityCompliantAsync(IVsHierarchy hierarchy) { - ThreadHelper.ThrowIfNotOnUIThread(); + await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); // NOTE: (AssemblyReferences + DeclaredSourceItems + UserSourceItems) exists solely for compatibility reasons // with existing custom CPS-based projects that existed before "PackageReferences" capability was introduced. @@ -126,7 +125,7 @@ namespace NuGet.VisualStudio foreach (var project in projects.Cast()) { var expandedNodes = - GetExpandedProjectHierarchyItems(project); + await GetExpandedProjectHierarchyItemsAsync(project); Debug.Assert(!results.ContainsKey(project.GetUniqueName())); results[project.GetUniqueName()] = new HashSet(expandedNodes); @@ -150,52 +149,49 @@ namespace NuGet.VisualStudio && expandedNodes != null) { - CollapseProjectHierarchyItems(project, expandedNodes); + await CollapseProjectHierarchyItemsAsync(project, expandedNodes); } } } - private static ICollection GetExpandedProjectHierarchyItems(EnvDTE.Project project) + private static async Task> GetExpandedProjectHierarchyItemsAsync(EnvDTE.Project project) { - ThreadHelper.ThrowIfNotOnUIThread(); - - var projectHierarchyItem = VsHierarchyItem.FromDteProject(project); + var projectHierarchyItem = await VsHierarchyItem.FromDteProjectAsync(project); var solutionExplorerWindow = GetSolutionExplorerHierarchyWindow(); if (solutionExplorerWindow == null) { // If the solution explorer is collapsed since opening VS, this value is null. In such a case, simply exit early. - return new VsHierarchyItem[0]; + return Array.Empty(); } var expandedItems = new List(); + await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + // processCallback return values: // 0 continue, // 1 don't recurse into, // -1 stop - projectHierarchyItem.WalkDepthFirst( + await projectHierarchyItem.WalkDepthFirstAsync( fVisible: true, - processCallback: - (VsHierarchyItem vsItem, object callerObject, out object newCallerObject) => + processCallbackAsync: + async (VsHierarchyItem vsItem, object callerObject) => { - newCallerObject = null; - if (IsVsHierarchyItemExpanded(vsItem, solutionExplorerWindow)) + if (await IsVsHierarchyItemExpandedAsync(vsItem, solutionExplorerWindow)) { expandedItems.Add(vsItem); } - return 0; + return (0, null); }, callerObject: null); return expandedItems; } - private static void CollapseProjectHierarchyItems(EnvDTE.Project project, ISet ignoredHierarcyItems) + private static async Task CollapseProjectHierarchyItemsAsync(EnvDTE.Project project, ISet ignoredHierarcyItems) { - ThreadHelper.ThrowIfNotOnUIThread(); - - var projectHierarchyItem = VsHierarchyItem.FromDteProject(project); + var projectHierarchyItem = await VsHierarchyItem.FromDteProjectAsync(project); var solutionExplorerWindow = GetSolutionExplorerHierarchyWindow(); if (solutionExplorerWindow == null) @@ -208,39 +204,35 @@ namespace NuGet.VisualStudio // 0 continue, // 1 don't recurse into, // -1 stop - projectHierarchyItem.WalkDepthFirst( + await projectHierarchyItem.WalkDepthFirstAsync( fVisible: true, - processCallback: - (VsHierarchyItem currentHierarchyItem, object callerObject, out object newCallerObject) => + processCallbackAsync: + async (VsHierarchyItem currentHierarchyItem, object callerObject) => { - newCallerObject = null; if (!ignoredHierarcyItems.Contains(currentHierarchyItem)) { - CollapseVsHierarchyItem(currentHierarchyItem, solutionExplorerWindow); + await CollapseVsHierarchyItemAsync(currentHierarchyItem, solutionExplorerWindow); } - return 0; + return (0, null); }, callerObject: null); } - private static void CollapseVsHierarchyItem(VsHierarchyItem vsHierarchyItem, IVsUIHierarchyWindow vsHierarchyWindow) + private static async Task CollapseVsHierarchyItemAsync(VsHierarchyItem vsHierarchyItem, IVsUIHierarchyWindow vsHierarchyWindow) { - ThreadHelper.ThrowIfNotOnUIThread(); - if (vsHierarchyItem == null || vsHierarchyWindow == null) { return; } + await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); vsHierarchyWindow.ExpandItem(AsVsUIHierarchy(vsHierarchyItem), vsHierarchyItem.VsItemID, EXPANDFLAGS.EXPF_CollapseFolder); } - private static bool IsVsHierarchyItemExpanded(VsHierarchyItem hierarchyItem, IVsUIHierarchyWindow uiWindow) + private static async Task IsVsHierarchyItemExpandedAsync(VsHierarchyItem hierarchyItem, IVsUIHierarchyWindow uiWindow) { - ThreadHelper.ThrowIfNotOnUIThread(); - - if (!hierarchyItem.IsExpandable()) + if (!await hierarchyItem.IsExpandableAsync()) { return false; } @@ -248,6 +240,7 @@ namespace NuGet.VisualStudio const uint expandedStateMask = (uint)__VSHIERARCHYITEMSTATE.HIS_Expanded; uint itemState; + await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); uiWindow.GetItemState(AsVsUIHierarchy(hierarchyItem), hierarchyItem.VsItemID, expandedStateMask, out itemState); return ((__VSHIERARCHYITEMSTATE)itemState == __VSHIERARCHYITEMSTATE.HIS_Expanded); } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/IVsProjectAdapter.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/IVsProjectAdapter.cs index 407e19ed4..9eeaece3a 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/IVsProjectAdapter.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/IVsProjectAdapter.cs @@ -24,7 +24,7 @@ namespace NuGet.VisualStudio /// /// MSBuildProjectExtensionsPath project property (e.g. c:\projFoo\obj) /// - string MSBuildProjectExtensionsPath { get; } + Task GetMSBuildProjectExtensionsPathAsync(); IProjectBuildProperties BuildProperties { get; } @@ -36,7 +36,7 @@ namespace NuGet.VisualStudio bool IsDeferred { get; } - bool IsSupported { get; } + Task IsSupportedAsync(); /// /// Comma or Semicolon separated list of NU* diagnostic codes e.g. NU1000,NU1001 @@ -58,7 +58,7 @@ namespace NuGet.VisualStudio /// /// Full path to a parent directory containing project file. /// - string ProjectDirectory { get; } + Task GetProjectDirectoryAsync(); string ProjectName { get; } diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/ProjectType.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/ProjectType.cs new file mode 100644 index 000000000..666b0ac8c --- /dev/null +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/ProjectType.cs @@ -0,0 +1,91 @@ +// Copyright (c) .NET Foundation. 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.Collections.Generic; +using System.IO; +using System.Linq; + +namespace NuGet.VisualStudio +{ + public static class ProjectType + { + private static readonly HashSet Supported = new HashSet(StringComparer.OrdinalIgnoreCase) + { + VsProjectTypes.WebSiteProjectTypeGuid, + VsProjectTypes.CsharpProjectTypeGuid, + VsProjectTypes.VbProjectTypeGuid, + VsProjectTypes.CppProjectTypeGuid, + VsProjectTypes.JsProjectTypeGuid, + VsProjectTypes.FsharpProjectTypeGuid, + VsProjectTypes.NemerleProjectTypeGuid, + VsProjectTypes.WixProjectTypeGuid, + VsProjectTypes.SynergexProjectTypeGuid, + VsProjectTypes.NomadForVisualStudioProjectTypeGuid, + VsProjectTypes.TDSProjectTypeGuid, + VsProjectTypes.DxJsProjectTypeGuid, + VsProjectTypes.DeploymentProjectTypeGuid, + VsProjectTypes.CosmosProjectTypeGuid, + VsProjectTypes.ManagementPackProjectTypeGuid, + }; + + private static readonly HashSet Unsupported = new HashSet(StringComparer.OrdinalIgnoreCase) + { + VsProjectTypes.LightSwitchProjectTypeGuid, + VsProjectTypes.InstallShieldLimitedEditionTypeGuid + }; + + // List of project types that cannot have binding redirects added + private static readonly HashSet UnsupportedProjectTypesForBindingRedirects = new HashSet(StringComparer.OrdinalIgnoreCase) + { + VsProjectTypes.WixProjectTypeGuid, + VsProjectTypes.JsProjectTypeGuid, + VsProjectTypes.NemerleProjectTypeGuid, + VsProjectTypes.CppProjectTypeGuid, + VsProjectTypes.SynergexProjectTypeGuid, + VsProjectTypes.NomadForVisualStudioProjectTypeGuid, + VsProjectTypes.DxJsProjectTypeGuid, + VsProjectTypes.CosmosProjectTypeGuid, + }; + + // List of project types that cannot have references added to them + private static readonly HashSet UnsupportedProjectTypesForAddingReferences = new HashSet(StringComparer.OrdinalIgnoreCase) + { + VsProjectTypes.WixProjectTypeGuid, + VsProjectTypes.CppProjectTypeGuid, + }; + + private static readonly HashSet UnsupportedProjectExtension = new HashSet(StringComparer.OrdinalIgnoreCase) + { + ".metaproj", + ".shproj", + ".vcxitems", + ".sqlproj" + }; + + public static bool IsSupported(string projectKind) + { + return Supported.Contains(projectKind); + } + + public static bool IsUnsupported(string projectKind) + { + return Unsupported.Contains(projectKind); + } + + public static bool IsSupportedForBindingRedirects(string projectKind) + { + return !UnsupportedProjectTypesForBindingRedirects.Contains(projectKind, StringComparer.OrdinalIgnoreCase); + } + + public static bool IsSupportedForAddingReferences(string projectKind) + { + return !UnsupportedProjectTypesForAddingReferences.Contains(projectKind, StringComparer.OrdinalIgnoreCase); + } + + public static bool IsSupportedProjectExtension(string projectPath) + { + return !UnsupportedProjectExtension.Contains(Path.GetExtension(projectPath)); + } + } +} diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/SupportedProjectTypes.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/SupportedProjectTypes.cs deleted file mode 100644 index 316051b40..000000000 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/SupportedProjectTypes.cs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) .NET Foundation. 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.Collections.Generic; -using System.IO; -using System.Linq; -using NuGet.VisualStudio; - -namespace NuGet.VisualStudio -{ - public static class SupportedProjectTypes - { - private static readonly HashSet _supportedProjectTypes = new HashSet(StringComparer.OrdinalIgnoreCase) - { - VsProjectTypes.WebSiteProjectTypeGuid, - VsProjectTypes.CsharpProjectTypeGuid, - VsProjectTypes.VbProjectTypeGuid, - VsProjectTypes.CppProjectTypeGuid, - VsProjectTypes.JsProjectTypeGuid, - VsProjectTypes.FsharpProjectTypeGuid, - VsProjectTypes.NemerleProjectTypeGuid, - VsProjectTypes.WixProjectTypeGuid, - VsProjectTypes.SynergexProjectTypeGuid, - VsProjectTypes.NomadForVisualStudioProjectTypeGuid, - VsProjectTypes.TDSProjectTypeGuid, - VsProjectTypes.DxJsProjectTypeGuid, - VsProjectTypes.DeploymentProjectTypeGuid, - VsProjectTypes.CosmosProjectTypeGuid, - VsProjectTypes.ManagementPackProjectTypeGuid, - }; - - private static readonly HashSet _unsupportedProjectTypes = new HashSet(StringComparer.OrdinalIgnoreCase) - { - VsProjectTypes.LightSwitchProjectTypeGuid, - VsProjectTypes.InstallShieldLimitedEditionTypeGuid - }; - - // List of project types that cannot have binding redirects added - private static readonly string[] _unsupportedProjectTypesForBindingRedirects = - { - VsProjectTypes.WixProjectTypeGuid, - VsProjectTypes.JsProjectTypeGuid, - VsProjectTypes.NemerleProjectTypeGuid, - VsProjectTypes.CppProjectTypeGuid, - VsProjectTypes.SynergexProjectTypeGuid, - VsProjectTypes.NomadForVisualStudioProjectTypeGuid, - VsProjectTypes.DxJsProjectTypeGuid, - VsProjectTypes.CosmosProjectTypeGuid, - }; - - // List of project types that cannot have references added to them - private static readonly string[] _unsupportedProjectTypesForAddingReferences = - { - VsProjectTypes.WixProjectTypeGuid, - VsProjectTypes.CppProjectTypeGuid, - }; - - private static readonly HashSet _unsupportedProjectExtension = new HashSet(StringComparer.OrdinalIgnoreCase) - { - ".metaproj", - ".shproj", - ".vcxitems", - ".sqlproj" - }; - - public static bool IsSupported(string projectKind) - { - return _supportedProjectTypes.Contains(projectKind); - } - - public static bool IsUnsupported(string projectKind) - { - return _unsupportedProjectTypes.Contains(projectKind); - } - - public static bool IsSupportedForBindingRedirects(string projectKind) - { - return !_unsupportedProjectTypesForBindingRedirects.Contains(projectKind, StringComparer.OrdinalIgnoreCase); - } - - public static bool IsSupportedForAddingReferences(string projectKind) - { - return !_unsupportedProjectTypesForAddingReferences.Contains(projectKind, StringComparer.OrdinalIgnoreCase); - } - - public static bool IsSupportedProjectExtension(string projectPath) - { - return !_unsupportedProjectExtension.Contains(Path.GetExtension(projectPath)); - } - } -} diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Implementation/Extensibility/VsPackageInstaller.cs b/src/NuGet.Clients/NuGet.VisualStudio.Implementation/Extensibility/VsPackageInstaller.cs index 71d85450d..dbe5a9ed0 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Implementation/Extensibility/VsPackageInstaller.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Implementation/Extensibility/VsPackageInstaller.cs @@ -70,7 +70,7 @@ namespace NuGet.VisualStudio { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - IVsHierarchy vsHierarchy = project.ToVsHierarchy(); + IVsHierarchy vsHierarchy = await project.ToVsHierarchyAsync(); if (vsHierarchy != null && VsHierarchyUtility.IsCPSCapabilityCompliant(vsHierarchy)) { diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Implementation/Extensibility/VsPathContextProvider.cs b/src/NuGet.Clients/NuGet.VisualStudio.Implementation/Extensibility/VsPathContextProvider.cs index b40d8df64..f58b38498 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Implementation/Extensibility/VsPathContextProvider.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Implementation/Extensibility/VsPathContextProvider.cs @@ -325,7 +325,15 @@ namespace NuGet.VisualStudio private async Task> GetProjectsInSolutionAsync(DTE dte) { IEnumerable allProjects = await EnvDTESolutionUtility.GetAllEnvDTEProjectsAsync(dte); - IEnumerable supportedProjects = allProjects.Where(EnvDTEProjectUtility.IsSupported); + var supportedProjects = new List(); + foreach (Project project in allProjects) + { + if (await EnvDTEProjectUtility.IsSupportedAsync(project)) + { + supportedProjects.Add(project); + } + } + return supportedProjects; } diff --git a/test/EndToEnd/API.Test.dll b/test/EndToEnd/API.Test.dll new file mode 100644 index 0000000000000000000000000000000000000000..0750fcd34ed5c6fe236ae85113e118f22da804a5 GIT binary patch literal 92160 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 literal 0 HcmV?d00001 diff --git a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/LegacyPackageReferenceProjectTests.cs b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/LegacyPackageReferenceProjectTests.cs index 97b1cfc87..e513ee87d 100644 --- a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/LegacyPackageReferenceProjectTests.cs +++ b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/LegacyPackageReferenceProjectTests.cs @@ -53,8 +53,8 @@ namespace NuGet.PackageManagement.VisualStudio.Test Directory.CreateDirectory(testMSBuildProjectExtensionsPath); var projectAdapter = Mock.Of(); Mock.Get(projectAdapter) - .SetupGet(x => x.MSBuildProjectExtensionsPath) - .Returns(testMSBuildProjectExtensionsPath); + .Setup(x => x.GetMSBuildProjectExtensionsPathAsync()) + .Returns(Task.FromResult(testMSBuildProjectExtensionsPath)); var testProject = new LegacyPackageReferenceProject( projectAdapter, @@ -72,7 +72,7 @@ namespace NuGet.PackageManagement.VisualStudio.Test // Verify Mock.Get(projectAdapter) - .VerifyGet(x => x.MSBuildProjectExtensionsPath, Times.AtLeastOnce); + .Verify(x => x.GetMSBuildProjectExtensionsPathAsync(), Times.AtLeastOnce); } } @@ -107,8 +107,8 @@ namespace NuGet.PackageManagement.VisualStudio.Test Directory.CreateDirectory(testMSBuildProjectExtensionsPath); var projectAdapter = Mock.Of(); Mock.Get(projectAdapter) - .SetupGet(x => x.MSBuildProjectExtensionsPath) - .Returns(testMSBuildProjectExtensionsPath); + .Setup(x => x.GetMSBuildProjectExtensionsPathAsync()) + .Returns(Task.FromResult(testMSBuildProjectExtensionsPath)); Mock.Get(projectAdapter) .SetupGet(x => x.FullProjectPath) @@ -130,7 +130,7 @@ namespace NuGet.PackageManagement.VisualStudio.Test // Verify Mock.Get(projectAdapter) - .VerifyGet(x => x.MSBuildProjectExtensionsPath, Times.AtLeastOnce); + .Verify(x => x.GetMSBuildProjectExtensionsPathAsync(), Times.AtLeastOnce); } } @@ -165,8 +165,8 @@ namespace NuGet.PackageManagement.VisualStudio.Test Directory.CreateDirectory(testMSBuildProjectExtensionsPath); var projectAdapter = Mock.Of(); Mock.Get(projectAdapter) - .SetupGet(x => x.MSBuildProjectExtensionsPath) - .Returns(testMSBuildProjectExtensionsPath); + .Setup(x => x.GetMSBuildProjectExtensionsPathAsync()) + .Returns(Task.FromResult(testMSBuildProjectExtensionsPath)); Mock.Get(projectAdapter) .SetupGet(x => x.FullProjectPath) @@ -186,7 +186,7 @@ namespace NuGet.PackageManagement.VisualStudio.Test // Verify Mock.Get(projectAdapter) - .VerifyGet(x => x.MSBuildProjectExtensionsPath, Times.AtLeastOnce); + .Verify(x => x.GetMSBuildProjectExtensionsPathAsync(), Times.AtLeastOnce); } } @@ -1203,8 +1203,8 @@ namespace NuGet.PackageManagement.VisualStudio.Test var testMSBuildProjectExtensionsPath = Path.Combine(fullPath, "obj"); Directory.CreateDirectory(testMSBuildProjectExtensionsPath); projectAdapter - .Setup(x => x.MSBuildProjectExtensionsPath) - .Returns(testMSBuildProjectExtensionsPath); + .Setup(x => x.GetMSBuildProjectExtensionsPathAsync()) + .Returns(Task.FromResult(testMSBuildProjectExtensionsPath)); return projectAdapter.Object; } diff --git a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/LegacyPackageReferenceRestoreUtilityTests.cs b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/LegacyPackageReferenceRestoreUtilityTests.cs index 656a4bdfa..c996583fb 100644 --- a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/LegacyPackageReferenceRestoreUtilityTests.cs +++ b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/LegacyPackageReferenceRestoreUtilityTests.cs @@ -361,8 +361,9 @@ namespace NuGet.PackageManagement.VisualStudio.Test Assert.True(File.Exists(projectLockFilePath)); // delete existing restore output files - File.Delete(Path.Combine(vsProjectAdapter.MSBuildProjectExtensionsPath, "project.assets.json")); - File.Delete(Path.Combine(vsProjectAdapter.MSBuildProjectExtensionsPath, NoOpRestoreUtilities.NoOpCacheFileName)); + string msBuildProjectExtensionsPath = await vsProjectAdapter.GetMSBuildProjectExtensionsPathAsync(); + File.Delete(Path.Combine(msBuildProjectExtensionsPath, "project.assets.json")); + File.Delete(Path.Combine(msBuildProjectExtensionsPath, NoOpRestoreUtilities.NoOpCacheFileName)); // add a new package var newPackageContext = new SimpleTestPackageContext("packageA", "1.0.1"); @@ -390,7 +391,7 @@ namespace NuGet.PackageManagement.VisualStudio.Test Assert.False(restoreSummary.NoOpRestore); } - var lockFilePath = Path.Combine(vsProjectAdapter.MSBuildProjectExtensionsPath, "project.assets.json"); + var lockFilePath = Path.Combine(msBuildProjectExtensionsPath, "project.assets.json"); Assert.True(File.Exists(lockFilePath)); var lockFile = new LockFileFormat().Read(lockFilePath); @@ -490,9 +491,11 @@ namespace NuGet.PackageManagement.VisualStudio.Test Assert.False(restoreSummary.NoOpRestore); } + string msBuildProjectExtensionsPath = await vsProjectAdapter.GetMSBuildProjectExtensionsPathAsync(); + // Initial asserts Assert.True(File.Exists(projectLockFilePath)); - var assetsFilePath = Path.Combine(vsProjectAdapter.MSBuildProjectExtensionsPath, "project.assets.json"); + var assetsFilePath = Path.Combine(msBuildProjectExtensionsPath, "project.assets.json"); Assert.True(File.Exists(assetsFilePath)); // Assert that there is no warning logged into assets file @@ -500,7 +503,7 @@ namespace NuGet.PackageManagement.VisualStudio.Test Assert.False(assetsFile.LogMessages.Any()); // delete existing restore output files - File.Delete(Path.Combine(vsProjectAdapter.MSBuildProjectExtensionsPath, "project.assets.json")); + File.Delete(Path.Combine(msBuildProjectExtensionsPath, "project.assets.json")); // install a new package projectServices.SetupInstalledPackages( @@ -867,8 +870,9 @@ namespace NuGet.PackageManagement.VisualStudio.Test Assert.True(File.Exists(projectLockFilePath)); // delete existing restore output files - File.Delete(Path.Combine(vsProjectAdapter.MSBuildProjectExtensionsPath, "project.assets.json")); - File.Delete(Path.Combine(vsProjectAdapter.MSBuildProjectExtensionsPath, NoOpRestoreUtilities.NoOpCacheFileName)); + string msBuildProjectExtensionsPath = await vsProjectAdapter.GetMSBuildProjectExtensionsPathAsync(); + File.Delete(Path.Combine(msBuildProjectExtensionsPath, "project.assets.json")); + File.Delete(Path.Combine(msBuildProjectExtensionsPath, NoOpRestoreUtilities.NoOpCacheFileName)); // clean packages folder Directory.Delete(testSolutionManager.GlobalPackagesFolder, true); @@ -1001,7 +1005,7 @@ namespace NuGet.PackageManagement.VisualStudio.Test Assert.True(File.Exists(projectLockFilePath)); // delete existing restore output files - File.Delete(Path.Combine(vsProjectAdapter.MSBuildProjectExtensionsPath, "project.assets.json")); + File.Delete(Path.Combine(await vsProjectAdapter.GetMSBuildProjectExtensionsPathAsync(), "project.assets.json")); // clean packages folder Directory.Delete(testSolutionManager.GlobalPackagesFolder, true); @@ -1153,7 +1157,7 @@ namespace NuGet.PackageManagement.VisualStudio.Test sourceRepositoryProvider.GetRepositories(), sourceRepositoryProvider.GetRepositories(), CancellationToken.None); // Assert - var lockFilePath = Path.Combine(vsProjectAdapterA.MSBuildProjectExtensionsPath, "project.assets.json"); + var lockFilePath = Path.Combine(await vsProjectAdapterA.GetMSBuildProjectExtensionsPathAsync(), "project.assets.json"); Assert.True(File.Exists(lockFilePath)); } @@ -1225,7 +1229,7 @@ namespace NuGet.PackageManagement.VisualStudio.Test sourceRepositoryProvider.GetRepositories(), sourceRepositoryProvider.GetRepositories(), CancellationToken.None); // Assert - var assetsFilePath = Path.Combine(vsProjectAdapterA.MSBuildProjectExtensionsPath, "project.assets.json"); + var assetsFilePath = Path.Combine(await vsProjectAdapterA.GetMSBuildProjectExtensionsPathAsync(), "project.assets.json"); Assert.True(File.Exists(assetsFilePath)); var assetsFile = new LockFileFormat().Read(assetsFilePath); @@ -1346,7 +1350,7 @@ namespace NuGet.PackageManagement.VisualStudio.Test CancellationToken.None); // Assert - var assetsFilePath = Path.Combine(vsProjectAdapterA.MSBuildProjectExtensionsPath, "project.assets.json"); + var assetsFilePath = Path.Combine(await vsProjectAdapterA.GetMSBuildProjectExtensionsPathAsync(), "project.assets.json"); Assert.True(File.Exists(assetsFilePath)); var assetsFile = new LockFileFormat().Read(assetsFilePath); @@ -1827,7 +1831,9 @@ namespace NuGet.PackageManagement.VisualStudio.Test Assert.True(File.Exists(projectLockFilePath)); - var lockFilePath = Path.Combine(vsProjectAdapterA.MSBuildProjectExtensionsPath, "project.assets.json"); + string msBuildProjectExtensionsPathA = await vsProjectAdapterA.GetMSBuildProjectExtensionsPathAsync(); + + var lockFilePath = Path.Combine(msBuildProjectExtensionsPathA, "project.assets.json"); Assert.True(File.Exists(lockFilePath)); var lockFile = new LockFileFormat().Read(lockFilePath); @@ -1835,8 +1841,8 @@ namespace NuGet.PackageManagement.VisualStudio.Test Assert.Equal("1.0.0", resolvedVersion.ToNormalizedString()); // delete existing restore output files - File.Delete(Path.Combine(vsProjectAdapterA.MSBuildProjectExtensionsPath, "project.assets.json")); - File.Delete(Path.Combine(vsProjectAdapterA.MSBuildProjectExtensionsPath, NoOpRestoreUtilities.NoOpCacheFileName)); + File.Delete(Path.Combine(msBuildProjectExtensionsPathA, "project.assets.json")); + File.Delete(Path.Combine(msBuildProjectExtensionsPathA, NoOpRestoreUtilities.NoOpCacheFileName)); //clear packageA 1.0.0 from global packages folder var packageAPath = Path.Combine(testSolutionManager.GlobalPackagesFolder, "packagea", "1.0.0"); @@ -2008,8 +2014,8 @@ namespace NuGet.PackageManagement.VisualStudio.Test Assert.Equal(PackageDependencyType.Project, lockFile.Targets[0].Dependencies[1].Type); // Act - File.Delete(Path.Combine(vsProjectAdapterA.MSBuildProjectExtensionsPath, NoOpRestoreUtilities.NoOpCacheFileName)); - File.Delete(Path.Combine(vsProjectAdapterB.MSBuildProjectExtensionsPath, NoOpRestoreUtilities.NoOpCacheFileName)); + File.Delete(Path.Combine(await vsProjectAdapterA.GetMSBuildProjectExtensionsPathAsync(), NoOpRestoreUtilities.NoOpCacheFileName)); + File.Delete(Path.Combine(await vsProjectAdapterB.GetMSBuildProjectExtensionsPathAsync(), NoOpRestoreUtilities.NoOpCacheFileName)); restoreSummaries = await DependencyGraphRestoreUtility.RestoreAsync( testSolutionManager, @@ -2156,7 +2162,7 @@ namespace NuGet.PackageManagement.VisualStudio.Test Assert.False(restoreSummary.NoOpRestore); } - var assetsFilePath = Path.Combine(vsProjectAdapterA.MSBuildProjectExtensionsPath, "project.assets.json"); + var assetsFilePath = Path.Combine(await vsProjectAdapterA.GetMSBuildProjectExtensionsPathAsync(), "project.assets.json"); Assert.True(File.Exists(assetsFilePath)); var assetsFile = new LockFileFormat().Read(assetsFilePath); @@ -2276,7 +2282,7 @@ namespace NuGet.PackageManagement.VisualStudio.Test Assert.False(restoreSummary.NoOpRestore); } - var assetsFilePath = Path.Combine(vsProjectAdapter.MSBuildProjectExtensionsPath, "project.assets.json"); + var assetsFilePath = Path.Combine(await vsProjectAdapter.GetMSBuildProjectExtensionsPathAsync(), "project.assets.json"); Assert.True(File.Exists(assetsFilePath)); var assetsFile = new LockFileFormat().Read(assetsFilePath); diff --git a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/TestVSProjectAdapter.cs b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/TestVSProjectAdapter.cs index 24378be42..0aa78e06e 100644 --- a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/TestVSProjectAdapter.cs +++ b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/TestVSProjectAdapter.cs @@ -54,12 +54,9 @@ namespace NuGet.PackageManagement.VisualStudio.Test } } - public string MSBuildProjectExtensionsPath + public async Task GetMSBuildProjectExtensionsPathAsync() { - get - { - return Path.Combine(ProjectDirectory, "obj"); - } + return Path.Combine(await GetProjectDirectoryAsync(), "obj"); } public IProjectBuildProperties BuildProperties { get; } = Mock.Of(); @@ -72,7 +69,7 @@ namespace NuGet.PackageManagement.VisualStudio.Test public bool IsDeferred => false; - public bool IsSupported => true; + public Task IsSupportedAsync() => Task.FromResult(true); public string NoWarn { @@ -100,13 +97,7 @@ namespace NuGet.PackageManagement.VisualStudio.Test } } - public string ProjectDirectory - { - get - { - return Path.GetDirectoryName(FullProjectPath); - } - } + public Task GetProjectDirectoryAsync() => Task.FromResult(Path.GetDirectoryName(FullProjectPath)); public string ProjectName => ProjectNames.ShortName; diff --git a/test/TestExtensions/API.Test/InternalAPITestHook.cs b/test/TestExtensions/API.Test/InternalAPITestHook.cs index 251013bc2..1c2f4b5ee 100644 --- a/test/TestExtensions/API.Test/InternalAPITestHook.cs +++ b/test/TestExtensions/API.Test/InternalAPITestHook.cs @@ -156,10 +156,13 @@ namespace API.Test }); } - public static async Task MigrateJsonProject(string projectName) + public static IVsProjectJsonToPackageReferenceMigrateResult MigrateJsonProject(string projectName) { - var migrator = ServiceLocator.GetComponent(); - return (IVsProjectJsonToPackageReferenceMigrateResult)await migrator.MigrateProjectJsonToPackageReferenceAsync(projectName); + return ThreadHelper.JoinableTaskFactory.Run(async () => + { + var migrator = ServiceLocator.GetComponent(); + return (IVsProjectJsonToPackageReferenceMigrateResult)await migrator.MigrateProjectJsonToPackageReferenceAsync(projectName); + }); } public static bool IsFileExistsInProject(string projectUniqueName, string filePath) @@ -178,7 +181,7 @@ namespace API.Test if (!string.IsNullOrEmpty(solutionProjectPath) && PathUtility.GetStringComparerBasedOnOS().Equals(solutionProjectPath, projectUniqueName)) { - return await EnvDTEProjectUtility.ContainsFile(project, filePath); + return await EnvDTEProjectUtility.ContainsFileAsync(project, filePath); } }