Skip to content
Open
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
55 changes: 55 additions & 0 deletions documentation/consuming-nuget-package.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,58 @@ compilers) into an application directory.
evaluate or build projects, you will generally not need this package. Instead,
use [MSBuildLocator](https://aka.ms/msbuild/locator) to use a complete toolset
provided by the .NET SDK or Visual Studio.

## Target framework support and reference-only assets

The MSBuild packages are the build engine that ships **inside** the .NET SDK and
Visual Studio. They are not general-purpose libraries meant to be redistributed
and run on an arbitrary runtime. This shapes how the packages are laid out and
which target frameworks they "support."
Comment on lines +56 to +59

### One .NET runtime per band, plus .NET Framework

Each MSBuild release band ships a runtime (`lib/`) assembly for exactly one .NET
(Core) target framework — the one the matching SDK runs on — plus `net472` for
Visual Studio. For example:

| MSBuild version | .NET runtime asset | .NET Framework runtime asset |
| --------------- | ------------------ | ---------------------------- |
| 17.11 | `lib/net8.0` | `lib/net472` |
| 17.14 | `lib/net9.0` | `lib/net472` |
| 18.x | `lib/net10.0` | `lib/net472` |

A newer band advancing its .NET target framework (for example `net9.0` →
`net10.0`) is expected, not a regression. If you need to *run* against a specific
.NET runtime, choose the MSBuild band whose SDK ships that runtime.

### The `netstandard2.0` asset is compile-only (`ref/`, no `lib/`)

The packages also include a `netstandard2.0` **reference assembly** under
`ref/netstandard2.0`, but no `lib/netstandard2.0` runtime assembly. This is
intentional: it is a compile-time surface, not a redistributable runtime.

It exists so that a single MSBuild extension — a task, logger, analyzer, or SDK
resolver — can be compiled once (typically as `netstandard2.0`) and then loaded
into either .NET Framework MSBuild or .NET MSBuild, with the **host** supplying
the matching runtime implementation. Binding to the host's copy guarantees your
extension talks to the exact engine that is running the build, rather than a
mismatched copy shipped alongside it.

Because NuGet computes compile compatibility from the `ref/` folder, a project
targeting a framework compatible with `netstandard2.0` (for example `net8.0`)
will **compile** successfully against these packages even when there is no
matching runtime asset. At run time the assembly will not be found unless an
MSBuild host provides it, and the packages emit a build warning to that effect.

If you are authoring an MSBuild extension that runs inside an MSBuild host,
reference the package with compile-only assets:

```xml
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="..."
PrivateAssets="all" ExcludeAssets="runtime" />
```

If your code instead runs standalone (outside an MSBuild host), target a
framework for which the package ships a runtime assembly (see the table above).
Do not simply suppress the warning — suppressing it only converts a build
failure into a load-time failure.
2 changes: 1 addition & 1 deletion eng/packaging.targets
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<![CDATA[<Project InitialTargets="$(_NETStandardCompatErrorFileTarget)">
<Target Name="$(_NETStandardCompatErrorFileTarget)"
Condition="'%24(SuppressTfmSupportBuildWarnings)' == ''">
<Warning Text="$(PackageId) $(PackageVersion) doesn't support %24(TargetFramework) and has not been tested with it. Consider upgrading your TargetFramework to %(NETStandardCompatError.Supported) or later. You may also set &lt%3BSuppressTfmSupportBuildWarnings&gt%3Btrue&lt%3B/SuppressTfmSupportBuildWarnings&gt%3B in the project file to ignore this warning and attempt to run in this unsupported configuration at your own risk." />
<Warning Text="$(PackageId) $(PackageVersion) does not ship a runtime assembly for %24(TargetFramework). It provides a runtime implementation only for %(NETStandardCompatError.Supported) or later (and .NET Framework), plus a netstandard2.0 reference assembly that is for compilation only. Your project will compile against that reference assembly, but no runtime assembly is copied to your output, so the assembly will fail to load at run time unless an MSBuild host provides it (for example when your code runs as an MSBuild task, logger, or analyzer inside MSBuild). If your code runs standalone, retarget to %(NETStandardCompatError.Supported) or later. If it is a compile-only MSBuild extension loaded by an MSBuild host, reference this package with compile-only assets such as PrivateAssets=all and ExcludeAssets=runtime, then set &lt%3BSuppressTfmSupportBuildWarnings&gt%3Btrue&lt%3B/SuppressTfmSupportBuildWarnings&gt%3B to acknowledge this configuration and silence this warning." />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't like the "no runtime assembly is copied to your output, so the assembly will fail to load at run time unless an MSBuild host provides it" -- that's the normal case. I can't think of a supported case for copying the assemblies locally, actually . . .

</Target>
</Project>]]>
</_NETStandardCompatErrorFileContent>
Expand Down
Loading