Skip to content

Conversation

@nkolev92
Copy link
Member

@nkolev92 nkolev92 commented Nov 26, 2025

Bug

Fixes: NuGet/Home#14534
Fixes: NuGet/Home#14536

Description

This PR implements the restore part of NuGet/Home#12124.
The short summary is:

  • Add an assets file format (version 4) that uses the alias as the pivot. Enable that assets file by default in all of code to ensure all tests are testing that code. Switch to the version 3, if SDKAnalysisLevel is 10.0.300 or lower or we have a legacy project.
  • Raise NU1018, whenever duplicate frameworks are attempted to be restored with the old resolver or an older version of the .NET SDK is used (by virtue of global.json usually when nuget.exe or msbuild.exe restore is being used). All of the duplicate framework checks have been moved into RestoreCommand out of SpecValidationUtility for simpicity. They're only done when the version of the .NET SDK doesn't support aliasing, when a legacy project, or we have a legacy algo is being used.

Some more helpers for the review:

  • src/NuGet.Core/NuGet.Build.Tasks/GetReferenceNearestTargetFrameworkTask.cs - We just add the current project's TargetFramework property and use it to pivot off that if there's a conflict. MSBuild equivalent in Add extra parameter to get nearest dotnet/msbuild#12932.
  • In PackageSpecExtensions, I turn on nullabilitty and add the method GetNearestTargetFramework. This is the single place that knows how to disambiguate by alias. Beyond the assets file, this is the meat and potatoes of the change. It is thoroughly tested.
  • In src/NuGet.Core/NuGet.DependencyResolver.Core/Providers/IDependencyProvider.cs, which is what gets called when anyone in restore needs a project or package, we plumb the target alias and use it to disambiguate.
    • src/NuGet.Core/NuGet.Commands/RestoreCommand/SourceRepositoryDependencyProvider.cs is packages, so we don't plumb the alias.
    • src/NuGet.Core/NuGet.ProjectModel/PackageSpecReferenceDependencyProvider.cs is for the projects, so this is where the majority of the work is. This is where GetNearestTargetFramework on a PackageSpec is called.
  • In src/NuGet.Core/NuGet.Commands/RestoreCommand/RequestFactory/RestoreArgs.cs, we downgrade the assets file version to v3 for legacy projects. By setting it here, we ensure it's done in proper restore, but ensuring the tests are actually using the new version.
  • In src/NuGet.Core/NuGet.DependencyResolver.Core/ResolverUtility.cs, the cache key now accounts for the alias as well.

Assets file and dg spec (packagespec) read/write changes pointers:

  • The PackageSpecWriter, which writes the package spec, has a legacy and "new" writer. This writer is used in 3 different places. The assets file file, the dg spec writer (dgspec.json in the obj folder) and the no-op hash. In order to avoid having to version the package spec, we only use the legacy writer in the assets file, when the version is 3. This ensures all of our tests are actually covering the new codepath and we don't accidentally break the legacy projects. The way the package spec writer is implemented probably allows the legacy projects to have v4 of the assets file, but I'd rather do that at a later point to minimize the risk of this current change.

  • Every time we write the package spec now, we write a "framework", which is basically the effective framework. For simplicity, we write this every time.

  • LockFileTarget.Name is what's being written as the key, so in LockFileBuilder, we set the Name based on the version. Similarly, the LockFileTarget reader, v3 and v4, will set the name based on the version. If V3, we fill out the TargetAlias, if V4, we fill out the Framework (which is not longer written).

  • In LockFileFormat, the default version is 4. Whenever we use V4 of the LockFileFormat (or assets file), we use the default package spec writer, with version 3, we use the legacy writer.

  • I cleaned up any global suppressions whenever I saw it fit. The fewer of these we have, the better.

PR Checklist

  • Meaningful title, helpful description and a linked NuGet/Home issue
  • Added tests
  • Link to an issue or pull request to update docs if this PR changes settings, environment variables, new feature, etc.

@nkolev92 nkolev92 force-pushed the dev-nkolev92-supportV4Format branch 3 times, most recently from 4827827 to e7cded1 Compare December 9, 2025 08:41
@nkolev92 nkolev92 force-pushed the dev-nkolev92-supportV4Format branch 3 times, most recently from 7cbe95e to 863e076 Compare December 10, 2025 17:28
JanProvaznik pushed a commit to dotnet/msbuild that referenced this pull request Dec 19, 2025
Fixes #

