Skip to content

Add DependencyOnlyTargetFrameworks to eliminate System.ValueTuple from net471 NuGet graph#126773

Draft
Copilot wants to merge 5 commits intomainfrom
copilot/add-net471-targets-to-packages
Draft

Add DependencyOnlyTargetFrameworks to eliminate System.ValueTuple from net471 NuGet graph#126773
Copilot wants to merge 5 commits intomainfrom
copilot/add-net471-targets-to-packages

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 10, 2026

net471 projects referencing these packages get System.ValueTuple pulled into the NuGet graph via the net462 dependency group, even though net471 has System.ValueTuple inbox. This adds a net471 dependency group (with no System.ValueTuple) to the 12 packages that directly reference it, without shipping any additional assemblies.

Description

Approach

Three options were evaluated. Size measurements are based on the 10.0.0 packages from the NuGet global cache.

Option 1 — Add net471 to all ~43 packages (transitive + direct) ❌ Discarded

Each of the 43 packages that transitively depends on System.ValueTuple would get a lib/net471 assembly added. The new net471 assemblies would be byte-for-byte identical to the existing net462 assemblies — pure overhead.

Package lib/net462 size Note
Microsoft.Bcl.Cryptography 307.8 KB transitive
Microsoft.Bcl.Memory 76.3 KB direct
Microsoft.Bcl.TimeProvider 33.3 KB direct
Microsoft.Extensions.Caching.Memory 51.8 KB direct
Microsoft.Extensions.Configuration 47.3 KB transitive
Microsoft.Extensions.Configuration.Abstractions 31.8 KB direct
Microsoft.Extensions.Configuration.Binder 48.8 KB transitive
Microsoft.Extensions.Configuration.CommandLine 28.8 KB transitive
Microsoft.Extensions.Configuration.EnvironmentVariables 26.8 KB transitive
Microsoft.Extensions.Configuration.FileExtensions 31.3 KB transitive
Microsoft.Extensions.Configuration.Ini 28.8 KB transitive
Microsoft.Extensions.Configuration.Json 31.3 KB transitive
Microsoft.Extensions.Configuration.UserSecrets 29.8 KB transitive
Microsoft.Extensions.Configuration.Xml 40.8 KB transitive
Microsoft.Extensions.DependencyModel 87.8 KB transitive
Microsoft.Extensions.Diagnostics 38.8 KB transitive
Microsoft.Extensions.Diagnostics.Abstractions 35.3 KB transitive
Microsoft.Extensions.Hosting 77.8 KB transitive
Microsoft.Extensions.Hosting.Abstractions 58.8 KB transitive
Microsoft.Extensions.Hosting.WindowsServices 33.8 KB transitive
Microsoft.Extensions.Http 83.8 KB transitive
Microsoft.Extensions.Logging 53.8 KB direct
Microsoft.Extensions.Logging.Configuration 33.8 KB transitive
Microsoft.Extensions.Logging.Console 85.8 KB transitive
Microsoft.Extensions.Logging.Debug 24.3 KB transitive
Microsoft.Extensions.Logging.EventLog 30.3 KB transitive
Microsoft.Extensions.Logging.EventSource 41.3 KB transitive
Microsoft.Extensions.Logging.TraceSource 26.3 KB transitive
Microsoft.Extensions.Options 68.8 KB direct
Microsoft.Extensions.Options.ConfigurationExtensions 29.3 KB transitive
Microsoft.Extensions.Options.DataAnnotations 27.8 KB transitive
System.Formats.Asn1 107.3 KB direct
System.Formats.Cbor 98.8 KB direct
System.Formats.Nrbf 69.8 KB direct
System.Linq.AsyncEnumerable ~300 KB direct (new package, not yet published standalone)
System.Memory.Data 37.8 KB transitive
System.Net.Http.Json 73.3 KB transitive
System.Resources.Extensions 113.3 KB transitive
System.Security.Cryptography.Cose 91.8 KB transitive
System.Security.Cryptography.Pkcs 23.3 KB transitive
System.Security.Cryptography.Xml 57.8 KB transitive
System.Text.Encoding.CodePages 751.8 KB direct
System.Text.Json 759.3 KB direct

Total additional assembly size: ~4.1 MB (3,835 KB measured + ~300 KB estimated for System.Linq.AsyncEnumerable). 43 packages grow.

Option 2 — Add net471 only to the 12 packages with direct System.ValueTuple references ❌ Discarded

NuGet resolves each package's dependency group independently, so fixing only the 12 packages that directly reference System.ValueTuple is sufficient for net471 consumers. However, each of the 12 packages would still need a lib/net471 assembly — again byte-for-byte identical to the lib/net462 assembly.

