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
7 changes: 7 additions & 0 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@
"dotnet-inspect"
],
"rollForward": false
},
"purview.build": {
"version": "0.5.3",
"commands": [
"purview-build"
],
"rollForward": false
}
}
}
6 changes: 5 additions & 1 deletion .config/purview-build.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
"TestFilter": "/*/*/*/*/"
},
"PackValidation": {
// Explicit because Purview.Build 0.5.3 defaults RequiredCompilerFlags to empty. Earlier
// versions forced this check on and could not be configured off, so these packages have
// always been validated against it; declaring it keeps that behaviour.
"RequiredCompilerFlags": ["optimization=release"],
"RequireSymbolPackage": true,
"RequireSymbolFiles": true,
"RequiredContent": {
Expand Down Expand Up @@ -59,4 +63,4 @@
"Release": {
"Mode": "None"
}
}
}
129 changes: 65 additions & 64 deletions .editorconfig

Large diffs are not rendered by default.

21 changes: 9 additions & 12 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,42 +12,39 @@ build_configuration := "Debug"
artifacts_folder := "./artifacts"
default_test_filter := "/*/*/*/*"

pipeline_feed := "https://api.nuget.org/v3/index.json"
pipeline_tool := ".tools/purview-build/purview-build"
pipeline_tool := "dotnet purview-build"

current_version := `bun -p "require('./package.json').version"`

[private]
default:
just --list

# Install the shared Purview.Build tool (authenticated to the Purview-Dev feed) if not present
# Restore the repository's .NET local tools (.config/dotnet-tools.json)
[private]
ensure-pipeline-tool:
if [ ! -x "{{ pipeline_tool }}" ]; then \
dotnet tool install Purview.Build --tool-path .tools/purview-build --add-source "{{ pipeline_feed }}"; \
fi
dotnet tool restore

# Run the PR pipeline (restore, build, lint, tests)
[group('Pipeline')]
pipeline-pr *args:
just ensure-pipeline-tool
echo "Running PR pipeline..."
"{{ pipeline_tool }}" {{ args }}
{{ pipeline_tool }} {{ args }}

# Run the build pipeline (restore, build, lint)
[group('Pipeline')]
pipeline-build *args:
just ensure-pipeline-tool
echo "Running build pipeline..."
"{{ pipeline_tool }}" --Build:RunTests=false --Release:Mode=None {{ args }}
{{ pipeline_tool }} --Build:RunTests=false --Release:Mode=None {{ args }}

# Run the release pipeline (restore, build, lint, tests, pack, publish, GitHub release)
[group('Pipeline')]
pipeline-release *args:
just ensure-pipeline-tool
echo "Running release pipeline..."
"{{ pipeline_tool }}" --Release:Mode=NuGet {{ args }}
{{ pipeline_tool }} --Release:Mode=NuGet {{ args }}

# Run the release pipeline (restore, build, lint, tests, pack, local nuget publish)
# Note: `just` runs recipes through the shell, which strips backslashes from unquoted arguments.
Expand All @@ -58,21 +55,21 @@ pipeline-local-release *args:
just ensure-pipeline-tool
just lint-fix
echo "Running local release pipeline..."
"{{ pipeline_tool }}" --Release:Mode=LocalNuGet {{ args }}
{{ pipeline_tool }} --Release:Mode=LocalNuGet {{ args }}

# Run the pipeline with tests enabled
[group('Pipeline')]
pipeline-tests *args:
just ensure-pipeline-tool
echo "Running tests pipeline..."
"{{ pipeline_tool }}" --Build:RunTests=true --Release:Mode=None {{ args }}
{{ pipeline_tool }} --Build:RunTests=true --Release:Mode=None {{ args }}

# Run the pipeline through pack + validate (restore, build, lint, tests, pack, validate pack contents) without publishing/releasing
[group('Pipeline')]
pipeline-pack-validate *args:
just ensure-pipeline-tool
echo "Running pack + validate pipeline..."
"{{ pipeline_tool }}" --Build:RunPack=true --Build:ValidatePack=true --Release:Mode=None {{ args }}
{{ pipeline_tool }} --Build:RunPack=true --Build:ValidatePack=true --Release:Mode=None {{ args }}

