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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ Version detection logging is disabled by default. Set `VersionDetectionLogEnable
| `NamespacePrefix` | *(required)* | Root namespace prefix, e.g. `Acme`. Results in `Acme.MyProject`. |
| `DisableNamespacePrefixCheck` | `false` | Set to `true` to suppress the build error for missing `NamespacePrefix`. |
| `TargetFramework` | `net10.0` | Override the default TFM per-project or globally. Defaults to `netstandard2.0` for projects declaring `IsRoslynComponent=true`. |
| `IsRoslynComponent` | `false` | When explicitly `true`, applies source-generator defaults: a single `netstandard2.0` target, `LangVersion=latest`, `Nullable=enable`, `TreatWarningsAsErrors=true`, extended analyzer rules, SourceLink, generated-file output, dependency output, symbol packaging (`IncludeSymbols=false` by default), telemetry exclusion, and package build output. Packable Roslyn components automatically pack the built analyzer assembly (and its PDB) into `analyzers/dotnet/cs/`. Roslyn development dependencies (`Microsoft.CodeAnalysis.*`, `Microsoft.CodeAnalysis.Analyzers`) default to `PrivateAssets="all"`. |
| `IsRoslynComponent` | `false` | When explicitly `true`, applies source-generator defaults: a single `netstandard2.0` target, `LangVersion=latest`, `Nullable=enable`, `TreatWarningsAsErrors=true`, `Deterministic=true`, extended analyzer rules, SourceLink with `EmbedUntrackedSources=true`, generated-file output, dependency output, symbol packaging (`IncludeSymbols=false` by default), telemetry exclusion, and package build output. Packable Roslyn components automatically pack the built analyzer assembly and its PDB into `analyzers/dotnet/cs/` (`PurviewPackAnalyzerPdb=true`; set `false` only when symbols are delivered another way — NuGet's `.snupkg` cannot host `analyzers/dotnet/cs` symbols). Pack-time validation (`ValidateRoslynComponentCompilerSettings`) fails the pack if the compiler defaults are missing unless `DisableRoslynCompilerDefaultsValidation=true`. Roslyn development dependencies (`Microsoft.CodeAnalysis.*`, `Microsoft.CodeAnalysis.Analyzers`) default to `PrivateAssets="all"`. |
| `PackProjectReferencedSourceGenerators` | `true` | Automatically packs analyzer `ProjectReference` outputs and their runtime dependencies under `analyzers/dotnet/cs/`. Set to `false` to opt out; set `Pack="false"` on an individual reference to exclude only that generator. |
| `SourceLinkPackageName` | `Microsoft.SourceLink.GitHub` | SourceLink provider. Set to `Microsoft.SourceLink.AzureDevOps.Git` for ADO repos. |
| `DisableSourceLink` | `false` | Set to `true` to stop the SDK from adding the configured SourceLink package automatically. |
Expand All @@ -243,7 +243,7 @@ For projects where `IsPackable=true`, the SDK provides these defaults **only whe
| Property | Default | Description |
| -- | -- | -- |
| `GenerateDocumentationFile` | `true` | Emits XML documentation. |
| `IncludeSymbols` | `true` | Produces a symbol package (`false` for Roslyn components — their PDB ships in `analyzers/dotnet/cs/` instead). |
| `IncludeSymbols` | `true` | Produces a symbol package (`false` for Roslyn components — their PDB ships inside the `.nupkg` under `analyzers/dotnet/cs/` via `PurviewPackAnalyzerPdb=true`). |
| `SymbolPackageFormat` | `snupkg` | Symbol package format. Always the modern `.snupkg`; the legacy `.symbols.nupkg` is never produced by default. |
| `PublishRepositoryUrl` | `true` | Publishes the repository URL. |
| `EmbedUntrackedSources` | `true` | Embeds untracked sources for SourceLink. |
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"name": "changeops",
"version": "1.0.0-prerelease.45"
"name": "pruview-dotnet-project-sdk",
"version": "1.0.0-prerelease.46"
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ These are the most important configurable properties exposed by the SDK:
- `NamespacePrefix` — required unless `DisableNamespacePrefixCheck=true`
- `DisableNamespacePrefixCheck` — default `false`
- `TargetFramework` — defaults to `net10.0` when neither `TargetFramework` nor `TargetFrameworks` is set; projects explicitly declaring `IsRoslynComponent=true` default to `netstandard2.0`
- `IsRoslynComponent` — when explicitly `true`, applies source-generator defaults: a single `netstandard2.0` target, `LangVersion=latest`, `Nullable=enable`, `TreatWarningsAsErrors=true`, extended analyzer rules, disabled SourceLink and untracked-source embedding, no dependency file, compiler-generated output under the framework-specific intermediate directory, telemetry exclusion, excluded normal build output, `IncludeSymbols=false` (no `.symbols.nupkg`/`.snupkg` by default — the PDB ships in `analyzers/dotnet/cs/`), and `PrivateAssets=all` applied to `Microsoft.CodeAnalysis.*` / `Microsoft.CodeAnalysis.Analyzers` references. Packable Roslyn components automatically pack the built analyzer assembly and PDB into `analyzers/dotnet/cs/`
- `IsRoslynComponent` — when explicitly `true`, applies source-generator defaults: a single `netstandard2.0` target, `LangVersion=latest`, `Nullable=enable`, `TreatWarningsAsErrors=true`, `Deterministic=true`, extended analyzer rules, SourceLink with `EmbedUntrackedSources=true`, no dependency file, compiler-generated output under the framework-specific intermediate directory, telemetry exclusion, excluded normal build output, `IncludeSymbols=false` (no `.symbols.nupkg`/`.snupkg` by default — the PDB ships inside the `.nupkg` under `analyzers/dotnet/cs/` via `PurviewPackAnalyzerPdb=true`), and `PrivateAssets=all` applied to `Microsoft.CodeAnalysis.*` / `Microsoft.CodeAnalysis.Analyzers` references. Packable Roslyn components automatically pack the built analyzer assembly and PDB into `analyzers/dotnet/cs/`; a pack-time validation (`ValidateRoslynComponentCompilerSettings`) fails the pack if the compiler defaults are missing unless `DisableRoslynCompilerDefaultsValidation=true`
- `PackProjectReferencedSourceGenerators` — default `true`; packable projects automatically include analyzer `ProjectReference` outputs and runtime dependencies under `analyzers/dotnet/cs/`. Set it to `false` globally or use `Pack="false"` on one analyzer reference to opt out.
- `EnableAssemblyNameGeneration` — default `true`; when `true`, `AssemblyName` and default `PackageId` follow the fully evaluated `RootNamespace`. Set `false` before the SDK import to use the standard project-name behaviour
- `DisableProjectFileNamingConventionCheck` — default `false`; disables the directory-name/file-name match validation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,12 @@ This is why consistent naming and placement matter so much in repos that use the

### For Roslyn component (analyzer/source-generator) projects

- Defaults a single `netstandard2.0` target, `LangVersion=latest`, `Nullable=enable`, `TreatWarningsAsErrors=true`, extended analyzer rules, disabled SourceLink, and excluded normal build output (`IncludeBuildOutput=false`)
- Defaults `IncludeSymbols=false` — no `.symbols.nupkg` or `.snupkg` is produced by default; the analyzer PDB ships inside the main `.nupkg` under `analyzers/dotnet/cs/` beside the analyzer assembly
- Defaults a single `netstandard2.0` target, `LangVersion=latest`, `Nullable=enable`, `TreatWarningsAsErrors=true`, `Deterministic=true`, extended analyzer rules, SourceLink with `EmbedUntrackedSources=true`, and excluded normal build output (`IncludeBuildOutput=false`)
- Defaults `IncludeSymbols=false` — no `.symbols.nupkg` or `.snupkg` is produced by default; the analyzer PDB ships inside the main `.nupkg` under `analyzers/dotnet/cs/` beside the analyzer assembly (`PurviewPackAnalyzerPdb=true`; set `false` only when symbols are delivered another way, since NuGet's `.snupkg` cannot host `analyzers/dotnet/cs` symbols)
- Packable Roslyn components automatically pack the built analyzer assembly (and PDB) into `analyzers/dotnet/cs/`; `SymbolPackageFormat` defaults to the modern `snupkg` if symbols are explicitly opted into
- `Microsoft.CodeAnalysis.*` and `Microsoft.CodeAnalysis.Analyzers` references are defaulted to `PrivateAssets=all` (development-only dependencies) so they never leak into the packed nuspec
- A pack-time validation (`ValidateRoslynComponentCompilerSettings`) fails the pack of a packable Roslyn component if `LangVersion`, `Nullable`, `TreatWarningsAsErrors`, or `EnforceExtendedAnalyzerRules` is missing; opt out with `DisableRoslynCompilerDefaultsValidation=true`
- `ContinuousIntegrationBuild` is set only by real CI environment variables — packability alone never forces SourceLink's CI-mode dirty-repository checks

### For test and shared-testing projects

Expand Down
50 changes: 46 additions & 4 deletions src/src/DotNetProjectSdk/Sdk/Sdk.props
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,40 @@
</CompilerVisibleProperty>
</ItemGroup>

<!-- =====================================================================
ROSLYN COMPONENT COMPILER-VISIBLE SETTINGS
Exposes the compiler defaults applied to Roslyn components to the
compiler (as build_property.* AnalyzerConfigOptions) so the SDK's own
analyzers/generators and downstream tooling can confirm the shipped
analyzer packages were built with the standard settings.
===================================================================== -->
<ItemGroup
Label="Roslyn Component Compiler Visible Settings"
Condition="'$(IsCSharpProject)' == 'true' AND '$(IsRoslynComponent)' == 'true'"
>
<CompilerVisibleProperty Include="LangVersion">
<Description>C# language version used to build the Roslyn component. Defaults to latest.</Description>
</CompilerVisibleProperty>
<CompilerVisibleProperty Include="Nullable">
<Description>Nullable reference-types context used to build the Roslyn component. Defaults to enable.</Description>
</CompilerVisibleProperty>
<CompilerVisibleProperty Include="TreatWarningsAsErrors">
<Description>Treats all compiler/analyzer warnings as errors. Defaults to true for Roslyn components.</Description>
</CompilerVisibleProperty>
<CompilerVisibleProperty Include="EnforceExtendedAnalyzerRules">
<Description>Enforces Roslyn analyzer best-practice rules. Defaults to true for Roslyn components.</Description>
</CompilerVisibleProperty>
<CompilerVisibleProperty Include="Deterministic">
<Description>Produce deterministic builds. Defaults to true.</Description>
</CompilerVisibleProperty>
<CompilerVisibleProperty Include="ContinuousIntegrationBuild">
<Description>True when building in a recognized CI environment, producing deterministic SourceLink output.</Description>
</CompilerVisibleProperty>
<CompilerVisibleProperty Include="EmbedUntrackedSources">
<Description>Embeds untracked source files for SourceLink. Defaults to true for Roslyn components.</Description>
</CompilerVisibleProperty>
</ItemGroup>

<!-- =====================================================================
C# PROJECT DEFAULTS
===================================================================== -->
Expand All @@ -330,8 +364,13 @@
modern snupkg format rather than the legacy symbols.nupkg. -->
<IncludeSymbols Condition="'$(IncludeSymbols)' == '' OR '$(IncludeSymbols)' == 'true'">false</IncludeSymbols>
<NoWarn>$(NoWarn);NU5128;</NoWarn>
<DisableSourceLink>true</DisableSourceLink>
<EmbedUntrackedSources>false</EmbedUntrackedSources>
<!-- The analyzer PDB ships inside the .nupkg under analyzers/dotnet/cs by default so symbols
are always delivered. NuGet's .snupkg only carries build-output (lib) symbols, so opt out
with PurviewPackAnalyzerPdb=false only when symbols are delivered another way. -->
<PurviewPackAnalyzerPdb Condition="'$(PurviewPackAnalyzerPdb)' == ''">true</PurviewPackAnalyzerPdb>
<!-- Roslyn components get the same SourceLink and source-embedding treatment as regular
packages so the shipped analyzer PDBs are source-linked and reproducible. -->
<EmbedUntrackedSources Condition="'$(EmbedUntrackedSources)' == ''">true</EmbedUntrackedSources>
<GenerateDependencyFile>false</GenerateDependencyFile>
<ExcludePurviewTelemetry>true</ExcludePurviewTelemetry>
<IncludeBuildOutput>false</IncludeBuildOutput>
Expand All @@ -340,9 +379,12 @@
<PropertyGroup Label="C# Project Property Groups" Condition="'$(IsCSharpProject)' == 'true'">
<TargetFramework Condition="'$(TargetFramework)' == '' AND '$(TargetFrameworks)' == ''"
>net10.0</TargetFramework>
<!-- Auto-detect CI environment from common env vars -->
<Deterministic Condition="'$(Deterministic)' == ''">true</Deterministic>
<!-- Auto-detect CI environment from common env vars. Local builds (even packable ones) must
not be treated as CI builds, otherwise SourceLink's CI-mode dirty-repository checks can
fail under TreatWarningsAsErrors when embedding untracked sources locally. -->
<ContinuousIntegrationBuild
Condition="'$(CI)' == 'true' OR '$(GITHUB_ACTIONS)' == 'true' OR '$(TF_BUILD)' == 'true' OR '$(IsPackable)' == 'true'"
Condition="'$(CI)' == 'true' OR '$(GITHUB_ACTIONS)' == 'true' OR '$(TF_BUILD)' == 'true'"
>true</ContinuousIntegrationBuild>
<!-- EditorConfig from the NuGet package Sdk/ folder — used for build-time code-style enforcement. -->
<_PurviewPackagedEditorConfigFilePath>$(MSBuildThisFileDirectory).editorconfig</_PurviewPackagedEditorConfigFilePath>
Expand Down
93 changes: 91 additions & 2 deletions src/src/DotNetProjectSdk/Sdk/Sdk.targets
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,16 @@
Condition="'$(IsCSharpProject)' == 'true' AND '$(IsRoslynComponent)' == 'true' AND '$(IsPackable)' == 'true' AND '$(TargetFrameworks)' == ''"
>
<None Include="$(TargetPath)" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<!-- The PDB ships inside the .nupkg beside the analyzer by default (PurviewPackAnalyzerPdb=true)
so symbols are always delivered. NuGet's .snupkg cannot host analyzers/dotnet/cs symbols
(symbols flow is gated on IncludeBuildOutput inside the PackTask), so opt out only when
symbols are delivered another way. -->
<None
Include="$(TargetDir)$(TargetName).pdb"
Pack="true"
PackagePath="analyzers/dotnet/cs"
Visible="false"
Condition="'$(DebugType)' != 'embedded' AND '$(DebugType)' != 'none'"
Condition="'$(DebugType)' != 'embedded' AND '$(DebugType)' != 'none' AND '$(PurviewPackAnalyzerPdb)' != 'false'"
/>
</ItemGroup>

Expand Down Expand Up @@ -64,6 +68,41 @@
</PackageReference>
</ItemGroup>

<!-- =====================================================================
VALIDATE ROSLYN COMPONENT COMPILER SETTINGS
Ensures packable Roslyn components are always shipped with the standard
compiler defaults so every analyzer package is built with latest C#,
nullable enable, warnings-as-errors, and extended analyzer rules.
Opt out with DisableRoslynCompilerDefaultsValidation=true.
===================================================================== -->
<Target
Name="ValidateRoslynComponentCompilerSettings"
BeforeTargets="Pack"
Condition="'$(IsCSharpProject)' == 'true' AND '$(IsRoslynComponent)' == 'true' AND '$(IsPackable)' == 'true' AND '$(DisableRoslynCompilerDefaultsValidation)' != 'true'"
Label="Validates Roslyn components are shipped with the standard compiler defaults."
>
<Error
Code="PRSGD0001"
Text="The Roslyn component '$(MSBuildProjectName)' does not set LangVersion. Source-generator packages must ship with a defined C# language version (default latest)."
Condition="'$(LangVersion)' == ''"
/>
<Error
Code="PRSGD0002"
Text="The Roslyn component '$(MSBuildProjectName)' does not set Nullable. Source-generator packages must ship with a defined nullable context (default enable)."
Condition="'$(Nullable)' == ''"
/>
<Error
Code="PRSGD0003"
Text="The Roslyn component '$(MSBuildProjectName)' disables TreatWarningsAsErrors. Source-generator packages must ship with warnings-as-errors enabled (default true)."
Condition="'$(TreatWarningsAsErrors)' != 'true'"
/>
<Error
Code="PRSGD0004"
Text="The Roslyn component '$(MSBuildProjectName)' disables EnforceExtendedAnalyzerRules. Source-generator packages must ship with extended analyzer rules enabled (default true)."
Condition="'$(EnforceExtendedAnalyzerRules)' != 'true'"
/>
</Target>

<Target
Name="CreateRoslynComponentGeneratedFilesDirectory"
BeforeTargets="CoreCompile"
Expand Down Expand Up @@ -164,6 +203,44 @@
/>
</Target>

<UsingTask
TaskName="DeduplicateSourceGeneratorPackageFiles"
TaskFactory="RoslynCodeTaskFactory"
AssemblyFile="$([System.IO.Path]::Combine('$(MSBuildToolsPath)', 'Microsoft.Build.Tasks.Core.dll'))"
>
<ParameterGroup>
<Files ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
<DeduplicatedFiles ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true" />
</ParameterGroup>
<Task>
<Using Namespace="System.IO" />
<Using Namespace="System.Collections.Generic" />
<Using Namespace="Microsoft.Build.Framework" />
<Using Namespace="Microsoft.Build.Utilities" />
<Reference Include="$([System.IO.Path]::Combine('$(MSBuildToolsPath)', 'Microsoft.Build.Framework.dll'))" />
<Reference Include="$([System.IO.Path]::Combine('$(MSBuildToolsPath)', 'Microsoft.Build.Utilities.Core.dll'))" />
<Code Type="Fragment" Language="cs">
<![CDATA[
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var results = new List<ITaskItem>();
foreach (var file in Files ?? Array.Empty<ITaskItem>())
{
// Two analyzer projects can share a runtime dependency (for example a shared
// support assembly). NuGet fails the pack on duplicate target paths, so keep
// only the first file for each resolved analyzers/dotnet/cs package path.
// The calling target re-applies PackagePath metadata afterwards.
var packagePath = "analyzers/dotnet/cs/" + Path.GetFileName(file.ItemSpec);
if (!seen.Add(packagePath))
continue;

results.Add(new TaskItem(file.ItemSpec));
}
DeduplicatedFiles = results.ToArray();
]]>
</Code>
</Task>
</UsingTask>

<Target
Name="IncludeProjectReferencedSourceGenerators"
Condition="'$(PackProjectReferencedSourceGenerators)' == 'true' AND ('$(DefaultTargetFramework)' == '' OR '$(TargetFramework)' == '$(DefaultTargetFramework)')"
Expand All @@ -187,8 +264,15 @@
<Output TaskParameter="TargetOutputs" ItemName="_PurviewSourceGeneratorPackageFile" />
</MSBuild>

<DeduplicateSourceGeneratorPackageFiles
Files="@(_PurviewSourceGeneratorPackageFile)"
Condition="'@(_PurviewSourceGeneratorPackageFile)' != ''"
>
<Output TaskParameter="DeduplicatedFiles" ItemName="_PurviewDeduplicatedSourceGeneratorPackageFile" />
</DeduplicateSourceGeneratorPackageFiles>

<ItemGroup>
<TfmSpecificPackageFile Include="@(_PurviewSourceGeneratorPackageFile)">
<TfmSpecificPackageFile Include="@(_PurviewDeduplicatedSourceGeneratorPackageFile)">
<PackagePath>analyzers/dotnet/cs/</PackagePath>
</TfmSpecificPackageFile>
</ItemGroup>
Expand Down Expand Up @@ -563,6 +647,7 @@
===================================================================== -->
<Target
Name="IncludeRepoReadmeInPackage"
DependsOnTargets="IncludeLinkedSdkFiles;IncludeSdkDotAgentsGitIgnoreFiles"
Condition="'$(IsPackable)' == 'true' AND '$(PackageReadmeFile)' == 'README.md' AND '$(RepoRoot)' != '' AND Exists('$(RepoRoot)/README.md') AND ('$(DefaultTargetFramework)' == '' OR '$(TargetFramework)' == '$(DefaultTargetFramework)')"
Label="Packs the repository-root README when present and PackageReadmeFile is unset."
>
Expand All @@ -571,6 +656,10 @@
Include="@(None)"
Condition="'%(None.Pack)' == 'true' AND $([System.String]::Copy('%(None.Filename)').ToLowerInvariant()) == 'readme'"
/>
<_PurviewPackedReadme
Include="@(None)"
Condition="($([System.String]::Copy('%(None.Link)').ToLowerInvariant()) == 'sdk/readme.md' OR $([System.String]::Copy('%(None.Link)').ToLowerInvariant()) == 'sdk\readme.md')"
/>
<_PurviewPackedReadme
Include="@(TfmSpecificPackageFile)"
Condition="$([System.String]::Copy('%(TfmSpecificPackageFile.Filename)').ToLowerInvariant()) == 'readme'"
Expand Down
Loading
Loading