Skip to content

Implement a Roslyn source generator for AssemblyScanAttribute - #3593

Merged
xen2 merged 32 commits into
stride3d:masterfrom
ds5678:assembly-scan-source-generator
Oct 23, 2026
Merged

xen2 merged 32 commits into
stride3d:masterfrom
ds5678:assembly-scan-source-generator

Conversation

@ds5678

@ds5678 ds5678 commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

PR Details

This pull request replaces the AssemblyScanProcessor in the AssemblyProcessor with a new Roslyn source generator in Stride.Core.CompilerServices.

Related Issue

Part of #1835

Types of changes

  • Docs change / refactoring / dependency upgrade

Checklist

  • All new and existing tests passed.
  • I have built and run the editor to try this change out.

Note

@github-actions

Copy link
Copy Markdown
Contributor

🤖 Draft PR — automatic CI is skipped to save runner minutes.

  • Mark the PR ready for review to run the full automatic CI — or add a ci-run-on-draft label to run it now without leaving draft.
  • Or arm a specific opt-in suite: ci-enduser, ci-editor, ci-ios, ci-android.

@ds5678
ds5678 force-pushed the assembly-scan-source-generator branch from cf8f1d0 to 2a8142a Compare October 22, 2026 07:29
@ds5678
ds5678 marked this pull request as ready for review October 22, 2026 07:38

@xen2 xen2 left a comment

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.

Thanks! A few points:

  • Rebase on latest master which merged #3678. Then you can rebuild AssemblyProcessor which is now deterministic (anybody can verify the build).
  • Some comments still say the scan index comes from the assembly processor (AssemblyRegistry.cs, Game.cs, StrideDefaultAssetsPlugin.cs, EditorGameController.cs, ...), and the StrideAssemblyProcessor comments in Stride.Audio.Editor.Wpf.csproj / Stride.UI.Editor.Wpf.csproj. Could you update them? Are those two still needed?
  • Could you add a few generator tests in Stride.Core.CompilerServices.Tests (Inherited = false, generic interface, nested type, keyword name)?

Comment thread sources/core/Stride.Core.CompilerServices/Generators/UnifiedGenerator.cs Outdated
Comment thread sources/core/Stride.Core.CompilerServices/Generators/UnifiedGenerator.cs Outdated
Comment thread sources/core/Stride.Core.CompilerServices/Generators/UnifiedGenerator.cs Outdated
Comment thread sources/core/Stride.Core.CompilerServices/Common/SymbolExtensions.cs Outdated
ds5678 added 18 commits October 22, 2026 11:14
Filter duplicate attribute type definitions from generator scan results.
Filter inaccessible symbols from assembly-scan generation to prevent generated code from referencing types that are not visible within the assembly. Centralize the accessibility check in a shared compilation helper.
Rename `AssemblyScanGenerator` to `UnifiedGenerator`.
Move generator data and definition records into partial-class files, make the generator sealed, and centralize attribute inheritance detection in a symbol extension.
Prefix reserved and contextual C# keywords with `@` in generated namespace, type, and generic parameter names to keep generated code valid.
Lower the assembly scan results module initializer order from -1000 to -10000 so they run earlier.
Enumerate referenced modules and scan types only in Stride.Core
or modules that reference it. Update type enumeration to work
from module symbols.
@ds5678
ds5678 force-pushed the assembly-scan-source-generator branch from 2a8142a to 9610783 Compare October 22, 2026 19:11
Remove the `StrideAssemblyProcessor` setting from the Audio and UI WPF editor projects.
Thread `CancellationToken` through reference scanning, type data generation, and scan result computation. Check cancellation while processing modules, types, interfaces, targets, and attributes so generator execution stops promptly when cancelled.

@xen2 xen2 left a comment

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.

Thanks for the fixes! The AssemblyProcessor binaries match a rebuild of the PR's source, so that part is good (thanks for being the first beta-tester).

You're right about RegisterImplementationSourceOutput, the IDE doesn't run it.
The filter is still nice for builds.

Still open: a few generator tests in Stride.Core.CompilerServices.Tests (Inherited = false, generic interface, nested type, keyword name). They would have caught the issue below.
Let me know if you want me to generate them?