# Build and test with the specified configuration, defaulting to "Debug"
[group('Build and Test')]
Expand Down
3 changes: 3 additions & 0 deletions docs/wiki/Analyzers.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ Opt out with `PurviewSourceGeneratorFrameworkAnalyzerValidation=false` (`PRSGD00
| `PSGFR39` | A non-packable Roslyn component that explicitly opts out of the default self-contained analyzer output (`PurviewMergeSourceGeneratorFrameworkForAnalyzerFiles=false`) while embedding the framework, otherwise the package embeds the loose framework DLL under `analyzers/`. |
| `PSGFR40` | In Roslyn components (`IsRoslynComponent=true`), reference SGF types as inline code (`<c>Type</c>`) instead of a `cref`: copied documentation must not depend on cref resolution. |
| `PSGFR41` | In Roslyn components whose framework implementation is merged, a public member (or generic constraint) whose signature references an SGF type: the merge internalizes every SGF type, so the signature is left referring to an internal type. Make the member or its declaring type non-public. |
| `PSGFR42` | A disposable Testing-harness result (`DriverRunResult`) is produced and never disposed, leaking the collectible `AssemblyLoadContext` and `MetadataLoadContext` of a compiled run. Reported as a `warning` when the run syntactically compiles to an assembly (a `Compile` options member or `CompileToAssembly = true`), otherwise as `info`. |

## Type-library and attribute-model diagnostics

Expand Down Expand Up @@ -101,6 +102,8 @@ analyzer rules above, including:
- `PreferInlineCodeForFrameworkCrefCodeFixProvider` — rewrites SGF XML doc `cref` targets to inline code (`<c>Type</c>`) (`PSGFR40`).
- `MakeComponentSurfaceNonPublicCodeFixProvider` — makes the exposing member, or its declaring type,
non-public (`PSGFR41`).
- `DisposeTestResultCodeFixProvider` — turns an undisposed test-harness result declaration into a
`using` declaration (`PSGFR42`).
- `MakeTypeLibrarySpecNonPublicCodeFixProvider` — declares a type-library spec non-public in a merged
component (`TLB0021`).
- `AttributeDataModelSymbolPropertyCodeFixProvider` — fixes attribute-data-model symbol properties.
Expand Down
4 changes: 2 additions & 2 deletions docs/wiki/Release-Flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ packs, validates, publishes to NuGet, and creates the GitHub release.

## Local pipelines

The `Justfile` wraps the shared `Purview.Build` pipeline (installed as a pinned dotnet tool to
`.tools/purview-build/purview-build`):
The `Justfile` wraps the shared `Purview.Build` pipeline (a pinned .NET local tool, restored from
`.config/dotnet-tools.json` and run as `dotnet purview-build`):

| Recipe | Pipeline mode | Purpose |
| --- | --- | --- |
Expand Down
17 changes: 17 additions & 0 deletions docs/wiki/Step-Cache-Tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,23 @@ exactly what the driver decided to recompute.

`GenerateIncrementalAsync` on `TUnitSourceGeneratorTestBase` wraps the same runner for test-base users.

## Log entries

Each `IncrementalCacheRun` carries the framework log entries that **that run** wrote, in the order the
generator wrote them, through `IncrementalCacheRun.LogEntries`. The sink is registered for the whole
sequence (the driver is shared) and drained between runs, so entries never accumulate across runs:

```csharp
var result = await GenerateIncrementalAsync([Source], cancellationToken: cancellationToken);

await Assert.That(result.Runs[0].LogEntries).IsNotEmpty(); // the first run executed the pipeline
await Assert.That(result.Runs[1].LogEntries).IsEmpty(); // the rerun reused the cached output
```

That makes logging a second, independent signal alongside the step reasons: a stage reported as `Cached`
should not have logged anything on that run. Entries are empty when
`SourceGeneratorTestOptions.EnableLogging` is disabled.

## Step reasons

Each pipeline stage (tracked by its tracking name) has outputs, each carrying an
Expand Down
20 changes: 20 additions & 0 deletions docs/wiki/Testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,26 @@ emitted assembly: inspect types, members and attributes without loading it into
executing any code. They are created lazily on first access. Dispose the result (or its
`DriverRunResult`) to unload the collectible context and release the metadata view.

[`PSGFR42`](Analyzers.md#rule-reference) enforces that: a `DriverRunResult` that is produced and never
disposed — including the indirect case where a helper creates a result, projects a value out of it and
returns only the projection — is flagged, as a `warning` when the run syntactically compiles to an assembly
and as `info` otherwise. `DisposeTestResultCodeFixProvider` inserts the `using`.

## Reference sharing and parallelism

The harness binds every trusted-platform assembly once per test process and hands the **same**
`MetadataReference` instances to every test compilation, so Roslyn reuses one set of assembly symbols
across the run instead of rebuilding symbol tables per test. Over 100 compilations of the same source
against the same assembly set, shared references peak at 156 MB where per-compilation references peak at
8,146 MB. An assembly named by `AdditionalAssemblyTypes` that is already in the trusted-platform set is a
no-op: the reference is not added a second time.

Because of that sharing, memory is largely flat in the degree of parallelism — but the test platform's
auto-detected default still costs more memory *and* more wall clock than an explicit ceiling. Wall clock
saturates at around four concurrent tests. On a 22-core machine, `--maximum-parallel-tests 8` measured
347–365 MB / 7.7–8.7s against 528–881 MB / 12.0–13.8s for the auto default. Set an explicit ceiling for a
test project that multi-targets, where several TFM executables run concurrently under `dotnet test`.

Analyzer options are preserved under their supplied keys. Keys without the Roslyn `build_property.`
prefix are additionally exposed as compiler-visible MSBuild properties, so either `MyGenerator_Disable`
or `build_property.MyGenerator_Disable` can be used in tests.
Expand Down
4 changes: 2 additions & 2 deletions global.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
"allowPrerelease": false
},
"msbuild-sdks": {
"Purview.BuildSdk": "1.0.4"
"Purview.BuildSdk": "1.0.5"
},
"test": {
"runner": "Microsoft.Testing.Platform"
}
}
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "purview-sourcegenerator-framework",
"version": "1.0.0",
"name": "sourcegenerator-framework",
"version": "1.1.0",
"license": "MIT",
"author": {
"name": "Kieron Lanning",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ PSGFR38 | Purview.SourceGeneratorFramework | Warning | Extension class is missin
PSGFR39 | Purview.SourceGeneratorFramework | Error | Roslyn component must produce a self-contained analyzer
PSGFR40 | Purview.SourceGeneratorFramework | Warning | SGF cref should be inline code
PSGFR41 | Purview.SourceGeneratorFramework | Warning | Component public surface exposes framework types
PSGFR42 | Purview.SourceGeneratorFramework | Info | Dispose test harness results with a using declaration
TLB0014 | TypeLibrary | Warning | Type library partial extension is declared in a different namespace
TLB0015 | TypeLibrary | Info | Type library partial extension must be declared 'public static partial'
TLB0016 | TypeLibrary | Error | Enum value member type must be TypeIdentity or EnumValueDefinition
Expand Down
Loading
Loading