Skip to content

Commit

Permalink
Make PackageSpecDependencyProvider consider the version being requested
Browse files Browse the repository at this point in the history
Fixes: NuGet/Home#10368
Re-submit of NuGet#5452

NuGet gets confused with transitive project and package
dependencies with the same identity (name) in a multi-targeting project
and selects the wrong type (project instead of package).

Projects are expected to be preferred over packages, but only when the
version matches. If the version doesn't match, projects shouldn't be
selected in frameworks which don't declare a ProjectReference to those
projects.
  • Loading branch information
ViktorHofer committed Aug 21, 2024
1 parent 1599103 commit 6784f45
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,15 @@ public Library GetLibrary(LibraryRange libraryRange, NuGetFramework targetFramew
// This must exist in the external references
if (_externalProjectsByUniqueName.TryGetValue(name, out ExternalProjectReference externalReference))
{
packageSpec = externalReference.PackageSpec;
if (externalReference.PackageSpec == null ||
libraryRange.VersionRange.FindBestMatch(new NuGetVersion[] { externalReference.PackageSpec.Version }) != null)
{
packageSpec = externalReference.PackageSpec;
}
else
{
externalReference = null;
}
}

if (externalReference == null && packageSpec == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3337,6 +3337,86 @@ public async Task ExecuteAsync_WithLegacyAlgorithmOptIn_ExecutesLegacyAlgorithm(
result.LockFile.PackageSpec.RestoreMetadata.UseLegacyDependencyResolver.Should().BeTrue();
}

[Fact]
public async Task ExecuteAsync_WithConditionalProjectAndPackageReferences_SelectsPackageWhereProjectIsNotAppropriate()
{
// Arrange
using var context = new SourceCacheContext();
using var pathContext = new SimpleTestPathContext();

var mainProject = "main";
var mainProjectPath = Path.Combine(pathContext.SolutionRoot, mainProject);

var systemNumericsVectorName = "System.Numerics.Vectors";
var childProjectPath = Path.Combine(pathContext.SolutionRoot, systemNumericsVectorName);

var mainProjectJson = @"
{
""version"": ""1.0.0"",
""frameworks"": {
""netstandard2.0"": {
""dependencies"": {
""System.Memory"": {
""target"": ""Package"",
""version"": ""[4.5.5, )""
},
""NETStandard.Library"": {
""suppressParent"": ""All"",
""target"": ""Package"",
""version"": ""[2.0.3, )"",
""autoReferenced"": true
}
}
},
""net8.0"": {
""dependencies"": {
}
}
}
}";
PackageSpec systemNumericsVectorPackageSpec = ProjectTestHelpers.GetPackageSpec(systemNumericsVectorName, pathContext.SolutionRoot, "net8.0");
PackageSpec mainPackageSpec = ProjectTestHelpers.GetPackageSpecWithProjectNameAndSpec(mainProject, pathContext.SolutionRoot, mainProjectJson);
var settings = Settings.LoadDefaultSettings(pathContext.SolutionRoot);
mainPackageSpec.RestoreMetadata.ConfigFilePaths = settings.GetConfigFilePaths();
mainPackageSpec.RestoreMetadata.Sources = SettingsUtility.GetEnabledSources(settings).ToList();
mainPackageSpec.RestoreMetadata.FallbackFolders = SettingsUtility.GetFallbackPackageFolders(settings).ToList();
mainPackageSpec.RestoreMetadata.PackagesPath = SettingsUtility.GetGlobalPackagesFolder(settings);

mainPackageSpec.RestoreMetadata.TargetFrameworks.Single(e => e.FrameworkName.Equals(NuGetFramework.Parse("net8.0"))).ProjectReferences.Add(new ProjectRestoreReference()
{
ProjectUniqueName = systemNumericsVectorPackageSpec.RestoreMetadata.ProjectUniqueName,
ProjectPath = systemNumericsVectorPackageSpec.RestoreMetadata.ProjectPath,
});

// create packages
var ns203 = new SimpleTestPackageContext("NETStandard.Library", "2.0.3");
var systemMemory = new SimpleTestPackageContext("System.Memory", "4.5.5");
var systemNumericsVector = new SimpleTestPackageContext(systemNumericsVectorName, "4.4.0");
systemMemory.Dependencies.Add(systemNumericsVector);

await SimpleTestPackageUtility.CreateFolderFeedV3Async(pathContext.PackageSource,
ns203,
systemMemory,
systemNumericsVector);

var logger = new TestLogger();
var request = ProjectTestHelpers.CreateRestoreRequest(pathContext, logger, mainPackageSpec, systemNumericsVectorPackageSpec);

var restoreCommand = new RestoreCommand(request);
RestoreResult result = await restoreCommand.ExecuteAsync();

result.Success.Should().BeTrue(because: logger.ShowMessages());
result.LockFile.LogMessages.Should().BeEmpty();
var net80Target = result.LockFile.Targets.Single(e => e.TargetFramework.Equals(NuGetFramework.Parse("net8.0")));
var netstandard20 = result.LockFile.Targets.Single(e => e.TargetFramework.Equals(NuGetFramework.Parse("netstandard2.0")));
net80Target.Libraries.Should().HaveCount(1);
var net80Vectors = net80Target.Libraries.Single(e => e.Name.Equals(systemNumericsVectorName));
net80Vectors.Version.Should().Be(new NuGetVersion(1, 0, 0));
netstandard20.Libraries.Should().HaveCount(3);
var ns20Target = netstandard20.Libraries.Single(e => e.Name.Equals(systemNumericsVectorName));
ns20Target.Version.Should().Be(new NuGetVersion(4, 4, 0));
}

private static TargetFrameworkInformation CreateTargetFrameworkInformation(List<LibraryDependency> dependencies, List<CentralPackageVersion> centralVersionsDependencies, NuGetFramework framework = null)
{
NuGetFramework nugetFramework = framework ?? new NuGetFramework("net40");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ public TestRestoreRequest(
DependencyGraphSpec.AddProject(project);
DependencyGraphSpec.AddRestore(project.RestoreMetadata.ProjectUniqueName);
AllowNoOp = true;
ProjectStyle = project.RestoreMetadata.ProjectStyle;
}
}
}

0 comments on commit 6784f45

Please sign in to comment.