Package lib/net462 size (= added lib/net471 size)
Microsoft.Bcl.Memory 76.3 KB
Microsoft.Bcl.TimeProvider 33.3 KB
Microsoft.Extensions.Caching.Memory 51.8 KB
Microsoft.Extensions.Configuration.Abstractions 31.8 KB
Microsoft.Extensions.Logging 53.8 KB
Microsoft.Extensions.Options 68.8 KB
System.Formats.Asn1 107.3 KB
System.Formats.Cbor 98.8 KB
System.Formats.Nrbf 69.8 KB
System.Linq.AsyncEnumerable ~300 KB (new package, not yet published standalone)
System.Text.Encoding.CodePages 751.8 KB
System.Text.Json 759.3 KB

Total additional assembly size: ~2.4 MB (2,102 KB measured + ~300 KB estimated for System.Linq.AsyncEnumerable). 12 packages grow.

Option 3 (chosen) — DependencyOnlyTargetFrameworks

Same NuGet graph fix as Option 2 (a net471 dependency group is emitted in the nuspec for each of the 12 packages), but lib/net471 is suppressed entirely. Consumers fall back to lib/net462 for the assembly. Total additional assembly size: 0 bytes. No packages grow.


Changes

  • eng/packaging.targets — Introduces DependencyOnlyTargetFrameworks: a general-purpose semicolon-separated property for TFMs that should emit a NuGet dependency group but ship no lib/ assembly. When set:

    • Appended to TargetFrameworks so the SDK runs inner builds and generates dependency groups
    • IncludeBuildOutput=false for those inner builds (no lib/ entry in the package)
    • NU5128 suppressed (intentional dep-group/lib mismatch)
  • 12 src .csproj files — Replace $(NetFrameworkWithValueTuple) in TargetFrameworks with a dedicated DependencyOnlyTargetFrameworks property:

    <DependencyOnlyTargetFrameworks>$(NetFrameworkWithValueTuple)</DependencyOnlyTargetFrameworks>

    Affected packages: Microsoft.Bcl.Memory, Microsoft.Bcl.TimeProvider, Microsoft.Extensions.Caching.Memory, Microsoft.Extensions.Configuration.Abstractions, Microsoft.Extensions.Logging, Microsoft.Extensions.Options, System.Formats.Asn1, System.Formats.Cbor, System.Formats.Nrbf, System.Linq.AsyncEnumerable, System.Text.Encoding.CodePages, System.Text.Json

  • docs/coding-guidelines/libraries-packaging.md — Added a DependencyOnlyTargetFrameworks section documenting the feature: what it is, when to use it, a code example using $(NetFrameworkWithValueTuple), and how the eng/packaging.targets infrastructure implements it.

Result

For each affected package, the nuspec gains an empty .NETFramework4.7.1 dependency group. net471 consumers resolve no System.ValueTuple dependency and fall back to lib/net462 for the assembly. No new assemblies ship; package sizes are unchanged.

  • Apply DependencyOnlyTargetFrameworks to 12 affected .csproj files
  • Implement DependencyOnlyTargetFrameworks in eng/packaging.targets
  • Add DependencyOnlyTargetFrameworks documentation to docs/coding-guidelines/libraries-packaging.md
  • Update PR description with Option 1/2 size measurements

Copilot AI and others added 4 commits April 10, 2026 16:34
…irect users (Option 2)

Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/55b33308-5b6c-4b57-9bdb-81b49c84b6bf

Co-authored-by: ericstj <8918108+ericstj@users.noreply.github.com>
…ref assets shipped)

Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/55b33308-5b6c-4b57-9bdb-81b49c84b6bf

Co-authored-by: ericstj <8918108+ericstj@users.noreply.github.com>
…n packaging.targets

Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/474682a7-ecbe-4a65-b384-1477530033eb

Co-authored-by: ericstj <8918108+ericstj@users.noreply.github.com>
@ericstj
Copy link
Copy Markdown
Member

ericstj commented Apr 10, 2026

@copilot can you fix the docs, can you also update this issue with the data we gathered by trying both option 1 and option 2. All of these measurements should be part of https://github.com/dotnet/runtime/tasks/1eacb5f4-97cd-4cc7-8fc3-812fe976ef9b?session_id=3ab46313-c615-4bf8-ab41-c39ac2b60d77

Copilot AI requested review from Copilot and removed request for Copilot April 10, 2026 22:54
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-infrastructure-libraries
See info in area-owners.md if you want to be subscribed.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 10, 2026

@copilot can you fix the docs, can you also update this issue with the data we gathered by trying both option 1 and option 2. All of these measurements should be part of https://github.com/dotnet/ru...

Added the documentation in commit 02fa65e (docs/coding-guidelines/libraries-packaging.md). For the issue data — the GitHub Tasks link points to an external session I can't access directly. The approach comparison (Options 1, 2, 3) is captured in the PR description above; if you'd like specific package size measurements included in the issue, please share those numbers and I can add them to the PR description or the tracking task.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants