Skip to content

Improve pruning documentation #3444

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

Merged
merged 8 commits into from
Jul 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 46 additions & 7 deletions docs/consume-packages/Package-References-in-Project-Files.md
Original file line number Diff line number Diff line change
Expand Up @@ -464,10 +464,12 @@ You can leave off `$(AssetTargetFallback)` if you wish to overwrite, instead of
## PrunePackageReference

The .NET Runtime is constantly evolving, with performance improvements and new APIs each release.
There is a lot of functionality that's available within the runtime, but also as packages, such as [System.Text.Json](https://www.nuget.org/packages/System.Text.Json). This can often lead to a `System.Text.Json 8.0.0` in a project targeting `.NET 9` or `.NET 8`. This dependency is unnecessary and the build conflict resolution would not use the assembly coming from the package since it's already available in the .NET Runtime.
Starting in in [NuGet version 6.13](..\release-notes\NuGet-6.13.md) and .NET SDK 9.0.200, `PrunePackageReference` enables the pruning of these packages at restore time for .NET SDK based projects.
New features added to .NET sometimes are also provided as packages, so that developers using older target frameworks can use the library, such as [System.Text.Json](https://www.nuget.org/packages/System.Text.Json).
This can often lead to a `System.Text.Json 8.0.0` in a project targeting `.NET 9` or `.NET 8`. This dependency is unnecessary and the build conflict resolution would not use the assembly coming from the package since it's already available in the .NET Runtime.
Starting in [NuGet version 6.13](..\release-notes\NuGet-6.13.md) and .NET SDK 9.0.200, `PrunePackageReference` enables the pruning of these packages at restore time for .NET SDK based projects.
The first iteration of pruning affected transitive packages only, but starting with .NET SDK 10, package pruning affects direct packages as well.

Package pruning is available as an opt-in feature with the .NET 9 SDK, and will be enabled by default for all `.NET` frameworks and `>= .NET Standard 2.0` starting with .NET 10 SDK.
Package pruning is available as an opt-in feature with the .NET 9 SDK, and is enabled by default for `>= .NET 8.0` frameworks and `>= .NET Standard 2.0` starting with .NET 10 SDK.

Package pruning is only available with the default dependency resolver as [released in 6.12](#nuget-dependency-resolver).

Expand All @@ -489,16 +491,53 @@ The .NET SDK predefines the list of packages to be pruned for you.

### How PrunePackageReference works

When a package is specified to be pruned during restore, it is removed from the dependency graph. This package is not downloaded and does not appear in any of the outputs of NuGet. When a package is pruned, there is a detailed verbosity message indicating that the package has been removed for the given target framework.
When a package is specified to be pruned during restore, it is removed from the dependency graph.
When a package is pruned, there is a message, visible at detailed verbosity, indicating that the package has been removed for the given target framework.
Copy link

@Frulfump Frulfump Jul 24, 2025

Choose a reason for hiding this comment

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

Is this for preview 7+? I don't see this with preview 6. (And I need to set -tl:off to see anything interesting with dotnet build otherwise I just see the really compact build output)

Interestingly if I target .NET 8 I see

Task "GetPackagesToPrune"
Loading packages to prune for .NETCoreApp 8.0 Microsoft.NETCore.App
Loaded prune package data from framework packages
Done executing task "GetPackagesToPrune".

But when I target .NET 10 I see

Task "GetPackagesToPrune"
Loading packages to prune for .NETCoreApp 10.0 Microsoft.NETCore.App
Loading prune package data from PrunePackageData folder
Failed to load prune package data from PrunePackageData folder, loading from targeting packs instead
Looking for targeting packs in C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref
Pack directories found: C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\10.0.0-preview.6.25358.103
Found package overrides file C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\10.0.0-preview.6.25358.103\data\PackageOverrides.txt
Done executing task "GetPackagesToPrune".

I don't see System.Memory.dll in either bin folder (when System.Memory is transitively referenced via Azure.Identity@1.14.2) (not when targeting .NET 7 either) maybe that's expected?

And direct deps if I add just System.Memory directly isn't "fully" pruned as we discussed below and doesn't show up either. And NU1510 is triggered which says it will not be pruned. So line 497 isn't really what I see either?


Pruning is only supported for transitive packages, meaning packages that are referenced by other packages or projects. The following table illustrates various package pruning behaviors.
For transitive packages, meaning dependencies of other packages or projects, the packages are not downloaded and do not appear in any of the outputs of NuGet.

For direct packages, `PrivateAssets='all'` and `IncludeAssets='none'` are implicitly applied.

- `IncludeAssets='none'` ensures that the assemblies from this package are not used during the build. Before pruning existed, the conflict resolution during the build ensured that platform assemblies were preferred over those coming from the packages.
- `PrivateAssets='all'` ensures that the packages aren't included in packages or through project references.

Example:

A project like below:

```xml
<PropertyGroup>
<TargetFrameworks>net9.0;netstandard2.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Text.Json" Version="9.0.4" />
</ItemGroup>
```

will have a nuspec with the following dependencies:

```xml
<dependencies>
<group targetFramework=".NETFramework4.7.2">
<dependency id="System.Text.Json" version="9.0.4" />
</group>
<group targetFramework="net9.0">
</group>
</dependencies>
```

When a direct PackageReference can be completely removed from your project, and one of the project frameworks are .NET 10 or newer, [NU1510](../reference/errors-and-warnings/NU1510.md) will be raised asking you to remove the package.
Following this suggestion will reduce the complexity of your project graph.

The following table summarizes all the package pruning behaviors.

| Dependency disposition | Behavior |
|-----------------|----------|
| Matches the ID of a transitive package coming through another package | Prune |
| Matches the ID of a transitive package coming through another project | Prune |
| Matches the ID of a direct `PackageReference` | Raise the [NU1510](../reference/errors-and-warnings/NU1510.md) warning and do not prune |
| Matches the ID of a `ProjectReference` | Raise the [NU1511](../reference/errors-and-warnings/NU1511.md) warning and do not prune |
| Matches the ID of a direct `PackageReference` | Apply `PrivateAssets='all'` and `IncludeAssets='none'` and raise the [NU1510](../reference/errors-and-warnings/NU1510.md) warning when the package can be removed from all frameworks and the project targets .NET 10. |
| Matches the ID of a `ProjectReference` | Do not prune and raise the [NU1511](../reference/errors-and-warnings/NU1511.md) warning when the project targets .NET 10 |

### PrunePackageReference applications

Expand Down
63 changes: 61 additions & 2 deletions docs/reference/errors-and-warnings/NU1510.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,74 @@ f1_keywords:

# NuGet Warning NU1510

## Scenario 1
## Scenario

> PackageReference System.Text.Json 9.0.0 will not be pruned. Consider removing this package from your dependencies, as it is likely unnecessary.
> PackageReference System.Text.Json will not be pruned. Consider removing this package from your dependencies, as it is likely unnecessary.

### Issue

The package `System.Text.Json` has been specified for pruning through the [PrunePackageReference](../../consume-packages/Package-References-in-Project-Files.md#prunepackagereference) feature.
The `.NET SDK` generally specifies the list of packages to be pruned. The package is not needed as a direct PackageReference since the .NET runtime itself carries either the same or higher version of the assembly.

This warning is *only* raised when the PackageReference in question *can be completely removed from the project*.

#### Example 1

```xml
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Text.Json" Version="10.0.0" />
</ItemGroup>
```

#### Example 2

```xml
<PropertyGroup>
<TargetFrameworks>net9.0;net10.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<PackageReference Condition="'$(TargetFramework)' == 'net10.0'" Include="System.Text.Json" Version="10.0.0" />
<PackageReference Condition="'$(TargetFramework)' == 'net9.0'" Include="System.Text.Json" Version="9.0.0" />
</ItemGroup>
```

Each conditional `PackageReference` for the `System.Text.Json` package can be removed because the package is already included in the respective .NET runtime versions for the frameworks it is declared for.

#### Example 3

```xml
<PropertyGroup>
<TargetFrameworks>net9.0;net10.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<PackageReference Condition="'$(TargetFramework)' == 'net9.0'" Include="System.Text.Json" Version="9.0.4" />
</ItemGroup>
```

The `System.Text.Json` package is within the pruning range of the only framework it's declared for.

### Solution

Remove the PackageReference as it's unnecessary.

> [!NOTE]
> In order to allow for easier adoption of the PrunePackageReference feature, this warning is raised by default when a project targets the .NET 10 framework or newer.

> [!NOTE]
> The warning will not be raised in scenarios in which at least one of the frameworks still needs the package, such as `net48` in the below example.
>
> ```xml
> <PropertyGroup>
> <TargetFrameworks>net10.0;net48</TargetFrameworks>
> </PropertyGroup>
>
> <ItemGroup>
> <PackageReference Include="System.Text.Json" Version="9.0.7" />
> </ItemGroup>
> ```
3 changes: 3 additions & 0 deletions docs/reference/errors-and-warnings/NU1511.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@ Pruning of projects is not supported.
- The `.NET SDK` generally specifies the list of packages to be pruned. It may mean that you have a project matching the id of a platform assembly.
In this scenario, change the name of the project.
- If the `PrunePackageReference` has been specified within your project, you may need to remove the `PrunePackageReference`

> [!NOTE]
> In order to allow for easier adoption of the PrunePackageReference feature, this warning is raised by default when a project targets the .NET 10 framework or newer.