Comment thread sources/core/Stride.Core.CompilerServices/Generators/UnifiedGenerator.cs Outdated
Comment thread sources/core/Stride.Core.CompilerServices/Generators/UnifiedGenerator.cs Outdated
@xen2 xen2 added the ci-enduser Run the end-user sample screenshot + packaging suite on this PR label Oct 22, 2026
@xen2 xen2 added ci-editor Run the GameStudio editor screenshot suite on this PR ci-ios Run the iOS game test suite on this PR ci-android Run the Android game test suite on this PR labels Oct 22, 2026
Rename `GeneratorTestBase` to `UnifiedGeneratorTestBase` and update `DerivedClassTests` inheritance while preserving the existing test context and configuration.
@ds5678
ds5678 requested a review from xen2 October 22, 2026 22:55
@ds5678

ds5678 commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

@xen2 I added some tests. Let me know if you think anything else should be covered.

@ds5678 ds5678 mentioned this pull request Oct 23, 2026
3 tasks done

@xen2 xen2 left a comment

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.

Thanks for the tests!
Just small things left below.

Comment thread sources/Directory.Packages.props Outdated
Move source generator testing to test-only dependencies and remove the unused Net100 reference assemblies package.
Rename `GenericInterfaceTests` to `InterfaceTests` and move the
generic interface scanning test. Add coverage verifying assembly scanning
discovers `IAppSettingsProvider` implementations from referenced assemblies.
@ds5678
ds5678 requested a review from xen2 October 23, 2026 09:50
@xen2

xen2 commented Oct 23, 2026

Copy link
Copy Markdown
Member

Editor CI is failing on all 3 graphics APIs, GameStudio crashes on startup with Could not load file or assembly 'Stride.Graphics'.

Looks like GameStudio now gets its own scan index (the old processor only did it for --serialization assemblies). Its module initializer builds the dictionary right away, so all the typeof load their assemblies and run their module initializers, all the way to Stride.Engine needing Stride.Graphics, before the AutoTesting host had a chance to set up the graphics API resolver in Main.

How about making the registration lazy? Module init would just register a factory, something like RegisterScanTypes(assembly, () => new ScanTypes(...)) stored in a Lazy, and the dictionary gets built on the first GetScanTypes(assembly). That way module init stays cheap and doesn't load anything, so we avoid this kind of load-order issue in general (and startup is a bit faster too).

@ds5678

ds5678 commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

How about making the registration lazy? Module init would just register a factory, something like RegisterScanTypes(assembly, () => new ScanTypes(...)) stored in a Lazy, and the dictionary gets built on the first GetScanTypes(assembly).

I'll take a look at this tomorrow.

That way module init stays cheap and doesn't load anything, so we avoid this kind of load-order issue in general (and startup is a bit faster too).

Do you want me to unmerge the generators, or is it still good to have a defined point in module initialization for that?

xen2 added 3 commits October 23, 2026 21:07
…tScanTypes (building it in the module initializer loaded the scanned types' assemblies too early)
# Conflicts:
#	sources/engine/Stride.Engine/Engine/Game.cs
…it again (AssetQuantumRegistry asks for it from a module initializer the index build triggers)
@xen2
xen2 merged commit a7f4379 into stride3d:master Oct 23, 2026
38 of 39 checks passed
@ds5678
ds5678 deleted the assembly-scan-source-generator branch October 23, 2026 20:28
@xen2

xen2 commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

How about making the registration lazy? Module init would just register a factory, something like RegisterScanTypes(assembly, () => new ScanTypes(...)) stored in a Lazy, and the dictionary gets built on the first GetScanTypes(assembly).

I'll take a look at this tomorrow.

That way module init stays cheap and doesn't load anything, so we avoid this kind of load-order issue in general (and startup is a bit faster too).

Do you want me to unmerge the generators, or is it still good to have a defined point in module initialization for that?

I took the liberty to do the lazy registration, so that I could merge this right away and you could focus on next steps of assembly processor removal.
Feel free to rebase next PR(s) on latest master to fix AssemblyProcessor.exe conflicts.
Thanks again!

@ds5678

ds5678 commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

I appreciate you getting this across the finish line. I'll rebase the other pull requests promptly!

@xen2

xen2 commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

I appreciate you getting this across the finish line. I'll rebase the other pull requests promptly!

No problem, it's me thanking you for all your work on removing the assembly processor. It's a huge task!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-android Run the Android game test suite on this PR ci-editor Run the GameStudio editor screenshot suite on this PR ci-enduser Run the end-user sample screenshot + packaging suite on this PR ci-ios Run the iOS game test suite on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants