Skip to content

Commit 8afbdbe

Browse files
authored
Document aliasing (#3543)
1 parent b5f0b70 commit 8afbdbe

8 files changed

Lines changed: 286 additions & 15 deletions

File tree

.github/copilot-instructions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Follow [Microsoft Writing Style Guide](https://learn.microsoft.com/en-us/style-g
3030

3131
- Sentence case headings (no gerunds in titles).
3232
- Be concise, break up long sentences.
33+
- Every sentence should be in its own line in the Markdown source for better readability and version control diffs.
3334
- Oxford comma in lists.
3435
- Use bullets for unordered lists.
3536
- Number all ordered list items as "1." (not sequential numbering like "1.", "2.", "3.", etc.)

docs/consume-packages/Package-References-in-Project-Files.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,28 @@ Note that because `build` is not included with `PrivateAssets`, targets and prop
142142
> [!NOTE]
143143
> When `developmentDependency` is set to `true` in a `.nuspec` file, this marks a package as a development-only dependency, which prevents the package from being included as a dependency in other packages. With PackageReference *(NuGet 4.8+)*, this flag also means that it will exclude compile-time assets from compilation. For more information, see [DevelopmentDependency support for PackageReference](https://github.com/NuGet/Home/wiki/DevelopmentDependency-support-for-PackageReference).
144144
145+
## Targeting multiple frameworks
146+
147+
SDK-style projects support multi-targeting by listing multiple values in the `TargetFrameworks` property. When a project targets multiple frameworks, NuGet restore produces a separate dependency graph for each framework, and `dotnet pack` creates a package with framework-specific assets for each target.
148+
149+
```xml
150+
<Project Sdk="Microsoft.NET.Sdk">
151+
<PropertyGroup>
152+
<TargetFrameworks>net10.0;netstandard2.0</TargetFrameworks>
153+
</PropertyGroup>
154+
</Project>
155+
```
156+
157+
For a step-by-step guide on setting up a multi-targeted project, see [Support multiple .NET frameworks in your project file](../create-packages/multiple-target-frameworks-project-file.md).
158+
159+
### How TargetFramework values work
160+
161+
The `TargetFramework` property in a project file is a friendly name — an alias — that gets translated into a canonical framework identity. The .NET SDK performs this translation by setting the `TargetFrameworkMoniker` (TFM), and when applicable, the `TargetPlatformMoniker` properties.
162+
163+
NuGet uses these moniker properties — not the `TargetFramework` string — for package compatibility checks. This means the `TargetFramework` value itself can be any string, as long as the moniker properties are set correctly.
164+
165+
For more details on the aliasing mechanism, see [TargetFramework values are aliases](../reference/target-frameworks.md#targetframework-values-are-aliases).
166+
145167
## Adding a PackageReference condition
146168

147169
You can use a condition to control whether a package is included. Conditions can use any MSBuild variable or a variable defined in the targets or props file. However, at present, only the `TargetFramework` variable is supported.
@@ -332,6 +354,9 @@ In order to persist the full closure of package dependencies, you can opt-in to
332354

333355
If this property is set, NuGet restore will generate a lock file (`packages.lock.json`) at the project root directory that lists all the package dependencies.
334356

357+
The `packages.lock.json` format is versioned.
358+
The format version depends on the features you use, such as Central Package Management's transitive pinning or duplicate effective target frameworks.
359+
335360
> [!Note]
336361
> Once a project has `packages.lock.json` file in its root directory, the lock file is always used with restore even if the property `RestorePackagesWithLockFile` is not set. So another way to opt-in to this feature is to create a dummy blank `packages.lock.json` file in the project's root directory.
337362
@@ -471,6 +496,62 @@ You can leave off `$(AssetTargetFallback)` if you wish to overwrite, instead of
471496
>
472497
> `$(PackageTargetFallback)` was an earlier feature that attempted to address this challenge, but it is fundamentally broken and *should* not be used. To migrate from `$(PackageTargetFallback)` to `$(AssetTargetFallback)`, simply change the property name.
473498
499+
### Multi-targeting with duplicate frameworks
500+
501+
*This feature requires [NuGet 7.6](../release-notes/NuGet-7.6.md) / .NET SDK 10.0.300 or later.*
502+
503+
Because `TargetFramework` values are aliases, multiple aliases can resolve to the *same* effective framework. Starting with [NuGet 7.6](../release-notes/NuGet-7.6.md) / .NET SDK 10.0.300, NuGet and the .NET SDK support this scenario.
504+
505+
This enables use cases such as:
506+
507+
- **Multi-RID builds**: Build platform-specific assemblies from a single project.
508+
509+
```xml
510+
<Project Sdk="Microsoft.NET.Sdk">
511+
<PropertyGroup>
512+
<TargetFrameworks>net10.0;linux;ios</TargetFrameworks>
513+
</PropertyGroup>
514+
515+
<PropertyGroup Condition="'$(TargetFramework)' == 'linux' OR '$(TargetFramework)' == 'ios' OR '$(TargetFramework)' == 'net10.0'">
516+
<TargetFrameworkIdentifier>.NETCoreApp</TargetFrameworkIdentifier>
517+
<TargetFrameworkVersion>v10.0</TargetFrameworkVersion>
518+
<TargetFrameworkMoniker>.NETCoreApp,Version=v10.0</TargetFrameworkMoniker>
519+
</PropertyGroup>
520+
</Project>
521+
```
522+
523+
- **Benchmarking different versions of a package**
524+
525+
```xml
526+
<Project Sdk="Microsoft.NET.Sdk">
527+
<PropertyGroup>
528+
<TargetFrameworks>benchmark7.0;benchmark8.0</TargetFrameworks>
529+
</PropertyGroup>
530+
531+
<!-- Frameworks omitted for brevity-->
532+
533+
<ItemGroup>
534+
<PackageReference Include="BenchmarkDotNet" Version="0.13.9" />
535+
<PackageReference Include="Contoso.FastLibrary" Version="7.0" Condition="'$(TargetFramework)' == 'benchmark7.0' "/>
536+
<PackageReference Include="Contoso.FastLibrary" Version="8.0" Condition="'$(TargetFramework)' == 'benchmark8.0' "/>
537+
</ItemGroup>
538+
</Project>
539+
```
540+
541+
#### Pack
542+
543+
A NuGet package can only contain one set of build output and one dependency group per effective framework. When you pack a project with duplicate effective frameworks, you must tell NuGet which alias contributes these assets or the pack raises [NU5051](../reference/errors-and-warnings/NU5051.md). See [NU5051](../reference/errors-and-warnings/NU5051.md) for resolution steps and examples.
544+
545+
#### Project references
546+
547+
When a project references another project that has multiple aliases resolving to the same framework, NuGet uses the alias name as a tiebreaker. If the referencing project has an alias with the same name as one in the referenced project, that alias is preferred. If there’s no matching name and multiple candidates exist, NuGet reports an error.
548+
549+
#### Limitations
550+
551+
- Only SDK-style projects support duplicate effective frameworks.
552+
- Aliases that contain path separator characters (`/` or `\`) are blocked.
553+
- Visual Studio’s Package Manager UI doesn’t have special support for duplicate frameworks, but you can manage packages by editing the project file directly or using the `dotnet` CLI.
554+
474555
## PrunePackageReference
475556

476557
The .NET Runtime is constantly evolving, with performance improvements and new APIs each release.

docs/create-packages/multiple-target-frameworks-project-file.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
11
---
22
title: Multi-targeting for NuGet Packages in your project file
3-
description: Description of the various methods to target multiple .NET Framework versions from within a single NuGet package in your project file.
3+
description: Description of the various methods to target multiple .NET frameworks from within a single NuGet package in your project file.
44
author: JonDouglas
55
ms.author: jodou
66
ms.date: 07/15/2019
77
ms.topic: how-to
88
---
99

10-
# Support multiple .NET Framework versions in your project file
10+
# Support multiple .NET frameworks in your project file
1111

1212
When you first create a project, we recommend you create a .NET Standard class library, as it provides compatibility with the widest range of consuming projects. By using .NET Standard, you add [cross-platform support](/dotnet/standard/library-guidance/cross-platform-targeting) to a .NET library by default. However, in some scenarios, you may also need to include code that targets a particular framework. This article shows you how to do that for [SDK-style](../resources/check-project-format.md) projects.
1313

1414
For SDK-style projects, you can configure support for multiple targets frameworks ([TFM](/dotnet/standard/frameworks)) in your project file, then use `dotnet pack` or `msbuild /t:pack` to create the package.
1515

16-
> [!NOTE]
17-
> nuget.exe CLI does not support packing SDK-style projects, so you should only use `dotnet pack` or `msbuild /t:pack`. We recommend that you [include all the properties](../reference/msbuild-targets.md#pack-target) that are usually in the `.nuspec` file in the project file instead. To target multiple .NET Framework versions in a non-SDK-style project, see [Supporting multiple .NET Framework versions](supporting-multiple-target-frameworks.md).
18-
19-
## Create a project that supports multiple .NET Framework versions
16+
## Create a project that supports multiple .NET frameworks
2017

2118
1. Create a new .NET Standard class library either in Visual Studio or use `dotnet new classlib`.
2219

@@ -69,7 +66,14 @@ Here is the *.csproj* file that is generated using the preceding steps and .NET
6966
</Project>
7067
```
7168

69+
## Multi-targeting with duplicate frameworks
70+
71+
Starting with [NuGet 7.6](../release-notes/NuGet-7.6.md) / .NET SDK 10.0.300, you can use multiple `TargetFrameworks` values that resolve to the same underlying framework. This enables scenarios like building for multiple runtimes or targeting multiple versions of a host application from a single project.
72+
73+
For details on how this works with restore and pack, see [Targeting multiple frameworks](../consume-packages/package-references-in-project-files.md#multi-targeting-with-duplicate-frameworks).
74+
7275
## See also
7376

7477
* [How to specify target frameworks](/dotnet/standard/frameworks#how-to-specify-target-frameworks)
7578
* [Cross-platform targeting](/dotnet/standard/library-guidance/cross-platform-targeting)
79+
* [Target frameworks reference](../reference/target-frameworks.md)

docs/reference/Errors-and-Warnings.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ NuGet supports the following configuration properties.
3535
| NuGet source errors | [NU1301](./errors-and-warnings/NU1301.md), [NU1302](./errors-and-warnings/NU1302.md) |
3636
| NuGet internal errors | [NU1000](./errors-and-warnings/NU1000.md) |
3737
| Signed packages errors (creation and verification) | [NU3001](./errors-and-warnings/NU3001.md), [NU3004](./errors-and-warnings/NU3004.md), [NU3005](./errors-and-warnings/NU3005.md), [NU3008](./errors-and-warnings/NU3008.md), [NU3034](./errors-and-warnings/NU3034.md)|
38-
| Pack Errors | [NU5000](./errors-and-warnings/NU5000.md), [NU5001](./errors-and-warnings/NU5001.md), [NU5002](./errors-and-warnings/NU5002.md), [NU5003](./errors-and-warnings/NU5003.md), [NU5004](./errors-and-warnings/NU5004.md), [NU5005](./errors-and-warnings/NU5005.md), [NU5007](./errors-and-warnings/NU5007.md), [NU5008](./errors-and-warnings/NU5008.md), [NU5009](./errors-and-warnings/NU5009.md), [NU5010](./errors-and-warnings/NU5010.md), [NU5011](./errors-and-warnings/NU5011.md), [NU5012](./errors-and-warnings/NU5012.md), [NU5013](./errors-and-warnings/NU5013.md), [NU5014](./errors-and-warnings/NU5014.md), [NU5015](./errors-and-warnings/NU5015.md), [NU5016](./errors-and-warnings/NU5016.md), [NU5017](./errors-and-warnings/NU5017.md), [NU5018](./errors-and-warnings/NU5018.md), [NU5019](./errors-and-warnings/NU5019.md), [NU5020](./errors-and-warnings/NU5020.md), [NU5021](./errors-and-warnings/NU5021.md), [NU5022](./errors-and-warnings/NU5022.md), [NU5023](./errors-and-warnings/NU5023.md), [NU5024](./errors-and-warnings/NU5024.md), [NU5025](./errors-and-warnings/NU5025.md), [NU5026](./errors-and-warnings/NU5026.md), [NU5027](./errors-and-warnings/NU5027.md), [NU5028](./errors-and-warnings/NU5028.md), [NU5029](./errors-and-warnings/NU5029.md), [NU5036](./errors-and-warnings/NU5036.md), [NU5042](./errors-and-warnings/NU5042.md), [NU5049](./errors-and-warnings/NU5049.md) |
38+
| Pack Errors | [NU5000](./errors-and-warnings/NU5000.md), [NU5001](./errors-and-warnings/NU5001.md), [NU5002](./errors-and-warnings/NU5002.md), [NU5003](./errors-and-warnings/NU5003.md), [NU5004](./errors-and-warnings/NU5004.md), [NU5005](./errors-and-warnings/NU5005.md), [NU5007](./errors-and-warnings/NU5007.md), [NU5008](./errors-and-warnings/NU5008.md), [NU5009](./errors-and-warnings/NU5009.md), [NU5010](./errors-and-warnings/NU5010.md), [NU5011](./errors-and-warnings/NU5011.md), [NU5012](./errors-and-warnings/NU5012.md), [NU5013](./errors-and-warnings/NU5013.md), [NU5014](./errors-and-warnings/NU5014.md), [NU5015](./errors-and-warnings/NU5015.md), [NU5016](./errors-and-warnings/NU5016.md), [NU5017](./errors-and-warnings/NU5017.md), [NU5018](./errors-and-warnings/NU5018.md), [NU5019](./errors-and-warnings/NU5019.md), [NU5020](./errors-and-warnings/NU5020.md), [NU5021](./errors-and-warnings/NU5021.md), [NU5022](./errors-and-warnings/NU5022.md), [NU5023](./errors-and-warnings/NU5023.md), [NU5024](./errors-and-warnings/NU5024.md), [NU5025](./errors-and-warnings/NU5025.md), [NU5026](./errors-and-warnings/NU5026.md), [NU5027](./errors-and-warnings/NU5027.md), [NU5028](./errors-and-warnings/NU5028.md), [NU5029](./errors-and-warnings/NU5029.md), [NU5036](./errors-and-warnings/NU5036.md), [NU5042](./errors-and-warnings/NU5042.md), [NU5049](./errors-and-warnings/NU5049.md), [NU5051](./errors-and-warnings/NU5051.md) |
3939
| License specific Pack Errors | [NU5030](./errors-and-warnings/NU5030.md), [NU5031](./errors-and-warnings/NU5031.md), [NU5032](./errors-and-warnings/NU5032.md), [NU5033](./errors-and-warnings/NU5033.md), [NU5034](./errors-and-warnings/NU5034.md), [NU5035](./errors-and-warnings/NU5035.md) |
4040
| NuGetAudit specific warnings | [NU1014](./errors-and-warnings/NU1014.md), [NU1900](./errors-and-warnings/NU1900.md), [NU1901, NU1902, NU1903, NU1904](./errors-and-warnings/NU1901-NU1904.md), [NU1905](./errors-and-warnings/NU1905.md) |
4141

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
---
2+
title: NuGet Error NU5051
3+
description: NU5051 error code
4+
author: nkolev92
5+
ms.author: nikolev
6+
ms.date: 03/25/2026
7+
ms.topic: reference
8+
f1_keywords:
9+
- "NU5051"
10+
---
11+
12+
# NuGet Error NU5051
13+
14+
## Scenario 1
15+
16+
A project has multiple target framework aliases that resolve to the same effective framework, and pack can't determine which alias should contribute build output, dependencies, or framework references to the package.
17+
18+
### Issue
19+
20+
A project like the following has two aliases (`apple` and `banana`) that both resolve to `net10.0`:
21+
22+
```xml
23+
<Project Sdk="Microsoft.NET.Sdk">
24+
<PropertyGroup>
25+
<TargetFrameworks>apple;banana</TargetFrameworks>
26+
</PropertyGroup>
27+
28+
<PropertyGroup>
29+
<TargetFrameworkIdentifier>.NETCoreApp</TargetFrameworkIdentifier>
30+
<TargetFrameworkVersion>v10.0</TargetFrameworkVersion>
31+
<TargetFrameworkMoniker>.NETCoreApp,Version=v10.0</TargetFrameworkMoniker>
32+
</PropertyGroup>
33+
</Project>
34+
```
35+
36+
When you run `dotnet pack`, NuGet raises NU5051 because it can't include duplicate build output or dependency groups for the same framework in a single package.
37+
38+
### Solution
39+
40+
Set `IncludeBuildOutput` to `false` and `SuppressDependenciesWhenPacking` to `true` on all but one alias per effective framework. This tells NuGet which alias contributes the build output and dependencies.
41+
42+
```xml
43+
<!-- Let 'apple' contribute the build output and dependencies -->
44+
<PropertyGroup Condition="'$(TargetFramework)' == 'banana'">
45+
<IncludeBuildOutput>false</IncludeBuildOutput>
46+
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
47+
</PropertyGroup>
48+
```
49+
50+
If the aliases have different `FrameworkReference` items, use `PrivateAssets="all"` on the framework references in the secondary aliases to suppress them from the package.
51+
52+
## Scenario 2
53+
54+
A project has aliases for runtime-specific builds and wants to place each alias's build output into a custom package path (for example, `runtimes/<rid>/lib/<tfm>/`).
55+
56+
### Issue
57+
58+
A project like the following has `net10.0` as the primary alias and `linux` and `ios` as secondary aliases. All three resolve to the same effective framework:
59+
60+
```xml
61+
<Project Sdk="Microsoft.NET.Sdk">
62+
<PropertyGroup>
63+
<TargetFrameworks>net10.0;linux;ios</TargetFrameworks>
64+
</PropertyGroup>
65+
66+
<PropertyGroup Condition="'$(TargetFramework)' == 'linux' OR '$(TargetFramework)' == 'ios' OR '$(TargetFramework)' == 'net10.0'">
67+
<TargetFrameworkIdentifier>.NETCoreApp</TargetFrameworkIdentifier>
68+
<TargetFrameworkVersion>v10.0</TargetFrameworkVersion>
69+
<TargetFrameworkMoniker>.NETCoreApp,Version=v10.0</TargetFrameworkMoniker>
70+
</PropertyGroup>
71+
</Project>
72+
```
73+
74+
Running `dotnet pack` raises NU5051 because three aliases produce build output and dependencies for the same framework.
75+
76+
### Solution
77+
78+
Suppress the default build output and dependencies for the secondary aliases, and use `TargetsForTfmSpecificContentInPackage` to place the secondary aliases' assemblies into custom package paths:
79+
80+
```xml
81+
<PropertyGroup>
82+
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);GetMyPackageFiles</TargetsForTfmSpecificContentInPackage>
83+
</PropertyGroup>
84+
85+
<PropertyGroup Condition="'$(TargetFramework)' == 'linux' OR '$(TargetFramework)' == 'ios'">
86+
<IncludeBuildOutput>false</IncludeBuildOutput>
87+
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
88+
</PropertyGroup>
89+
90+
<Target Name="GetMyPackageFiles">
91+
<ItemGroup Condition="'$(TargetFramework)' == 'linux' OR '$(TargetFramework)' == 'ios'">
92+
<TfmSpecificPackageFile Include="$(OutputPath)$(AssemblyName).dll">
93+
<PackagePath>runtimes/$(TargetFramework)/lib/net10.0</PackagePath>
94+
</TfmSpecificPackageFile>
95+
</ItemGroup>
96+
</Target>
97+
```
98+
99+
With this configuration, `net10.0` contributes the default `lib/net10.0/` build output and dependencies, while `linux` and `ios` place their assemblies under `runtimes/linux/lib/net10.0/` and `runtimes/ios/lib/net10.0/` respectively.
100+
101+
For more information about multi-targeting with duplicate frameworks, see [Targeting multiple frameworks](../../consume-packages/package-references-in-project-files.md#multi-targeting-with-duplicate-frameworks). For more information about pack extensibility, see [pack target](../msbuild-targets.md#pack-target).

0 commit comments

Comments
 (0)