Skip to content
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
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Purview.BuildSdk 2.0.0 - managed file, do not edit
# Purview.BuildSdk 2.0.1 - managed file, do not edit
#
# This file is written by Purview.BuildSdk and is replaced whenever the SDK version changes. The version
# on the first line is how the SDK recognises a file it owns: a .editorconfig without that marker is
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ release summarise what the commits recorded rather than what was written at the

## [Unreleased]

## [2.0.1] - 2026-11-12

### Changed

- **Runtime Async is off by default.** A `net11.0`+ target no longer compiles with
`<Features>runtime-async=on</Features>` unless asked: it changes how every `await` compiles, so it
is an opt-in rather than a baseline. Set `PurviewRuntimeAsync=true` — or `UseRuntimeAsync=true`,
the name the .NET team uses — to enable it; an explicit `false` under either name keeps it off.
This reverts the 2.0.0 default, so a repository that relied on it should opt in.

## [2.0.0] - 2026-11-11

### Added

- **`ServerShared` projects.** A `ServerShared*` project is referenced automatically by every
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ A reusable MSBuild SDK NuGet package that delivers standardised .NET project def
| Feature | Detail |
| -- | -- |
| **Project type detection** | `IsCSharpProject`, `IsTestProject`, `IsSharedTestingProject`, `IsServerSharedProject`, `IsClientSideProject`, `IsContainerProject`, `IsWebSdkProject`, `IsAspireHostProject`, `IsBlazorWebAssemblyProject`, `IsBffProject`, … |
| **.NET 11 opt-ins** | A `net11.0` target compiles with Runtime Async (`runtime-async=on`) and the C# 15 updated memory-safety rules (`MemorySafetyRules=2`), while `AllowUnsafeBlocks` stays at its compiler default (`false`); builds use MSBuild's multithreaded mode through a bootstrapped `Directory.Build.rsp` (see [.NET 11 features](#net-11-features)) |
| **.NET 11 opt-ins** | A `net11.0` target compiles with the C# 15 updated memory-safety rules (`MemorySafetyRules=2`) and, when opted in with `PurviewRuntimeAsync=true`, Runtime Async (`runtime-async=on`), while `AllowUnsafeBlocks` stays at its compiler default (`false`); builds use MSBuild's multithreaded mode through a bootstrapped `Directory.Build.rsp` (see [.NET 11 features](#net-11-features)) |
| **ServerShared** | A `ServerShared*` project is referenced automatically by every non-client-side project, and never by a WebAssembly client, browser-safe library, Razor class library or `*.Client`-style project; `PRSHR0001`/`PRSHR0002` report a reference that would still put it in a browser download (see [ServerShared](#servershared)) |
| **Blazor and BFF** | Blazor WebAssembly clients, Blazor Web App hosts and BFF hosts are detected; `PurviewBrowserSafe` makes a library the client downloads fail the build on trim, AOT and browser-unsupported APIs, and the client checks every reference sets it (see [Blazor WebAssembly and BFF](#blazor-webassembly-and-bff)) |
| **C# defaults** | `net10.0;net11.0` TFMs (overridable), `LangVersion=latest` (C# 15), `Nullable=enable`, `ImplicitUsings=enable`, deterministic builds |
Expand Down Expand Up @@ -44,7 +44,7 @@ A reusable MSBuild SDK NuGet package that delivers standardised .NET project def
"runner": "Microsoft.Testing.Platform"
},
"msbuild-sdks": {
"Purview.BuildSdk": "2.0.0"
"Purview.BuildSdk": "2.0.1"
}
}
```
Expand Down Expand Up @@ -203,7 +203,7 @@ to a low-importance message) and only a copy that still fails after every retry
The bootstrapped `.editorconfig` opens with a version marker:

```editorconfig
# Purview.BuildSdk 2.0.0 - managed file, do not edit
# Purview.BuildSdk 2.0.1 - managed file, do not edit
```

That line is how the SDK recognises a file **it** wrote. When the SDK version changes, a file carrying
Expand Down Expand Up @@ -378,7 +378,7 @@ Version detection logging is disabled by default. Set `VersionDetectionLogEnable
| `AssemblyIdentifierClassName` | `<project name minus NamespacePrefix, dots removed>Assembly` | Overrides the name of the generated assembly identifier marker class. |
| `AutoIncludeUsings` | `true` | Controls SDK-added global usings for `NamespacePrefix` and `RootNamespace`. |
| `MemorySafetyRules` | `2` | C# 15 memory-safety tier for `net11.0`+ targets: `0` off, `1` preview pointer syntax, `2` updated rules (`updated-memory-safety-rules`, the default). `AllowUnsafeBlocks` is never changed. |
| `PurviewRuntimeAsync` | `true` | Compiles `net11.0`+ targets with `<Features>runtime-async=on</Features>`. `UseRuntimeAsync=false` also opts out. |
| `PurviewRuntimeAsync` | `false` | Set to `true` to compile `net11.0`+ targets with `<Features>runtime-async=on</Features>`. `UseRuntimeAsync=true` also opts in; an explicit `false` under either name keeps it off. |
| `IsClientSideProject` | *(detected)* | `true` for a project whose assemblies are downloaded to a browser — a WebAssembly client, `PurviewBrowserSafe` library, Razor class library, or `*.Client`/`*.Browser`/`*.Wasm`/`*.WebAssembly`/`*.Components` name. Set explicitly to override; a `ServerShared` project is never added to a client-side project. |
| `PurviewAutoServerSharedReference` | `true` | Adds `../ServerShared*/ServerShared*.csproj` to a non-client-side project. `false` removes the automatic reference. |
| `DisablePurviewServerSharedReferenceCheck` | `false` | Suppresses `PRSHR0001`/`PRSHR0002`. |
Expand Down Expand Up @@ -583,7 +583,7 @@ later) target, so the `net10.0` leg of the default `net10.0;net11.0` set is unaf

| Feature | Applied | Why |
| -- | -- | -- |
| Runtime Async V2 | `<Features>runtime-async=on</Features>` | .NET 11 no longer requires `EnablePreviewFeatures`, and the runtime libraries ship compiled with it. Cleaner live stack traces, lower await overhead, and continuations that skip `ExecutionContext` when nothing is ambient. Opt out with `PurviewRuntimeAsync=false` or `UseRuntimeAsync=false`. |
| Runtime Async V2 (opt-in) | `<Features>runtime-async=on</Features>` when enabled | Enables Runtime Async for a target where the team has decided it is wanted: cleaner live stack traces, lower await overhead, and continuations that skip `ExecutionContext` when nothing is ambient. It is **off by default** because it changes how every `await` compiles; opt in with `PurviewRuntimeAsync=true` or `UseRuntimeAsync=true`. An explicit `false` under either name keeps it off. |
| C# 15 updated memory-safety rules | `<Features>updated-memory-safety-rules</Features>` (`MemorySafetyRules=2`) | `unsafe` marks requires-unsafe APIs and the compiler records the choice in `MemorySafetyRulesAttribute`. Independent of `LangVersion`, so the default `latest` is enough. |
| Unsafe code | **not** enabled | `AllowUnsafeBlocks` stays at the compiler default (`false`). The updated model is strictest with unsafe code disallowed; a project that needs pointers opts in itself. |
| Multithreaded MSBuild | `Directory.Build.rsp` with `-mt` | Runs a build's projects concurrently in one process, which is 20–26% faster on large solutions per the MSBuild measurements. `-mt` is a host switch, so it cannot be set from a project; the response file is the only mechanism. `-nr:false` accompanies it because a node left by a non-`-mt` build can cache an `RoslynCodeTaskFactory` task with no assembly path and fail the next `-mt` build (`dotnet/msbuild#15213`); the short-lived server still gives Server GC. |
Expand Down
6 changes: 3 additions & 3 deletions docs/wiki/Configuration-Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ See [Version Detection](Version-Detection.md) for the full resolution rules.
| `AssemblyIdentifierClassName` | `<project name minus NamespacePrefix, dots removed>Assembly` | Overrides the name of the generated assembly identifier marker class. |
| `AutoIncludeUsings` | `true` | Controls SDK-added global usings for `NamespacePrefix` and `RootNamespace`. |
| `MemorySafetyRules` | `2` | C# 15 memory-safety tier on `net11.0`+ targets: `0` off, `1` preview pointer syntax (sets `LangVersion=preview` when the project has not chosen one), `2` updated rules (`updated-memory-safety-rules`). An unrecognised value fails with `PRCFG0001`. `AllowUnsafeBlocks` is never changed. |
| `PurviewRuntimeAsync` | `true` | Adds `<Features>runtime-async=on</Features>` on `net11.0`+ targets. `PurviewRuntimeAsync=false` or `UseRuntimeAsync=false` opts out. |
| `PurviewRuntimeAsync` | `false` | Set to `true` to add `<Features>runtime-async=on</Features>` on `net11.0`+ targets. `UseRuntimeAsync=true` also opts in; an explicit `false` under either name keeps it off. |
| `IsClientSideProject` | *(detected)* | `true` when the project is downloaded to a browser: a WebAssembly client, a `PurviewBrowserSafe` library, a Razor class library, or a `*.Client`/`*.Browser`/`*.Wasm`/`*.WebAssembly`/`*.Components` name. Set explicitly to override the detection either way. |
| `PurviewAutoServerSharedReference` | `true` | Adds `../ServerShared*/ServerShared*.csproj` to every non-client-side project. `false` disables it. |
| `DisablePurviewServerSharedReferenceCheck` | `false` | Turns off `PRSHR0001` and `PRSHR0002`. |
Expand Down Expand Up @@ -186,7 +186,7 @@ unchanged.

| Property | Applied | Notes |
| -- | -- | -- |
| `PurviewRuntimeAsync` | `true` | `<Features>runtime-async=on</Features>`. `UseRuntimeAsync=false` also opts out. |
| `PurviewRuntimeAsync` | `false` | When set to `true`, adds `<Features>runtime-async=on</Features>`. `UseRuntimeAsync=true` also opts in; an explicit `false` under either name keeps it off. |
| `MemorySafetyRules` | `2` | `updated-memory-safety-rules`; `1` sets `LangVersion=preview` for the pointer-syntax tier; `0` disables. `PRCFG0001` on an unknown value. |
| `AllowUnsafeBlocks` | *(compiler default: false)* | Never set by the SDK. |

Expand Down Expand Up @@ -301,7 +301,7 @@ read them through `build_property.<PropertyName>`:
| `DisablePurviewServerSharedReferenceCheck` | When `true`, suppresses `PRSHR0001`/`PRSHR0002`. |
| `PurviewServerSharedReferenceCheckAsError` | When `true`, reports `PRSHR0001`/`PRSHR0002` as errors. |
| `MemorySafetyRules` | C# 15 memory-safety tier applied to `net11.0`+ targets. |
| `PurviewRuntimeAsync` | When `true` (default), `net11.0`+ targets compile with `runtime-async=on`. |
| `PurviewRuntimeAsync` | When `true`, `net11.0`+ targets compile with `runtime-async=on`. Defaults to `false`; `UseRuntimeAsync=true` also opts in. |
| `PurviewMultiThreadedBuild` | When `true` (default), a managed `Directory.Build.rsp` enables MSBuild `-mt`. |
| `EditorConfigFilePath` | Path to the SDK-provided `.editorconfig` injected into `@(EditorConfigFiles)`. |
| `RepositoryEditorConfigFilePath` | Destination path for bootstrapping a physical repo-level `.editorconfig`. |
Expand Down
2 changes: 1 addition & 1 deletion docs/wiki/Getting-Started.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Add `Purview.BuildSdk` to the `msbuild-sdks` section so MSBuild can resolve the
"runner": "Microsoft.Testing.Platform"
},
"msbuild-sdks": {
"Purview.BuildSdk": "2.0.0"
"Purview.BuildSdk": "2.0.1"
}
}
```
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "build-sdk",
"version": "2.0.0",
"version": "2.0.1",
"homepage": "https://purview.dev/projects/build-sdk/",
"bugs": {
"url": "https://github.com/purview-dev/build-sdk/issues"
Expand Down
2 changes: 1 addition & 1 deletion skills/purview-buildsdk-configuration/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ in `Directory.Build.props` unless the setting is deliberately project-specific.
| `IsBffProject` | name ends `Bff` | Classifies a BFF host; `false` opts out |
| `PurviewBrowserSafeReferenceCheckAsError` | `false` | Makes `PRBLZ0001` (a client reference that is not browser-safe) an error |
| `MemorySafetyRules` | `2` | C# 15 memory-safety tier on `net11.0`+: `0` off, `1` preview syntax, `2` updated rules |
| `PurviewRuntimeAsync` | `true` | `runtime-async=on` on `net11.0`+ |
| `PurviewRuntimeAsync` | `false` | Opts a `net11.0`+ target into `runtime-async=on`; `UseRuntimeAsync=true` also opts in |
| `IsClientSideProject` | *(computed)* | Whether the project's assemblies can reach a browser; overrides the `ServerShared` classification |
| `PurviewAutoServerSharedReference` | `true` | Adds a sibling `ServerShared` reference to a non-client-side project |
| `PurviewMultiThreadedBuild` | `true` | Bootstraps a `Directory.Build.rsp` enabling MSBuild `-mt` |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ consuming project supplies nothing.
| Property | Default | Behaviour |
| -- | -- | -- |
| `MemorySafetyRules` | `2` | C# 15 memory-safety tier on `net11.0`+ targets: `0` off, `1` preview pointer syntax (sets `LangVersion=preview` when none was chosen), `2` `updated-memory-safety-rules`. Unknown values fail with `PRCFG0001`. Never touches `AllowUnsafeBlocks` |
| `PurviewRuntimeAsync` | `true` | `runtime-async=on` on `net11.0`+ targets. `UseRuntimeAsync=false` also opts out |
| `PurviewRuntimeAsync` | `false` | Set to `true` for `runtime-async=on` on `net11.0`+ targets. `UseRuntimeAsync=true` also opts in; an explicit `false` under either name keeps it off |
| `PurviewMultiThreadedBuild` | `true` | Bootstraps a managed `Directory.Build.rsp` with `-mt`/`-nr:false`; `false` writes `-mt:false` |
| `BootstrapMultiThreadedBuildRspToRepoRoot` | `true` | Whether that bootstrapped response file is written at all |
| `IsClientSideProject` | *(computed)* | `true` for a WebAssembly client, `PurviewBrowserSafe` library, Razor class library, or `*.Client`/`*.Browser`/`*.Wasm`/`*.WebAssembly`/`*.Components` name. Explicit value wins |
Expand Down
4 changes: 3 additions & 1 deletion src/src/BuildSdk/Sdk/Props/Defaults.props
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,9 @@
and PRSHR0002). Warning by default; set this to true to fail the build instead. -->
<PurviewServerSharedReferenceCheckAsError Condition="'$(PurviewServerSharedReferenceCheckAsError)' == ''"
>false</PurviewServerSharedReferenceCheckAsError>
<PurviewRuntimeAsync Condition="'$(PurviewRuntimeAsync)' == ''">true</PurviewRuntimeAsync>
<!-- Runtime Async is resolved per-TFM in Targets/Modern.targets, after the project body, so
PurviewRuntimeAsync=true or UseRuntimeAsync=true works from the project file as well as
from Directory.Build.props. It defaults to off: it changes how every await compiles. -->
<!-- MSBuild's single-process multithreaded build (-mt). Applied through the bootstrapped
Directory.Build.rsp, because -mt is a build-host switch and cannot be set from a project. -->
<PurviewMultiThreadedBuild Condition="'$(PurviewMultiThreadedBuild)' == ''">true</PurviewMultiThreadedBuild>
Expand Down
2 changes: 1 addition & 1 deletion src/src/BuildSdk/Sdk/Sdk.props
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@
<Description>The C# 15 memory-safety tier: 0 off, 1 preview syntax, 2 updated rules (default). AllowUnsafeBlocks is left unset.</Description>
</CompilerVisibleProperty>
<CompilerVisibleProperty Include="PurviewRuntimeAsync">
<Description>When true (default), a net11.0+ project compiles with the runtime-async compiler feature. UseRuntimeAsync=false opts out.</Description>
<Description>When true, a net11.0+ project compiles with the runtime-async compiler feature. Defaults to false; UseRuntimeAsync=true also opts in.</Description>
</CompilerVisibleProperty>
<CompilerVisibleProperty Include="PurviewMultiThreadedBuild">
<Description>When true (default), boots a Directory.Build.rsp that enables MSBuild's multithreaded build (-mt).</Description>
Expand Down
20 changes: 12 additions & 8 deletions src/src/BuildSdk/Sdk/Targets/Modern.targets
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
property. That matters: TargetFrameworks defaults to net10.0;net11.0, so
a switch that improves net11.0 must not be applied to the net10.0 leg.

Two compiler features are enabled for a net11.0+ target:
One compiler feature is enabled by default for a net11.0+ target, and
a second is available as an opt-in:

runtime-async=on Runtime Async V2. .NET 11 no longer needs
EnablePreviewFeatures for it, and the
runtime libraries themselves ship built with
it, so an application built without it runs
against libraries that did opt in.
runtime-async=on Runtime Async V2. Off by default: it
changes how every await compiles, so it is
opted into with PurviewRuntimeAsync=true
(or UseRuntimeAsync=true), never assumed.

updated-memory-safety-rules
updated-memory-safety-rules (default)
The C# 15 memory-safety model: 'unsafe'
marks requires-unsafe APIs and the caller
must acknowledge the access. Independent of
Expand Down Expand Up @@ -43,7 +43,11 @@
</PropertyGroup>

<PropertyGroup Label=".NET 11 feature selection" Condition="'$(_PurviewTargetsNet11OrLater)' == 'true'">
<PurviewRuntimeAsync Condition="'$(PurviewRuntimeAsync)' == ''">true</PurviewRuntimeAsync>
<!-- Runtime Async is opt-in. The .NET team's property name opts in too, and an explicit false
under either name wins, so a repository can turn it off regardless of how it was enabled. -->
<PurviewRuntimeAsync Condition="'$(PurviewRuntimeAsync)' == '' AND '$(UseRuntimeAsync)' != ''"
>$(UseRuntimeAsync)</PurviewRuntimeAsync>
<PurviewRuntimeAsync Condition="'$(PurviewRuntimeAsync)' == ''">false</PurviewRuntimeAsync>
<MemorySafetyRules Condition="'$(MemorySafetyRules)' == ''">2</MemorySafetyRules>
<_PurviewMemorySafetyRulesNormalized>$([System.String]::Copy('$(MemorySafetyRules)').Trim().ToLowerInvariant())</_PurviewMemorySafetyRulesNormalized>
<!-- The planned stable property's tiers: 0/off, 1 (preview syntax), 2 (updated rules). -->
Expand Down
Loading
Loading