Skip to content

Conversation

@nkolev92
Copy link
Member

@nkolev92 nkolev92 commented Oct 3, 2022

Design for #5154

Rendered

cc @dsplaisted

Copy link

@Nirmal4G Nirmal4G left a comment

Choose a reason for hiding this comment

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

Some ideas and suggestions. Feel free to ignore it. 😅🙃

@nkolev92
Copy link
Member Author

nkolev92 commented Oct 4, 2022

Thanks for taking a look @Nirmal4G

For context, this is design I'm still working on, I'd say it's at about 50%.

Hoping to add more details about the scenarios and just in general provide more concrete proposals soon.

@Nirmal4G
Copy link

Nirmal4G commented Oct 4, 2022

@nkolev92 These are some of my observations and preliminary suggestions, ideas and takeaways from this draft. I'm not taking this as a concrete proposal. I'm just viewing this from a 10-foot view (or design perspective) of the proposal to see how it could impact normal as well as advanced users. In that mindset, read through my comments again. You would then see what I was talking about.

@Nirmal4G
Copy link

Nirmal4G commented Oct 4, 2022

Hoping to add more details about the scenarios and just in general provide more concrete proposals soon.

Sure, Will look though once you mark this ready for review. Sorry for the uninvited comments. I'll stop now.

@nkolev92
Copy link
Member Author

nkolev92 commented Oct 4, 2022

Hoping to add more details about the scenarios and just in general provide more concrete proposals soon.

Sure, Will look though once you mark this ready for review. Sorry for the uninvited comments. I'll stop now.

Oh no worries. I appreciate the feedback as this is a large doc so getting feedback early on is always great.

My comment was more about that fact that I'll be adding even more info to the doc, setting expectations about when something like this would even be close to happening :)

@JonDouglas
Copy link
Contributor

@ViktorHofer & @ericstj

@zivkan zivkan self-assigned this Nov 13, 2023
@zivkan zivkan marked this pull request as ready for review November 15, 2023 13:27
@zivkan zivkan requested a review from a team as a code owner November 15, 2023 13:27
@zivkan zivkan force-pushed the dev-nkolev92-tfmaliases branch from 55c6bba to 1c19625 Compare November 23, 2023 08:55
@ghost
Copy link

ghost commented Jan 11, 2024

This PR has been automatically marked as stale because it has no activity for 30 days. It will be closed if no further activity occurs within another 15 days of this comment, unless it has a "Status:Do not auto close" label. If it is closed, you may reopen it anytime when you're ready again, as long as you don't delete the branch.

@zivkan
Copy link
Member

zivkan commented Jan 26, 2024

oops, vacation and other priorities has kept this feature on the backburner, but it's still planned.

@Nigusu-Allehu Nigusu-Allehu added the Status:Do not auto close Do not auto close for PRs needs long review process label Jun 4, 2024
@zivkan zivkan marked this pull request as draft July 23, 2024 22:34
@nkolev92 nkolev92 force-pushed the dev-nkolev92-tfmaliases branch from 1c19625 to ddc7fae Compare September 16, 2025 18:27
@nkolev92 nkolev92 force-pushed the dev-nkolev92-tfmaliases branch from ddc7fae to 413d370 Compare November 26, 2025 17:58
@nkolev92 nkolev92 assigned nkolev92 and unassigned zivkan Dec 1, 2025
@nkolev92 nkolev92 changed the title Design for allowing restore of multiple equivalent frameworks Design for allowing restore of multiple equivalent frameworks (Target Frameworks as Aliases) Dec 12, 2025
@nkolev92 nkolev92 force-pushed the dev-nkolev92-tfmaliases branch from 650ca9a to b6c47f6 Compare December 12, 2025 18:39
@nkolev92 nkolev92 marked this pull request as ready for review December 12, 2025 18:42
@nkolev92 nkolev92 requested a review from baronfel December 12, 2025 18:43
@nkolev92
Copy link
Member Author

@ericstj @ViktorHofer @dsplaisted @baronfel @zivkan @jeffkl

This is ready for review.

It's a complicated, but I'd love to get through it async as much as possible because of the length itself :D

dsplaisted
dsplaisted previously approved these changes Dec 12, 2025

