Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ public async Task<NuGetProject> TryCreateNuGetProjectAsync(
return null;
}

// Check if the project is not CPS capable or if it is CPS capable that its opt'd in PackageReferences
if (!VsHierarchyUtility.IsCPSCapabilityCompliant(hierarchy) ||
!VsHierarchyUtility.IsProjectCapabilityCompliant(hierarchy))
// Check that the project supports both CPS and PackageReferences
if (!(await vsProject.IsCapabilityMatchAsync(NuGet.VisualStudio.IDE.ProjectCapabilities.Cps) &&
await vsProject.IsCapabilityMatchAsync(NuGet.VisualStudio.IDE.ProjectCapabilities.PackageReferences)))
{
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Runtime.Versioning;
using System.Threading.Tasks;
using Microsoft;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using NuGet.Commands;
using NuGet.Common;
Expand Down Expand Up @@ -444,6 +445,13 @@ private async Task<string> GetTargetFrameworkStringAsync()
return frameworkStrings.FirstOrDefault();
}

public async Task<bool> IsCapabilityMatchAsync(string capabilityExpression)
{
await _threadingService.JoinableTaskFactory.SwitchToMainThreadAsync();

return VsHierarchy.IsCapabilityMatch(capabilityExpression);
}

#endregion Getters
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// 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.