### Context

Design: NuGet/Home#12124

To allow duplicate frameworks in aliasing, the project reference
protocol nearest framework selection needs to be updated to support
matching by alias as well.

Relevant part: 

https://github.com/NuGet/Home/blob/dev-nkolev92-tfmaliases/accepted/2025/Multiple-Equivalent-Framework-Support-TFM-As-Aliases.md#project-to-project-references

NuGet/NuGet.Client#7011 NuGet.Client side
adding the parameter.
NuGet/NuGet.Client#6972 will add the full
implementation at a later point.

### Changes Made

- Pass CurrentProjectTargetFrameworkProperty if
GetReferenceNearestTargetFrameworkTaskSupportsTargetFrameworKPropertyParameter
is set.
- If
GetReferenceNearestTargetFrameworkTaskSupportsTargetFrameworKPropertyParameter
is not set, but
GetReferenceNearestTargetFrameworkTaskSupportsTargetPlatformParameter is
set, call the old variation.
- Otherwise calls the last variation.

### Testing

- Manual testing.
- I'd be happy to add tests if someone can point me in the right
direction.
### Notes

The idea here is to get ahead of things. Currently aliasing work can't
be end to end tested because it requires an msbuild change. It makes it
really hard to validate the NuGet changes are enough and good, but this
is the only change needed on the msbuild side.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@dotnet-policy-service dotnet-policy-service bot added the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label Dec 20, 2025
@nkolev92 nkolev92 reopened this Jan 15, 2026
@dotnet-policy-service dotnet-policy-service bot removed the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label Jan 15, 2026
@nkolev92 nkolev92 force-pushed the dev-nkolev92-supportV4Format branch 4 times, most recently from 2c2a38f to 2d62c8b Compare January 16, 2026 22:18
@nkolev92 nkolev92 force-pushed the dev-nkolev92-supportV4Format branch 3 times, most recently from 91829c0 to 1d505fc Compare January 24, 2026 00:57
@nkolev92 nkolev92 requested a review from Copilot January 26, 2026 19:26
@nkolev92 nkolev92 force-pushed the dev-nkolev92-supportV4Format branch from 1d505fc to 0db929a Compare January 26, 2026 19:30
@nkolev92 nkolev92 marked this pull request as ready for review January 26, 2026 19:30
@nkolev92 nkolev92 requested a review from a team as a code owner January 26, 2026 19:30
Copy link
Member

@zivkan zivkan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just finished going through the product code, but haven't started looking at the tests yet. Thought I'd share my comments so far.

@nkolev92 nkolev92 requested review from jeffkl and zivkan January 30, 2026 01:52
zivkan
zivkan previously approved these changes Jan 30, 2026
Copy link
Member

@zivkan zivkan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always find tests hard to review. They all look so similar, making it hard to see the differences between them. I don't have any ideas how to make it better though.

Copy link
Contributor

@jeffkl jeffkl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for undoing the breaking changes

@jeffkl jeffkl self-requested a review January 30, 2026 18:46
@jeffkl jeffkl dismissed their stale review January 30, 2026 18:46

because

@nkolev92
Copy link
Member Author

@jeffkl, @zivkan I think it's ready for a final review.

@nkolev92 nkolev92 enabled auto-merge (squash) January 30, 2026 19:46
zivkan
zivkan previously approved these changes Jan 30, 2026
jeffkl
jeffkl previously approved these changes Jan 30, 2026
@nkolev92 nkolev92 dismissed stale reviews from jeffkl and zivkan via d5fb7af January 30, 2026 20:17
@nkolev92 nkolev92 requested review from jeffkl and zivkan January 30, 2026 20:18
@nkolev92 nkolev92 merged commit fbcb617 into dev Jan 30, 2026
17 of 18 checks passed
@nkolev92 nkolev92 deleted the dev-nkolev92-supportV4Format branch January 30, 2026 21:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ensure the .NET SDK can handled aliased assets file Change the assets file format to support aliasing

5 participants