Add .NET 10 support via NativeAOT-LLVM along side .NET 8 support - #4915
Merged
Conversation
1) Created exception to `check_global_json_policy` to allow for .NET 8 templates to be generated. 2) Removed .NET 10 DLLs from SDK to prevent Unity issue. 3) Updated NativeAOT smoketest test include `packageSourceMapping` in `NuGet.Config`.
…SDK` for Windows builds
- Add --dotnet-version flag to build/publish/init commands for explicit .NET version control - Fix smoketests to use local SpacetimeDB.Runtime/BSATN.Runtime packages (not public NuGet) - Fix CLI binary path detection for debug vs release builds - Add NativeAOT-LLVM output paths (native/ subdirectory) to csharp.rs - Force .NET 8 for C# templates in template tests to match template TFM
…rsion `25` from `29`
…imental` can install
bfops
reviewed
Jul 13, 2026
bfops
reviewed
Jul 13, 2026
bfops
reviewed
Jul 13, 2026
bfops
reviewed
Jul 14, 2026
bfops
reviewed
Jul 14, 2026
bfops
reviewed
Jul 14, 2026
bfops
reviewed
Jul 14, 2026
bfops
reviewed
Jul 14, 2026
bfops
reviewed
Jul 14, 2026
bfops
left a comment
Collaborator
There was a problem hiding this comment.
I left comments on my code-owned files.
The init, dev, and tasks::csharp subcommand changes are large enough that I find them hard to review, so I can't say that I've been super thorough with them.
bfops
approved these changes
Jul 17, 2026
bfops
left a comment
Collaborator
There was a problem hiding this comment.
all my comments on my code-owned files have been addressed.
my previous comment still stands:
The
init,dev, andtasks::csharpsubcommand changes are large enough that I find them hard to review, so I can't say that I've been super thorough with them.
but from the point of view of this being okay CLI code, I'm fine to merge. I can't speak to the dotnet-validity / edge cases around that stuff, but that's not what my codeowner review is for.
lisandroct
enabled auto-merge
July 17, 2026 17:12
13 tasks
pull Bot
pushed a commit
to age-rs/SpacetimeDB
that referenced
this pull request
Aug 18, 2026
…orklabs#5576) # Description of Changes Updates the LLM benchmark C# project template and generated NuGet configuration to support the repository’s .NET 10 NativeAOT build path. The repository now selects .NET 10 through `global.json`, but the LLM benchmark template targeted only `net8.0`. During C# benchmark runs, `spacetime publish` therefore selected `net10.0` while publishing a `net8.0`-only project. This caused golden-module preparation to fail with MSBuild error `NETSDK1005` before any model benchmarks could run. The project template now changes from: ```xml <TargetFramework>net8.0</TargetFramework> ``` to: ```xml <TargetFrameworks>net8.0;net10.0</TargetFrameworks> ``` After enabling the .NET 10 target, the benchmark exposed a second issue: its generated `NuGet.Config` did not include the `dotnet-experimental` feed required by the .NET 10 NativeAOT LLVM packages. Restore consequently failed to find: - `Microsoft.DotNet.ILCompiler.LLVM` - `runtime.<platform>.Microsoft.DotNet.ILCompiler.LLVM` The generated NuGet configuration now includes the `dotnet-experimental` feed and package-source mappings for those dependencies. The first full C# run then exposed a benchmark lifecycle issue. Generated databases remained loaded after scoring, allowing hundreds of C# NativeAOT modules to accumulate on the local server. The server eventually stopped responding, and the remaining publish failures were recorded as zero-score results. The benchmark harness now: - Deletes each generated database immediately after its scoring finishes - Keeps golden databases available while scorers compare schemas, reducers, and data - Deletes the golden databases after all models and modes finish - Reuses the publishing CLI identity when deleting each database - Treats cleanup and server availability failures as infrastructure failures - Stops remaining work and avoids uploading affected results when the local server fails - Reports the local server’s exit status when a benchmark run fails Together, these changes align the LLM benchmark project with the current C# server templates and prevent generated benchmark databases from accumulating throughout long-running model evaluations. This is a follow-up to the .NET 10 support introduced in clockworklabs#4915. # API and ABI breaking changes None. # Expected complexity level and risk **3 — moderate benchmark lifecycle change.** The C# template and NuGet changes are isolated configuration updates. The database cleanup changes affect the benchmark lifecycle for all supported server languages, but do not affect production runtime behavior. The primary risks are deleting a generated database before its scorers finish or deleting golden databases while they are still needed. The harness keeps each generated database alive through scoring and keeps all goldens alive until every model and mode has completed. # Testing - [x] Ran `cargo test -p xtask-llm-benchmark` — 10 tests passed - [x] Ran `cargo check -p xtask-llm-benchmark --all-targets` - [x] Ran `cargo clippy -p xtask-llm-benchmark --all-targets -- -D warnings` - [x] Verified with MSBuild that the template resolves `TargetFrameworks` to `net8.0;net10.0` - [x] Packed the local C# runtime packages successfully - [x] Restored the .NET 10 NativeAOT LLVM dependencies using the generated NuGet configuration - [x] Published C# NativeAOT golden modules and produced `StdbModule.wasm` - [x] Verified multiple C# goldens remain published through preparation and are deleted during final cleanup - [x] Completed a full C# dry run for all default models in both `guidelines` and `no_context` modes - [x] Confirmed the dry run completed with 1 successful benchmark run and 0 failures - [x] Confirmed the dry run did not upload results - [x] Ran `git diff --check` - [ ] Confirm the upload-enabled C# benchmark completes successfully Initial C# validation run: https://github.com/clockworklabs/SpacetimeDB/actions/runs/29789520324 Successful full C# dry run: https://github.com/clockworklabs/SpacetimeDB/actions/runs/29835979746 Upload-enabled C# run: https://github.com/clockworklabs/SpacetimeDB/actions/runs/29848593880 --------- Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Description of changes
Builds on #4741 to clean up and refine the NativeAOT-LLVM integration. The major changes:
Standardized the repo on .NET 10 SDK (
global.json→10.0.100). The team is migrating to .NET 10 ahead of .NET 8 EOL. End users are unaffected as their SDK version is controlled by their ownglobal.jsonemitted byspacetime init.Multi-target NuGet packages instead of conditional single-target:
SpacetimeDB.Runtimenow unconditionally targetsnet8.0;net10.0SpacetimeDB.BSATN.Runtimenow unconditionally targetsnetstandard2.1;net8.0;net10.0.nupkg. NoEXPERIMENTAL_WASM_AOTenv var needed at pack timeEXPERIMENTAL_WASM_AOTdefine constant is set automatically whenTargetFramework == net10.0ILCompiler.LLVMpackage references conditioned onTargetFramework == net10.0(not env var)Centralized NativeAOT-LLVM build logic in
SpacetimeDB.Runtime.propsandSpacetimeDB.Runtime.targets:PublishTrimmed,SelfContained,WasmEnableThreadsinto.props(previously duplicated across 4+.csprojfiles).wit-strippingUseWasiRuntimeOverlayWithoutComponentWittarget into.targets(was duplicated in every server.csproj)IlcLlvmTarget=wasm32-unknown-wasip1override after ILCompiler.LLVM.targets import (previously passed as/p:arg)_InitializeWasiSdktoObtainWasiSdkBeforeTargets for .NET 10 compatibility.csprojfiles no longer need any AOT boilerplateRefactored
csharp.rsbuild logic to support three build paths viaCsharpBuildPathenum:Net8Jit: stable .NET 8 path usingwasi-experimentalworkload (Mono WASM)Net8Aot: .NET 8 NativeAOT-LLVM (opt-in via--native-aotorEXPERIMENTAL_WASM_AOT=1)Net10Aot: .NET 10 NativeAOT-LLVM (auto-detected when .NET 10 SDK is active)dotnet --version(respectsglobal.json)EXPERIMENTAL_WASM_AOT=1for AOT paths so MSBuild conditionals activatedotnet publishcommand for all paths. Build-specific config handled by props/targets/p:IlcLlvmTargetand/p:WasmEnableThreadsCLI args (now in.targets)Refactored
init.rsfor .NET version-aware scaffolding:--dotnet-versionCLI arg to explicitly select .NET 8 or 10resolve_dotnet_major()which auto-detects or prompts interactively when multiple SDKs are installed--native-aotnot required)<TargetFramework>net10.0</TargetFramework>directly (no conditional)ILCompiler.LLVM 8.0.0-*package refs gated onEXPERIMENTAL_WASM_AOT=1global.jsonper SDK version (8.0.100or10.0.100)Added
_initializecall inwasmtime_module.rs: NativeAOT-LLVM modules are WASI reactors that export_initializeto bootstrap the native runtime. This is called before preinit functions. Traditional .NET 8 WASI modules export_startinstead and are unaffected.Updated
FFI.csWasmImportLinkageAttributeguard to#if EXPERIMENTAL_WASM_AOT && NET10_0_OR_GREATER, ensuring the real attribute is only used when both the AOT flag and .NET 10+ TFM are active. The dummy shim is used for all other builds.Updated
csharp_aot_module.rssmoketest:Updated CLI reference docs to reflect
--native-aotbehavior change (not needed for .NET 10) and new--dotnet-versionarg.This PR addresses several issues from #4741 review:
NativeAOT-LLVM build logic was duplicated in every server
.csproj(4+ files had identicalPropertyGroupandTargetblocks). This is now centralized in the shipped.props/.targets.NuGet packages only shipped a single TFM. The env var controlled which TFM was built, meaning you needed
EXPERIMENTAL_WASM_AOT=1at pack time to get thenet10.0DLL. Now both TFMs are always included.spacetime initemitted unnecessary conditional wrappers in the generated.csprojfor .NET 10 projects. Since the project is definitively targeting .NET 10, the TFM should be unconditional.spacetime publishpassed MSBuild properties via/p:args that are now handled by centralized.targets, simplifying the CLI code.API and ABI breaking changes
global.jsonnow requires .NET 10 SDK for repo development. All developers and CI agents need .NET SDK 10.0+ installed.global.jsonspecifies.Expected complexity level and risk
2 - The core risk is the same as #4741: future changes in the upstream NativeAOT-LLVM/WASI pipeline. Centralizing the build logic in
.props/.targetsreduces the surface area for breaking changes (one place to update vs. N consumer projects). The.wit-stripping workaround remains necessary until dotnet/runtimelab#3144 is resolved.Testing
spacetime publishwithout--native-aot)spacetime publishwithEXPERIMENTAL_WASM_AOT=1and .NET 8 SDK)spacetime publishwith .NET 10 SDK, auto-detected)spacetime init --lang csharpgenerates correct project structure for both .NET 8 and .NET 10spacetime init --lang csharp --dotnet-version 10emitsnet10.0TFM and10.0.100inglobal.jsonspacetime init, but the tests themselves remained identical.lib/net8.0/andlib/net10.0/DLLstest_build_csharp_module_aotto work with both .NET 8 and .NET 10 SDK