-
Notifications
You must be signed in to change notification settings - Fork 0
feat(scribe): design for BulletsForHumanity.Scribe.Sdk MSBuild SDK package #6
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
crazycrank
merged 4 commits into
master
from
copilot/create-dotnet-new-project-templates
Apr 6, 2026
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
c539432
Initial plan
Copilot 1a46e77
feat(scribe): add BulletsForHumanity.Scribe.Sdk MSBuild SDK package
Copilot 0334011
docs: update project-setup, architecture-infrastructure, and README f…
Copilot 4a22ed1
fix(scribe): address PR review comments on Scribe SDK
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| # BulletsForHumanity.Scribe.Sdk | ||
|
|
||
| A custom MSBuild SDK that eliminates boilerplate from Roslyn analyzer and source generator projects. | ||
|
|
||
| ## Quick Start | ||
|
|
||
| **1. Add a `global.json`** with the SDK version: | ||
|
|
||
| ```json | ||
| { | ||
| "msbuild-sdks": { | ||
| "BulletsForHumanity.Scribe.Sdk": "0.3.0" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| **2. Use the SDK** in your `.csproj`: | ||
|
|
||
| ```xml | ||
| <Project Sdk="BulletsForHumanity.Scribe.Sdk"> | ||
| <ItemGroup> | ||
| <PackageReference Include="Microsoft.CodeAnalysis.CSharp" PrivateAssets="all" /> | ||
| <PackageReference Include="BulletsForHumanity.Scribe" PrivateAssets="all" /> | ||
| </ItemGroup> | ||
| </Project> | ||
| ``` | ||
|
|
||
| That's it. The SDK handles everything else: | ||
|
|
||
| - `TargetFramework=netstandard2.0` (required by Roslyn compiler host) | ||
| - `LangVersion=14` for modern C# features | ||
| - `EnforceExtendedAnalyzerRules=true` to catch common mistakes | ||
| - Analyzer packaging (`analyzers/dotnet/cs/` placement, embedded PDB) | ||
| - Private dependency bundling (Scribe DLL alongside your analyzer) | ||
| - `Stubs.cs` polyfills for `init`, `record`, `required`, nullable annotations | ||
| - LocalDev multi-repo development infrastructure | ||
|
|
||
| ## What the SDK Sets | ||
|
|
||
| | Property | Default | Purpose | | ||
| |----------|---------|---------| | ||
| | `TargetFramework` | `netstandard2.0` | Required by the Roslyn compiler host | | ||
| | `LangVersion` | `14` | Modern C# features with polyfill stubs | | ||
| | `EnforceExtendedAnalyzerRules` | `true` | Catches analyzer authoring mistakes | | ||
| | `IncludeBuildOutput` | `false` | DLL goes into `analyzers/`, not `lib/` | | ||
| | `PackageType` | `Analyzer` | Marks package as an analyzer | | ||
| | `IncludeSymbols` | `false` | PDB is embedded in the DLL | | ||
| | `DebugType` | `embedded` | IDE debugging support | | ||
| | `Nullable` | `enable` | Null-safety | | ||
| | `GenerateDocumentationFile` | `false` | Not needed for bundled DLLs | | ||
| | `CopyLocalLockFileAssemblies` | `true` | Enables private dependency bundling | | ||
|
|
||
| All defaults are set in the early phase (`Sdk.props`) and can be overridden in your `.csproj`. Packaging targets (analyzer DLL placement, dependency bundling) are enforced by the SDK and cannot be overridden. | ||
|
|
||
| ## Stubs.cs Polyfills | ||
|
|
||
| The SDK auto-includes `Stubs.cs` with polyfill types for modern C# features on netstandard2.0: | ||
|
|
||
| - `init` setters / `record` types (C# 9) | ||
| - `[ModuleInitializer]`, `[SkipLocalsInit]` (C# 9) | ||
| - `[CallerArgumentExpression]` (C# 10) | ||
| - Interpolated string handlers (C# 10) | ||
| - `required` members (C# 11) | ||
| - `scoped ref` parameters (C# 11) | ||
| - Nullable flow analysis attributes | ||
|
|
||
| All polyfills are guarded by `#if !NET5_0_OR_GREATER` and deactivate on modern targets. | ||
|
|
||
| **Opt out:** Set `<ScribeSdkIncludeStubs>false</ScribeSdkIncludeStubs>` if you provide your own. | ||
|
|
||
| ## Packaging | ||
|
|
||
| `dotnet pack` produces a correct analyzer NuGet package: | ||
|
|
||
| - Your analyzer DLL is placed in `analyzers/dotnet/cs/` | ||
| - Private dependencies (like Scribe) are bundled alongside | ||
| - Roslyn SDK DLLs are excluded (provided by the compiler host) | ||
|
|
||
| ## LocalDev | ||
|
|
||
| The SDK includes [LocalDev](https://github.com/BulletsForHumanity/Scribe/blob/master/docs/project-setup.md#local-development-localdev) infrastructure for multi-repo development. See the main Scribe documentation for setup instructions. | ||
|
|
||
| ## Links | ||
|
|
||
| - [Scribe Documentation](https://github.com/BulletsForHumanity/Scribe) | ||
| - [Project Setup Guide](https://github.com/BulletsForHumanity/Scribe/blob/master/docs/project-setup.md) | ||
| - [Writing Generators](https://github.com/BulletsForHumanity/Scribe/blob/master/docs/writing-generators.md) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
| <!-- | ||
| BulletsForHumanity.Scribe.Sdk — MSBuild SDK for Roslyn analyzer/generator projects. | ||
|
|
||
| This is a pack-only project. It produces no build output — the NuGet package | ||
| contains only MSBuild props/targets, polyfill stubs, and LocalDev infrastructure. | ||
| --> | ||
| <PropertyGroup> | ||
| <PackageId>BulletsForHumanity.Scribe.Sdk</PackageId> | ||
| <TargetFramework>netstandard2.0</TargetFramework> | ||
| <!-- No build output — this is a pure SDK package. --> | ||
| <IncludeBuildOutput>false</IncludeBuildOutput> | ||
| <SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking> | ||
| <GenerateDocumentationFile>false</GenerateDocumentationFile> | ||
| <!-- NU5128: no DLL in lib/ — expected for SDK packages. | ||
| NU5129: build/ files don't need PackageId-matching names — this is an MSBuild SDK, not a regular package. | ||
| NETSDK1212: IsTrimmable not supported on netstandard2.0 — irrelevant for pack-only project. --> | ||
| <NoWarn>$(NoWarn);NU5128;NU5129;NETSDK1212</NoWarn> | ||
| <!-- Ensure .template.config and other dotfiles are included. --> | ||
| <NoDefaultExcludes>true</NoDefaultExcludes> | ||
| <Description>MSBuild SDK for Roslyn analyzer and source generator projects. Provides default project configuration, analyzer packaging targets, netstandard2.0 polyfill stubs, and LocalDev multi-repo development infrastructure. Use with: <Project Sdk="BulletsForHumanity.Scribe.Sdk"></Description> | ||
| <PackageTags>roslyn;analyzer;generator;source-generator;msbuild-sdk;scribe</PackageTags> | ||
| </PropertyGroup> | ||
|
|
||
| <!-- Pack the SDK props/targets into the Sdk/ directory. --> | ||
| <ItemGroup> | ||
| <None Include="Sdk\**" Pack="true" PackagePath="Sdk" /> | ||
| </ItemGroup> | ||
|
|
||
| <!-- Pack the polyfill stubs into the content/ directory. --> | ||
| <ItemGroup> | ||
| <None Include="content\**" Pack="true" PackagePath="content" /> | ||
| </ItemGroup> | ||
|
|
||
| <!-- Pack the LocalDev build infrastructure from the main Scribe project. --> | ||
| <ItemGroup> | ||
| <None Include="..\Scribe\build\Scribe.LocalDev.props" Pack="true" PackagePath="build" /> | ||
| <None Include="..\Scribe\build\Scribe.LocalDev.targets" Pack="true" PackagePath="build" /> | ||
| </ItemGroup> | ||
|
|
||
| <!-- Pack LICENSE and README. --> | ||
| <ItemGroup> | ||
| <None Include="..\LICENSE" Pack="true" PackagePath="" /> | ||
| <None Include="README.md" Pack="true" PackagePath="" /> | ||
| </ItemGroup> | ||
| </Project> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| <Project> | ||
| <!-- | ||
| BulletsForHumanity.Scribe.Sdk — Early Phase (Sdk.props) | ||
|
|
||
| Sets default properties for Roslyn analyzer/source generator projects. | ||
| Chains to the standard Microsoft.NET.Sdk. All properties set here can be | ||
| overridden by the consuming project file. | ||
|
|
||
| Usage: | ||
| <Project Sdk="BulletsForHumanity.Scribe.Sdk"> | ||
| <ItemGroup> | ||
| <PackageReference Include="Microsoft.CodeAnalysis.CSharp" PrivateAssets="all" /> | ||
| </ItemGroup> | ||
| </Project> | ||
|
|
||
| Requires a global.json entry for SDK resolution: | ||
| { | ||
| "msbuild-sdks": { | ||
| "BulletsForHumanity.Scribe.Sdk": "<version>" | ||
| } | ||
| } | ||
| --> | ||
|
|
||
| <!-- Chain to the standard .NET SDK (early phase). --> | ||
| <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" /> | ||
|
|
||
| <!-- ── Analyzer/Generator Project Defaults ───────────────────────────── --> | ||
| <!-- These can be overridden in the consuming .csproj. --> | ||
| <PropertyGroup> | ||
| <!-- Roslyn compiler host requires netstandard2.0. --> | ||
| <TargetFramework>netstandard2.0</TargetFramework> | ||
| <!-- Modern C# features, enabled by Stubs.cs polyfills. --> | ||
| <LangVersion>14</LangVersion> | ||
| <!-- Catches common analyzer authoring mistakes at compile time (e.g. RS1035). --> | ||
| <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules> | ||
| <!-- Analyzer DLL goes into analyzers/dotnet/cs/, not lib/. --> | ||
| <IncludeBuildOutput>false</IncludeBuildOutput> | ||
| <!-- Marks the NuGet package as an analyzer package. --> | ||
| <PackageType>Analyzer</PackageType> | ||
| <!-- Embed PDB into the DLL for IDE debugging. No separate .snupkg needed. --> | ||
| <IncludeSymbols>false</IncludeSymbols> | ||
| <DebugType>embedded</DebugType> | ||
| <!-- Enable nullable reference types for modern null-safety. --> | ||
| <Nullable>enable</Nullable> | ||
| <!-- Disable doc XML file — analyzer DLLs are bundled, not referenced directly. --> | ||
| <GenerateDocumentationFile>false</GenerateDocumentationFile> | ||
| <!-- NU5128: expected warning when lib/ is empty (due to IncludeBuildOutput=false). --> | ||
| <NoWarn>$(NoWarn);NU5128</NoWarn> | ||
| <!-- Copy NuGet dependency DLLs to output so they can be bundled into the analyzer package. --> | ||
| <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> | ||
| </PropertyGroup> | ||
|
|
||
| <!-- ── Stubs Opt-Out ─────────────────────────────────────────────────── --> | ||
| <!-- Consumers can set ScribeSdkIncludeStubs=false to skip Stubs.cs injection. --> | ||
| <PropertyGroup Condition="'$(ScribeSdkIncludeStubs)' == ''"> | ||
| <ScribeSdkIncludeStubs>true</ScribeSdkIncludeStubs> | ||
| </PropertyGroup> | ||
|
|
||
| <!-- ── LocalDev Infrastructure (Early Phase) ─────────────────────────── --> | ||
| <Import Project="$(MSBuildThisFileDirectory)..\build\Scribe.LocalDev.props" /> | ||
| </Project> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| <Project> | ||
| <!-- | ||
| BulletsForHumanity.Scribe.Sdk — Late Phase (Sdk.targets) | ||
|
|
||
| Defines packaging targets and auto-includes Stubs.cs polyfills. | ||
| These run after the project file has been evaluated, so they cannot | ||
| be overridden — they enforce correct analyzer packaging behaviour. | ||
| --> | ||
|
|
||
| <!-- Chain to the standard .NET SDK (late phase). --> | ||
| <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" /> | ||
|
|
||
| <!-- ── Auto-Include Stubs.cs Polyfills ───────────────────────────────── --> | ||
| <!-- | ||
| Injects Stubs.cs as a compile item. The polyfills are guarded by | ||
| #if !NET5_0_OR_GREATER and deactivate on modern targets. | ||
| Set ScribeSdkIncludeStubs=false in your .csproj to opt out. | ||
| --> | ||
| <ItemGroup Condition="'$(ScribeSdkIncludeStubs)' == 'true'"> | ||
| <Compile Include="$(MSBuildThisFileDirectory)..\content\Stubs.cs" | ||
| Link="Generated\Stubs.cs" | ||
| Visible="false" /> | ||
| </ItemGroup> | ||
|
|
||
| <!-- ── Analyzer DLL Packaging ────────────────────────────────────────── --> | ||
| <!-- | ||
| Places the analyzer DLL into analyzers/dotnet/cs/ in the NuGet package. | ||
| This is required for Roslyn to discover the analyzer at build time. | ||
| --> | ||
| <Target Name="_ScribeSdkAddAnalyzerDlls" BeforeTargets="_GetPackageFiles"> | ||
| <ItemGroup> | ||
| <None Include="$(TargetPath)" | ||
| Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" /> | ||
| </ItemGroup> | ||
| </Target> | ||
|
|
||
| <!-- ── Dependency Bundling ───────────────────────────────────────────── --> | ||
| <!-- | ||
| Bundles private NuGet dependencies alongside the analyzer DLL. | ||
| Roslyn SDK DLLs (Microsoft.CodeAnalysis.*) are excluded — they are | ||
| provided by the compiler host. | ||
| --> | ||
|
|
||
| <Target Name="_ScribeSdkAddAnalyzerDependencies" BeforeTargets="_GetPackageFiles" | ||
| DependsOnTargets="ResolvePackageAssets"> | ||
| <ItemGroup> | ||
| <_ScribeSdkDeps Include="@(ReferenceCopyLocalPaths)" | ||
| Condition="'%(ReferenceCopyLocalPaths.NuGetPackageId)' != '' | ||
| and '$([System.String]::Copy("%(ReferenceCopyLocalPaths.NuGetPackageId)").StartsWith("Microsoft.CodeAnalysis"))' != 'True' | ||
| and '%(Extension)' == '.dll'" /> | ||
| <None Include="@(_ScribeSdkDeps)" | ||
| Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" /> | ||
| </ItemGroup> | ||
| </Target> | ||
|
|
||
| <!-- ── LocalDev Infrastructure (Late Phase) ──────────────────────────── --> | ||
| <Import Project="$(MSBuildThisFileDirectory)..\build\Scribe.LocalDev.targets" /> | ||
| </Project> |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This README says "All properties can be overridden in your .csproj", but the SDK sets/enforces some behavior in
Sdk.targets(late phase) such as dependency bundling (CopyLocalLockFileAssemblies) and packaging targets, which cannot be overridden the same way. Please adjust the wording to distinguish overridable defaults (Sdk.props) from enforced behavior (Sdk.targets), and/or document the specific opt-out switches.