PackageReference is also supported by non-SDK style projects, which use [dotnet/NuGet.BuildTools](https://github.com/NuGet.BuildTools) which, despite the name, is owned by the non-SDK project system team, not NuGet.
The changes to the assets file that affect legacy projects will be done in a non-breaking way and as such, changes should not be required there.
NuGet will utilize the SDKAnalysisLevel property when it writes out an assets file with breaking changes, ensuring that the .NET SDK will be able to read the assets file for the build.
Copy link
Member

Choose a reason for hiding this comment

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

Customers are expected to change SDKAnalysisLevel if they have build errors when upgrading to a newer SDK. I can't remember the name of the property, but there's an SDK version that feels more appropriate for what this is aiming to achieve.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, there's an SDK version property for sure, we raise telemetry on that, but I see SDKAnalysisLevel to mean, behave like the previous SDK version, and to me that'd imply the older restore behavior in general.

I don't feel too strongly though, and can easily be convinced otherwise.

Copy link
Member

Choose a reason for hiding this comment

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

In my mind, SdkAnalysisLevel is about warnings and errors (diagnostics), not features, and looking at the original spec, I feel my opinion is reinforced: https://github.com/dotnet/designs/blob/main/proposed/sdk-analysis-level.md

The spec doesn't say it, but I always thought SdkAnalysisLevel was inspired by Roslyn's AnalysisLevel, and Roslyn also has LangVersion which I consider to be the feature toggle. The SDK/NuGet doesn't have a "global feature toggle" like Roslyn does with LangVersion. Using SdkAnalysisLevel is a choice that can be made, but then customers lose the ability to enable new features while disabling new diagnostics. I guess we need to decide whether that's an important distinction to allow customers to have.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think Chet has a good compass when it comes to SDKAnalysisLevel.

Curious what you think @baronfel

tldr;
NuGet will introduce a V4 of the assets file.
It requires a new .NET SDK reader, ie a specific .NET SDK version.

Should that "switch" be based off of the SDKAnalysisLevel or NETCoreSdkVersion property.
More context on the pros and cons in the above comments.

Copy link
Member Author

Choose a reason for hiding this comment

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

@baronfel attempted to post the below, but was facing GH problems:


The intent of SdkAnalysisLevel is to easily apply behaviors to all kinds of SDK-level analysis - not just Roslyn Analyzers, but built-in diagnostics, MSBuild BuildChecks, and similar. Any kind of validation that we do during a build was in scope.

This has evolved slightly to controlling behaviors as well - I am entirely supportive of this. The main reason for this evolution/broadening of meaning is that the user-facing view of this property is often "make the current SDK behave like the SDK band I tell you to". With this lens, applying SdkAnalysisLevel to behaviors in addition to validations is entirely appropriate.


As previously shown in [the assets file pivots changes](#target-framework-pivots), NuGet uses `tfm/rid` as the property name in the `$/targets` object.
Therefore, a `TargetFramework` that includes a `/` will cause parsing challenges.
NuGet's libraries could attempt to do the split on the last `/` character.
Copy link
Member

Choose a reason for hiding this comment

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

Considering the assets file has an entry for the ridless target, in addition to the per-rid targets, I think it would be challening to tell the difference between the two.

If the parser keeps raw strings in memory and then parses the alias/rid only once all targets are parsed from json, then you can find what the max number of / are, and assume the ridless has n-1.

But I think it's easier if we fail restore if the alias has a /. It's easier to block first and later allow, than the other way around.

Since we're already incrementing the version number, I think it'd be ideal if we could make another breaking change to the format, so instead of

"targets": {
   "alias" : {  },
   "alias/rid": {  }
}

we could instead have

"targets": {
   "alias": {
     "": { },
     "rid": {  }
  }
}

But I suspect this might be a lot harder to implement.

However, as is pointed out just below, the target alias is used as the directory name for the obj and bin directories, and / is a directory separator, so I think that's another reason we could just block / from the alias

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, I'd be fine with blocking that yeah.
Honestly, I can't image this to be a super critical scenario that would need to be addressed.
The more changes we make, the higher the risk of the change anyways.

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>
Copy link
Contributor

@donnie-msft donnie-msft left a comment

Choose a reason for hiding this comment

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

I had a couple questions, and left a few nits.
Suggest having Copilot review spelling/etc and also telling it to put each sentence on a new line in the markdown, as I know Andy prefers that.

- Disabling pack requires setting three properties. Should we aim to reduce this number?
- Are there any concerns regarding the changes to the [ProjectReference](#project-to-project-references) protocol?
- Should the alias matching require the frameworks to be fully equivalent? My instinct is no, since we'd potentially like to allow the test projects to have a newer framework than the libraries.
- Should target alias based matching be consider when applying AssetTargetFallback as well?
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
- Should target alias based matching be consider when applying AssetTargetFallback as well?
- Should target alias based matching be considered when applying AssetTargetFallback as well?

- Are there any concerns regarding the changes to the [ProjectReference](#project-to-project-references) protocol?
- Should the alias matching require the frameworks to be fully equivalent? My instinct is no, since we'd potentially like to allow the test projects to have a newer framework than the libraries.
- Should target alias based matching be consider when applying AssetTargetFallback as well?
Example: P1 targets net10.0 with alias apple, P2 targets net472 (apple) and net472 (banana). Should the matching allow net10.0 to match net472 (apple) or should it fail due to ambiguity?
Copy link
Contributor

Choose a reason for hiding this comment

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

nit

Suggested change
Example: P1 targets net10.0 with alias apple, P2 targets net472 (apple) and net472 (banana). Should the matching allow net10.0 to match net472 (apple) or should it fail due to ambiguity?
Example: P1 targets net10.0 with alias apple, P2 targets net472 (apple) and net472 (banana).
Should the matching allow net10.0 to match net472 (apple) or should it fail due to ambiguity?

Since the `TargetFramework` value is used by the .NET SDK as the directory name for build output, should we block invalid characters on Windows file systems? (Linux doesn't prevent any characters, with the possible exception of `/`, since that's used as the directory separator).

Note that in the current iteration, something like `banana/3` is allowed as an alias and it seems to work.
Adding an error here would be a potential breaking change. It is very unlikely to affect people, but something that would probably need to be documented.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit

Suggested change
Adding an error here would be a potential breaking change. It is very unlikely to affect people, but something that would probably need to be documented.
Adding an error here would be a potential breaking change.
It is very unlikely to affect people, but something that would probably need to be documented.

### Lock file changes

NuGet's "repeatable build" feature adds a package lock file to the project directory.
The restore has a lock file has a similar schema to the assets file, so that schema would also need to be amended.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
The restore has a lock file has a similar schema to the assets file, so that schema would also need to be amended.
The restore lock file has a similar schema to the assets file, so that schema would also need to be amended.


NuGet's "repeatable build" feature adds a package lock file to the project directory.
The restore has a lock file has a similar schema to the assets file, so that schema would also need to be amended.
Fortunately when Central Package Management and its transitive pinning was introduced we introduced the concept of a PackagesLockFile version successfully.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit

Suggested change
Fortunately when Central Package Management and its transitive pinning was introduced we introduced the concept of a PackagesLockFile version successfully.
Fortunately, when Central Package Management and its transitive pinning was introduced, we introduced the concept of a PackagesLockFile version successfully.

Non-SDK style project only have a single target, so those project types won't see a change.

For simplicity of implementation, whenever SDKAnalysisLevel is set to 10.0.300 or more, NuGet will use the V4 assets file.
Whenever the SDKAnalysisLevel is set to a lower version, then NuGet will use the V3 assets file.
Copy link
Contributor

Choose a reason for hiding this comment

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

And this ages out after 3 major releases, based on how SDKAnalysisLevel works, right?
In other words, there's ample time (years?) for a customer to move a solution to version 4.


Take a project using `<TargetFramework>production</TargetFramework>`, `<TargetFrameworkMoniker>.NETCoreApp,Version=v8.0</TargetFrameworkMoniker>`, and `<RuntimeIdentifiers>linux-x64</RuntimeIdentifiers>`.

Note that the effective framework itself will not be part of the serialized part, but it'll cleaned up by the reader by looking up the target framework based off of the "project" section of the assets file.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Note that the effective framework itself will not be part of the serialized part, but it'll cleaned up by the reader by looking up the target framework based off of the "project" section of the assets file.
Note that the effective framework itself will not be part of the serialized part, but it'll be cleaned up by the reader by looking up the target framework based off of the "project" section of the assets file.


The following 2 changes will be made on the writer side:

- Add "framework" to the packageSpec, while keeping targetAlias for simplicity sake.
Copy link
Contributor

Choose a reason for hiding this comment

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

As a reader not as familiar with targetAlias, I'm not understanding how this is simple. Why would I use targetAlias and framework?

```diff
"frameworks": {
- "net8.0": {
- "targetAlias": "production",
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a scenario where targetAlias can be selecting 1 of multiple aliases? Let's say I have "production" and "staging" and "development", can I then set targetAlias to any of those three?


Given that there is no version for the package spec at this point, we can do a simple trick to infer the type of PackageSpec to read.

- When reading a framework, if both targetAlias and framework, then this is equivalent to V4 and a .NET SDK scenario
Copy link
Contributor

Choose a reason for hiding this comment

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

Could this be simplified from the textual scenarios into a small matrix of scenarios?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Status:Do not auto close Do not auto close for PRs needs long review process

Projects

None yet

Development

Successfully merging this pull request may close these issues.