You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- GitHub Issue ([5154](https://github.com/NuGet/Home/issues/5154)) - Treat Target Framework as Aliases
6
5
7
6
## Summary
8
7
9
8
.NET SDK projects are capable of multi targeting based on a target framework where you can have different dependencies for each framework.
10
-
In some cases, it is convenient to multi target based on different pivots such as target appplication version or maybe the specific runtime, as packages can contain runtime assets.
9
+
In some cases, it is convenient to multi target based on different pivots such as target application version or maybe the specific runtime, as packages can contain runtime assets.
11
10
This proposals covers the steps that need to be taken to enable that capability.
12
11
13
12
## Motivation
@@ -66,34 +65,42 @@ MSBuild version 17.3.0+92e077650 for .NET
66
65
67
66
The missing part is allowing the same framework to be targeting among these aliases.
68
67
69
-
The following scenarios would be enabled:
68
+
The following hypothetical scenarios would be enabled.
69
+
Note that these examples are not being proposed as syntax that will be implemented.
70
+
Rather, they're intended to demonstrate a scenario that is not possible today.
71
+
72
+
Firstly, a single project that creates multiple packages for different platforms, but the same TFM:
Alternatively, the VS extension scenario would be something like:
93
+
Secondly, a Visual Studio Extension project targeting multiple versions of VS could look similar to the following.
94
+
In this example, the Visual Studio Extensibility SDK would be responsible for setting `TargetFrameworkIdentifier`, `TargetFrameworkVersion` and `TargetFrameworkMoniker`.
@@ -120,87 +127,124 @@ While this scenario does have a number of interesting uses, there are many comma
120
127
| dotnet list package | dotnet list package | L | The work here would likely follow the work from the the restore side |
121
128
| Visual Studio challenges | Multi targeting, displaying (transitive) dependencies | L(+) ||
122
129
123
-
124
-
125
-
### Build challenges
130
+
### Assets file changes
126
131
127
132
In PackageReference, NuGet has a contract with the .NET SDK, where NuGet writes the assets file and the .NET SDK consumes it.
133
+
Therefore, the assets file schema that NuGet writes must match what's expected by the .NET SDK.
128
134
In most scenarios, people use `dotnet restore` and `dotnet build --no-restore` and there are no issues.
129
-
It is not uncommon that people use `nuget.exe restore` due to the fact that they have non-SDK projects in their solution.
135
+
It is not uncommon that people use `nuget.exe restore` due to the fact that they have non-SDK projects in their solution and they have not modernized their CI build to use MSBuild restore.
136
+
Given that NuGet.exe and .NET SDK do not ship together, it is very common that the assets file was generated by a different version of NuGet.exe than what the .NET SDK can contains.
137
+
Normally, this works.
138
+
However, when NuGet makes a breaking change to the assets file, a significant number of customers will experience issues.
139
+
We should design the feature to avoid incorrect results, and also make error messages as easy as possible to understand and resolve.
130
140
131
-
Given that NuGet.exe and .NET SDK do not ship together, it is super common that the assets file was generated by a newer/older version of NuGet.exe than the .NET SDK can support.
141
+
#### Assets file version
132
142
133
-
Normally, this works. NuGet rarely makes non-additive changes to the assets file.
143
+
A breaking change in the assets file will be very difficult to implement unless we allow a period where NuGet supports multiple versions of the assets file in the same release.
144
+
Additionally, the [.NET SDK assets file reader](https://github.com/dotnet/sdk/blob/main/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs) and [non-SDK style projects assets file reader](https://github.com/dotnet/NuGet.BuildTasks) have different implementations, across different repos.
145
+
Therefore, unless Nuget supports multiple multiple asset file versions in a single binary, it would be necessary to insert the change simultaneously across NuGet, the .NET SDK, NuGet.BuildTools, VS, and possibly MSBuild.
146
+
This is so infeasible it's effectively impossible.
134
147
135
-
Sample part of an assets file:
148
+
Therefore, the proposal is that the .NET SDK will define an MSBuild property `ProjectAssetsFileVersion`, with allowed values `3` or `4`.
149
+
When the property is not defined, it will assume `3`, in order to maintain backwards compatibility with the existing build tools.
150
+
The .NET SDK already defines property `PropertyAssetsFile`, which is the location it reads the assets file from, hence the proposal of `ProjectAssetsFileVersion`.
151
+
It may be that the non-SDK style project assets file reader (NuGet.BuildTools) may never migrate to v4 assets file, so supporting both assets file schemas could be a permanent cost, rather than a temporary situation.
136
152
137
-
```json
138
-
"targets": {
139
-
".NETFramework,Version=v4.7.2": {},
140
-
".NETFramework,Version=v4.8.0": {}
141
-
},
153
+
Reading the assets file will need a new data structure, and therefore will need new APIs to read.
154
+
The goal is to enable the new data structure to be compatible with both v3 and v4 assets files, so that consumers of the assets file don't need multiple code paths to handle both assets file versions.
155
+
This is not only relevant for the .NET SDK, but NuGet's own Visual Studio integration.
142
156
143
-
```
157
+
The assets file already contains a `version` property, always written out as the first property on the root object.
158
+
This enables our assets file reader to use polymorphic deserialization to handle.
159
+
Although if this turns out to be difficult to implement, we can provide separate V3 and V4 read methods (but both using the new data structures), and callers will need to check the `ProjectAssetsFileVersion` MSBuild property to determine which one to use.
160
+
However, I'm reasonably confident that polymorphic deserialization should be feasible to implement.
144
161
145
-
NuGet's assets file is written pivoting on the actual target framework.
162
+
#### Inner build pivot
146
163
147
-
To solve this, NuGet would need to:
164
+
There are multiple places in the assets fie where the "NuGet Framework" (before .NET 5 it was just the `TargetFrameworkMoniker` MSBuild property, but since .NET 5 it can include the TargetPlatformMoniker as well, although in a simplified format) is used as a JSON property key.
165
+
Sometimes with an additional Runtime Identifier (RID).
166
+
Since the goal of this spec is to allow multiple `TargetFramework`s to resolve to the same "NuGet Framework", this means these parts of the assets file have to change.
148
167
149
-
- Add vNext for the assets file format, version 4. Version 4 would either a dictionary based on the `TargetFramework` value instead of the effective target framework, built by coming `TargetFrameworkMoniker` and `TargetPlatformMoniker`.
150
-
- This behavior would probably have to be opt-in to avoid breaking all combinations of new `NuGet.exe`, old `.NET SDK` combinations, but we can choose to intentionally not do this to encourage maintaining the 2 versions equivalently.
151
-
- Make a decision what would be a satisfactory behavior for combining newer/older versions of `NuGet.exe` and `.NET SDK`. This is largely just confirming some of the recent decisions.
168
+
Here is a short extract from an assets file:
152
169
153
-
To solve this, the .NET SDK would need:
170
+
```json
171
+
"targets": {
172
+
".NETFramework,Version=v4.8": {},
173
+
".NETFramework,Version=v4.8/win7-x64": {},
174
+
"net8.0": {},
175
+
"net8.0/win7-x64": {},
176
+
"net8.0-windows7.0": {},
177
+
"net8.0-windows7.0/win7-x64": {},
178
+
},
179
+
```
180
+
181
+
Currently the `targets` section of the assets file is loaded into a `Dictionary<string, NotRelevant>`, and therefore the `targets` node cannot contain two identical TFMs when two Target Frameworks resolve to the same TFM. The proposal is to change this, and all other examples in the assets file, to use the `TargetFramework` as a string literal instead.
154
182
155
-
- Be able to `build` with both versions 3 and version 4 of the assets file. The NuGet dependencies would naturally flow into the .NET SDK and the work is to just differentiate between the 2 different versions.
156
-
- The amount of work needed to be done here would likely be affected by the exact shape of the NuGet APIs. These changes may be seamless.
183
+
Todo: figure out best choice for proposed data model & schema.
184
+
Will there be a problem if customer uses `TargetFramework` with a `/` character?
157
185
158
-
### Restore challenges
186
+
### Lock file changes
159
187
160
-
In addition to the assets file, restore has a lock file which has a similar schema, that schema would need to be amended. Fortunately when Central Package Management and its transitive pinning was introduced we introduced the concept of a PackagesLockFile version succesfully. We'd just add a version 3.
188
+
NuGet's "repeatable build" feature adds a package lock file to the project directory.
189
+
The restore has a lock file has a similar schema to the assets file, so that schema would also need to be amended.
190
+
Fortunately when Central Package Management and its transitive pinning was introduced we introduced the concept of a PackagesLockFile version successfully.
191
+
We'd just add a version 3.
161
192
162
193
NuGet would:
163
194
164
195
- Add version 3 of the packages lock file.
165
196
166
-
NuGet's intra restore caching is based on the existing of a single framework. It is difficult to predict the amount of work that'd be required to implement this correctly, but it'll certainly involve public API changes to NuGet libraries. (NuGet ships libraries, NuGet.exe, VS and dotnet.exe)
197
+
NuGet's intra restore caching is based on the existing of a single framework.
198
+
It is difficult to predict the amount of work that'd be required to implement this correctly, but it'll certainly involve public API changes to NuGet libraries.
199
+
(NuGet ships libraries, NuGet.exe, VS and dotnet.exe)
167
200
168
-
### Pack challenges
201
+
### Pack changes
169
202
170
-
Multi-targetted pack only really works when each build leg (TargetFramework value) has 1 target location such as `lib/net5.0`.
171
-
Packing projects that do target the same framework multiple may be helpful in some scenarios, so completely blocking it is likely not a great idea, but having a coherent experience would require better understanding of the scenarios at question.
172
-
173
-
At the minimum, the pack command should be able to detect the scenario at hand and *not succeed queitly* as there's no guarantee what gets packed.
174
-
This scenario would require separate design work.
203
+
For the first version of this feature, projects that have more than one `TargetFramework` (inner-build) resolve to the same "NuGet Framework" will result in a pack error, therefore preventing these projects from being packed.
204
+
See [Extending Pack, under Future Possibilities](#extending-pack) for more information.
175
205
176
206
### dotnet.exe challenges
177
207
178
-
Today, some values in dotnet.exe work with an aliased `TargetFramework`, such as `build` and `publish`.
208
+
Today, some values in dotnet.exe work with an aliased `TargetFramework`, such as `build` and `publish`, using the `--framework` or `-f` argument.
179
209
The expectation is that all the commands support this, but this would require a deeper investigation.
180
210
181
211
#### dotnet list package
182
212
183
-
`dotnet list package` has an output that pivots on the effective target framework. This would need to be changed to use the aliased value.
213
+
`dotnet list package` has an output that pivots on the effective target framework.
214
+
This would need to be changed to use the aliased value.
184
215
185
216
NuGet would need to:
186
217
187
218
- Display the output with an aliased pivot.
188
-
- Update the machine readable output to handle the new schema, <https://github.com/NuGet/Home/issues/11449>. Given that the machine readable output has not shipped yet, this may be something that can be done ahead of time.
219
+
- Update the machine readable output to handle the new schema, <https://github.com/NuGet/Home/issues/11449>.
189
220
190
221
#### Visual Studio challenges
191
222
192
-
Currently the PM UI does not have any support for multi targeting. This isn't anything new, but it can affect the experience of customers moving to SDK projects for the first time.
223
+
Currently the PM UI does not have any support for multi targeting.
224
+
This isn't anything new, but it can affect the experience of customers moving to SDK projects for the first time.
225
+
Especially for customers who are not comfortable hand editing XML or MSBuild files.
193
226
194
227
Another concern are all the APIs for displaying the dependencies, transitive dependencies and Get Installed Packages APIs in NuGet.VisualStudio.Contracts.
195
228
All of these APIs may require an incremental addition.
196
229
230
+
#### Project to Project references
231
+
232
+
MSBuild has a [`ProjectReference` protocol](https://github.com/dotnet/msbuild/blob/25fdeb3c8c2608f248faec3d6d37733ae144bbbb/documentation/ProjectReference-Protocol.md), which explains how `ProjectReferences` work.
233
+
This will need changes to support scenarios where a project where more than one `TargetFramework` resolves to the same "NuGet Framework".
234
+
235
+
Additionally, NuGet's `PackageReference` requires a project's entire dependency graph to be resolved, which includes projects, in order to calculate transitive packages.
236
+
Therefore, NuGet has the same, or at least very similar, problems as MSBuild with regards to `ProjectReference`s.
237
+
238
+
This needs further discussion between the MSBuild and NuGet teams.
239
+
197
240
## Drawbacks
198
241
199
242
<!-- Why should we not do this? -->
200
243
201
244
## Rationale and alternatives
202
245
203
-
- N/A. This is a binary decision to either support or not support this scenario.
246
+
From a high level feature point of view, the alternative is that customers must use separate projects, rather than a single project file.
247
+
Therefore, this feature is a binary decision to either support or not support this scenario.
204
248
205
249
## Prior Art
206
250
@@ -215,6 +259,31 @@ All of these APIs may require an incremental addition.
215
259
<!-- What parts of the proposal need to be resolved before the proposal is stabilized? -->
216
260
<!-- What related issues would you consider out of scope for this proposal but can be addressed in the future? -->
217
261
262
+
### Exact assets file schema changes
263
+
264
+
Should we "simply" replace the TFM in property keys with the Target Framework alias?
265
+
For example, change ".NETFramework,Version=4.7.2/win-x64" with "vs17.7/win-x64"?
266
+
But there will be parsing problems when a `TargetFramework` contains a `/` character.
267
+
While "something/another thing/win-x64" can determine that "win-x64" is a RID, when we get the RID-less "something/another thing" string, it's not clear that what follows the `/` is a RID, unless the assets file parser starts knowing the entire RID graph.
268
+
269
+
### How ProjectReferences will work
270
+
271
+
Currently ProjectReferences (P2P) do a compatibility check, both by MSBuild and NuGet (although I'm sure MSBuild delegates to NuGet APIs).
272
+
When a project has multiple `TargetFramework`s that map to a single "NuGet Framework", which one should MSBuild and NuGet use?
273
+
MSBuild needs to choose in order to know which dll to pass to the compiler (in case different aliases have different APIs), and so test projects actualy test the desired product binary.
274
+
NuGet needs to know for the package dependency graph.
275
+
276
+
A simple option is to match the alias string.
277
+
But do we want to support different projects using different aliases?
278
+
If so, then a way to "set the desired alias in the target project" will be needed.
279
+
That may be usable as a way to [override selection of "nearest" framework](https://github.com/NuGet/Home/issues/7416), which may or may not be considered a good thing, depending on your view if overriding asset selection is good or not.
280
+
218
281
## Future Possibilities
219
282
220
-
<!-- What future possibilities can you think of that this proposal would help with? -->
283
+
<!-- What future possibilities can you think of that this proposal would help with? -->
284
+
285
+
### Extending Pack
286
+
287
+
It seems feasible that if a single project can target multiple versions of Visual Studio, through a new Visual Studio Extensibility SDK, that someone will also want to create a package that supports multiple versions of Visual Studio.
288
+
289
+
Designing how this could work is out of scope for this spec, and can be introduced in a different spec.
0 commit comments