-
Notifications
You must be signed in to change notification settings - Fork 696
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make PackageSpecDependencyProvider consider the version being requested #5985
base: dev
Are you sure you want to change the base?
Conversation
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.
The following entry gets added to a project's project.assets.json file with this change: NuGet apparently records that the project node can't be selected and therefore prefers the package one. I don't think that's a big deal but it would be better if that project node wouldn't exist at all. Meaning, this is a good change overall but we should file a follow-up issue to make NuGet not even consider the project node (because the project doesn't have a P2P, even transitively, to it). |
I tested this change on top of my PR in runtime with the following steps on Windows: mkdir temp-git
cd temp-git
# Prep nuget-client
git clone https://github.com/ViktorHofer/NuGet.Client
cd NuGet.Client
git checkout 10368fix
.\configure.cmd
.\build.cmd
# The build eventually fails to invoke some tests because of a missing 8.0.100 SDK (at least on my machine) but that isn't interesting for this validation.
# Prep runtime
cd ..
git clone https://github.com/ViktorHofer/runtime
cd runtime
git checkout UseP2PsEverywhere
# Restore runtime and the SDK and notice the NU1201 errors
.\build.cmd -restore
# Patch the local SDK
cp ..\NuGet.Client\artifacts\NuGet.Versioning\bin\Debug\netstandard2.0\NuGet.Versioning.dll .dotnet\sdk\9.0.100-preview.7.24407.12\
cp ..\NuGet.Client\artifacts\NuGet.Packaging\bin\Debug\netcoreapp5.0\NuGet.Packaging.dll .dotnet\sdk\9.0.100-preview.7.24407.12\
cp ..\NuGet.Client\artifacts\NuGet.ProjectModel\bin\Debug\netcoreapp5.0\NuGet.ProjectModel.dll .dotnet\sdk\9.0.100-preview.7.24407.12\
cp ..\NuGet.Client\artifacts\NuGet.Protocol\bin\Debug\netcoreapp5.0\NuGet.Protocol.dll .dotnet\sdk\9.0.100-preview.7.24407.12\
cp ..\NuGet.Client\artifacts\NuGet.Credentials\bin\Debug\netcoreapp5.0\NuGet.Credentials.dll .dotnet\sdk\9.0.100-preview.7.24407.12\
cp ..\NuGet.Client\artifacts\NuGet.DependencyResolver.Core\bin\Debug\netcoreapp5.0\NuGet.DependencyResolver.Core.dll .dotnet\sdk\9.0.100-preview.7.24407.12\
cp ..\NuGet.Client\artifacts\NuGet.Frameworks\bin\Debug\netstandard2.0\NuGet.Frameworks.dll .dotnet\sdk\9.0.100-preview.7.24407.12\
cp ..\NuGet.Client\artifacts\NuGet.LibraryModel\bin\Debug\netstandard2.0\NuGet.LibraryModel.dll .dotnet\sdk\9.0.100-preview.7.24407.12\
cp ..\NuGet.Client\artifacts\NuGet.Commands\bin\Debug\netcoreapp5.0\NuGet.Commands.dll .dotnet\sdk\9.0.100-preview.7.24407.12\
cp ..\NuGet.Client\artifacts\NuGet.Common\bin\Debug\netstandard2.0\NuGet.Common.dll .dotnet\sdk\9.0.100-preview.7.24407.12\
cp ..\NuGet.Client\artifacts\NuGet.Configuration\bin\Debug\netstandard2.0\NuGet.Configuration.dll .dotnet\sdk\9.0.100-preview.7.24407.12\
cp ..\NuGet.Client\artifacts\NuGet.Build.Tasks.Console\bin\Debug\net8.0\NuGet.Build.Tasks.Console.dll .dotnet\sdk\9.0.100-preview.7.24407.12\
cp ..\NuGet.Client\artifacts\NuGet.Build.Tasks\bin\Debug\net8.0\NuGet.Build.Tasks.dll .dotnet\sdk\9.0.100-preview.7.24407.12\
cp ..\NuGet.Client\artifacts\NuGet.CommandLine.XPlat\bin\Debug\net8.0\NuGet.CommandLine.XPlat.dll .dotnet\sdk\9.0.100-preview.7.24407.12\
# Restore runtime without any errors
.\build.cmd -restore |
The test failure in CI is unrelated to this change. |
@nkolev92 is this something you can help review? Please let us know if you'd like to see additional changes or validation here. Thank you 🙇♂️ |
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)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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)); | |
var request = ProjectTestHelpers.CreateRestoreRequest(pathContext, logger, mainPackageSpec, systemNumericsVectorPackageSpec); | |
var restoreCommand = new RestoreCommand(request); | |
// Act | |
RestoreResult result = await restoreCommand.ExecuteAsync(); | |
// Assert | |
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)); |
@@ -101,7 +101,17 @@ public Library GetLibrary(LibraryRange libraryRange, NuGetFramework targetFramew | |||
// This must exist in the external references | |||
if (_externalProjectsByUniqueName.TryGetValue(name, out ExternalProjectReference externalReference)) | |||
{ | |||
packageSpec = externalReference.PackageSpec; | |||
// A library with a null version range means that all versions should match. | |||
if (externalReference.PackageSpec == null || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this check: externalReference.PackageSpec == null
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nkolev92 had it in the original PR. This check guards against null before the PackageSpec
is dereferenced in the later check externalReference.PackageSpec.Version
.
if (_externalProjectsByUniqueName.TryGetValue(name, out ExternalProjectReference externalReference)) | ||
{ | ||
packageSpec = externalReference.PackageSpec; | ||
// A library with a null version range means that all versions should match. | ||
if (externalReference.PackageSpec == null || | ||
libraryRange.VersionRange == null || | ||
libraryRange.VersionRange.FindBestMatch(new NuGetVersion[] { externalReference.PackageSpec.Version }) != null) | ||
{ | ||
packageSpec = externalReference.PackageSpec; | ||
} | ||
else | ||
{ | ||
externalReference = null; | ||
} | ||
} | ||
|
||
if (externalReference == null && packageSpec == null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (_externalProjectsByUniqueName.TryGetValue(name, out ExternalProjectReference externalReference)) | |
{ | |
packageSpec = externalReference.PackageSpec; | |
// A library with a null version range means that all versions should match. | |
if (externalReference.PackageSpec == null || | |
libraryRange.VersionRange == null || | |
libraryRange.VersionRange.FindBestMatch(new NuGetVersion[] { externalReference.PackageSpec.Version }) != null) | |
{ | |
packageSpec = externalReference.PackageSpec; | |
} | |
else | |
{ | |
externalReference = null; | |
} | |
} | |
if (externalReference == null && packageSpec == null) | |
if (!_externalProjectsByUniqueName.TryGetValue(name, out ExternalProjectReference externalReference)) | |
{ | |
// No project found | |
return null; | |
} | |
if (externalReference.PackageSpec != null && | |
libraryRange.VersionRange != null && | |
libraryRange.VersionRange.FindBestMatch(new NuGetVersion[] { externalReference.PackageSpec.Version }) == null)) | |
{ | |
// The package does not have a project with a version that best matches the range | |
return null; | |
} | |
packageSpec = externalReference.PackageSpec; | |
This PR has been automatically marked as stale because it has no activity for 7 days. It will be closed if no further activity occurs within another 90 days of this comment. If it is closed, you may reopen it anytime when you're ready again, as long as you don't delete the branch. |
Bug
Fixes: NuGet/Home#10368
Re-submission of #5452
Description
dotnet/runtime#106329 is currently blocked because of this NuGet tooling bug.
NuGet gets confused with transitive project and package dependencies with the same identity (name) in a multi-targeting project. It incorrectly 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.PR Checklist
cc @nkolev92
I'm happy to do as much validation as necessary. Please understand that this defect currently blocks one of our planned infrastructure modernization items. We can't merge the runtime PR until this fix flows into both the .NET SDK and VS and we can depend on the newer toolchain as otherwise restore would fail.