namespace NuGet.VisualStudio.IDE
{
public static class ProjectCapabilities
{
/// <summary>
/// All CPS projects will have CPS capability except VisualC projects.
/// So checking for VisualC explicitly with a OR flag.
/// </summary>
/// <remarks>This does not mean the project uses PackageReference.</remarks>
public const string Cps = "CPS | VisualC";

/// <summary>
/// Capability string for projects that want to support PackageReferences
/// </summary>
/// <remarks>There are multiple project systems that support PackageReferences, we can't assume which one without more information.</remarks>
public const string PackageReferences = "PackageReferences";

/// <summary>
/// Capability expression to try to determine if the (MSBuild based) project supports NuGet.
/// </summary>
/// <remarks>
/// VS project systems that are not MSBuild based (project.json) will not get detected by this.
/// Similarly, there are multiple project systems that match this expression, so additional information is needed to determine which project system.
/// </remarks>
public const string SupportsNuGet = "(AssemblyReferences + DeclaredSourceItems + UserSourceItems) | PackageReferences";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,17 @@ public static bool IsSupported(IVsHierarchy hierarchy, string projectTypeGuid)
return !string.IsNullOrEmpty(projectTypeGuid) && SupportedProjectTypes.IsSupported(projectTypeGuid) && !HasUnsupportedProjectCapability(hierarchy);
}

/// <summary>Check if this project appears to support NuGet.</summary>
/// <param name="hierarchy">IVsHierarchy representing the project in the solution.</param>
/// <returns>True if NuGet should enable this project, false if NuGet should ignore the project.</returns>
/// <remarks>The project may be packages.config or PackageReference. This method does not tell you which.</remarks>
public static bool IsProjectCapabilityCompliant(IVsHierarchy hierarchy)
{
ThreadHelper.ThrowIfNotOnUIThread();

// NOTE: (AssemblyReferences + DeclaredSourceItems + UserSourceItems) exists solely for compatibility reasons
// with existing custom CPS-based projects that existed before "PackageReferences" capability was introduced.
return hierarchy.IsCapabilityMatch("(AssemblyReferences + DeclaredSourceItems + UserSourceItems) | PackageReferences");
return hierarchy.IsCapabilityMatch(IDE.ProjectCapabilities.SupportsNuGet);
}

public static bool HasUnsupportedProjectCapability(IVsHierarchy hierarchy)
Expand Down Expand Up @@ -80,14 +84,14 @@ public static string[] GetProjectTypeGuids(IVsHierarchy hierarchy, string defaul
}

/// <summary>
/// Check for CPS capability in IVsHierarchy. All CPS projects will have CPS capability except VisualC projects.
/// So checking for VisualC explicitly with a OR flag.
/// Check for CPS capability in IVsHierarchy.
/// </summary>
/// <remarks>This does not mean the project also supports PackageReference!</remarks>
public static bool IsCPSCapabilityCompliant(IVsHierarchy hierarchy)
{
ThreadHelper.ThrowIfNotOnUIThread();

return hierarchy.IsCapabilityMatch("CPS | VisualC");
return hierarchy.IsCapabilityMatch(IDE.ProjectCapabilities.Cps);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,5 +159,10 @@ public interface IVsProjectAdapter
/// <param name="metadataNames">The metadata names to read.</param>
/// <returns>An <see cref="IEnumerable{(string ItemId, string[] ItemMetadata)}"/> containing the itemId and the metadata values.</returns>
Task<IEnumerable<(string ItemId, string[] ItemMetadata)>> GetBuildItemInformationAsync(string itemName, params string[] metadataNames);

/// <summary>
/// See <see cref="Microsoft.VisualStudio.Shell.PackageUtilities.IsCapabilityMatch(IVsHierarchy, string)"/>
/// </summary>
Task<bool> IsCapabilityMatchAsync(string capabilityExpression);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
<Compile Include="ProjectSystems\ProjectKNuGetProjectTests.cs" />
<Compile Include="ProjectSystems\ProjectSystemCacheTests.cs" />
<Compile Include="ProjectSystems\TestVSProjectAdapter.cs" />
<Compile Include="Projects\CpsPackageReferenceProjectProviderTests.cs" />
<Compile Include="Services\NuGetLockServiceTests.cs" />
<Compile Include="Services\NuGetProjectManagerServiceTests.cs" />
<Compile Include="Services\TestSharedServiceState.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,5 +246,10 @@ public async Task<string> GetPropertyValueAsync(string propertyName)

return string.Empty;
}

public Task<bool> IsCapabilityMatchAsync(string capabilityExpression)
{
throw new NotImplementedException();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// 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.Threading.Tasks;
using Microsoft;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Threading;
using Moq;
using NuGet.ProjectManagement;
using NuGet.VisualStudio;
using Test.Utility.Threading;
using Xunit;

namespace NuGet.PackageManagement.VisualStudio.Test.Projects
{
[Collection(DispatcherThreadCollection.CollectionName)]
public class CpsPackageReferenceProjectProviderTests
{
private readonly JoinableTaskFactory _jtf;

public CpsPackageReferenceProjectProviderTests(DispatcherThreadFixture fixture)
{
Assumes.Present(fixture);

_jtf = fixture.JoinableTaskFactory;

NuGetUIThreadHelper.SetCustomJoinableTaskFactory(_jtf);
}

// As of October 2020, Service Fabric projects (sfproj) uses CPS, but does not support PackageReference. Make sure non-PR CPS projects do not use this project system.
[Fact]
public async Task TryCreateNuGetProjectAsync_CpsProjectWithoutPackageReferencesCapability_ReturnsNull()
{
// Arrange
var hierarchy = new Mock<IVsHierarchy>();

var projectAdapter = new Mock<IVsProjectAdapter>();
projectAdapter.SetupGet(a => a.VsHierarchy)
.Returns(hierarchy.Object);
projectAdapter.Setup(a => a.IsCapabilityMatchAsync(NuGet.VisualStudio.IDE.ProjectCapabilities.Cps))
.Returns(Task.FromResult(true));
projectAdapter.Setup(a => a.IsCapabilityMatchAsync(NuGet.VisualStudio.IDE.ProjectCapabilities.PackageReferences))
.Returns(Task.FromResult(false));

var nugetProjectContext = new Mock<INuGetProjectContext>();

var ppc = new ProjectProviderContext(nugetProjectContext.Object, packagesPathFactory: () => throw new NotImplementedException());

var projectSystemCache = new Mock<IProjectSystemCache>();
var target = new CpsPackageReferenceProjectProvider(projectSystemCache.Object);

// Act
NuGetProject actual = await _jtf.RunAsync(async () =>
{
await _jtf.SwitchToMainThreadAsync();
return await target.TryCreateNuGetProjectAsync(projectAdapter.Object, ppc, forceProjectType: false);
});

// Assert
Assert.Null(actual);
projectAdapter.Verify(a => a.IsCapabilityMatchAsync(NuGet.VisualStudio.IDE.ProjectCapabilities.Cps), Times.Once);
projectAdapter.Verify(a => a.IsCapabilityMatchAsync(NuGet.VisualStudio.IDE.ProjectCapabilities.PackageReferences), Times.Once);
}
}
}