SpacetimeDB C 模块使用 NativeAOT-LLVM 编译实战指南:从 .NET 8 到 .NET 10 的 WASM 原生编译完整配置
2026/9/12 16:59:53 网站建设 项目流程

SpacetimeDB C# 模块使用 NativeAOT-LLVM 编译实战指南:从 .NET 8 到 .NET 10 的 WASM 原生编译完整配置

【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB

本文以 SpacetimeDB 官方文档 NATIVEAOT-LLVM.md 为核心骨架,结合仓库中 CLI 与 C# Runtime 的真实源码,系统讲解如何为 C# SpacetimeDB 模块启用 NativeAOT-LLVM 编译,将 C# 代码直接编译为原生 WebAssembly(WASM)以提升性能。读完本文,你将掌握 .NET 8(Windows)与 .NET 10(Windows/Linux)两套 AOT 构建目标的完整项目配置、spacetime init/spacetime publish/spacetime.json三种激活方式、WASI SDK 自动下载机制,以及常见构建故障的排查方法。

[!WARNING] NativeAOT-LLVM 目前仍处于实验阶段,用于生产环境前请充分评估与测试。

概述:C# 模块的三种构建路径

SpacetimeDB 为 C# 模块提供三种构建目标,区别在于 .NET 版本、目标平台与运行方式:

构建目标.NET 版本支持平台说明
JIT(Mono).NET 8.0Windows、Linux、macOS使用 Mono 运行时解释执行(默认路径)
NativeAOT-LLVM.NET 8.0仅 Windows将 C# 编译为原生 WASM
NativeAOT-LLVM.NET 10.0+Windows、Linux将 C# 编译为原生 WASM

[!NOTE] .NET 8.0 的 NativeAOT-LLVM 仅支持 Windows,原因在于runtime.linux-x64.Microsoft.DotNet.ILCompiler.LLVM从未发布到 dotnet-experimental feed,Linux 用户必须使用 .NET 10 才能获得 NativeAOT 支持。

这一限制在 CLI 源码中有明确校验。crates/cli/src/common_args.rs中的nativeaot_unsupported_on_host函数直接编码了平台约束:macOS 上任何版本都拒绝启用,Linux 上仅在 .NET 10 时放行;ensure_nativeaot_supported_on_host在不满足条件时会直接报错:

pub(crate) const NATIVEAOT_UNSUPPORTED_MESSAGE: &str = "NativeAOT-LLVM in only supported on Windows and Linux (.NET 10)."; pub(crate) fn nativeaot_unsupported_on_host(os: &str, dotnet_version: Option<u8>) -> bool { os == "macos" || (os == "linux" && dotnet_version == Some(8)) }

(见 crates/cli/src/common_args.rs)

NativeAOT-LLVM 的构建原理:从 IL 到原生 WASM

理解 NativeAOT-LLVM 之前,先看 SpacetimeDB 如何组织 C# 构建路径。crates/cli/src/tasks/csharp.rs中定义了CsharpBuildPath枚举,清晰地划分出三条路径:

enum CsharpBuildPath { /// .NET 8 JIT via the `wasi-experimental` workload (Mono WASM). Net8Jit, /// .NET 8 NativeAOT-LLVM (opt-in via `--native-aot`). Net8Aot, /// .NET 10 NativeAOT-LLVM (auto-detected, only available path for .NET 10). Net10Aot, }

(见 crates/cli/src/tasks/csharp.rs)

路径选择的核心逻辑如下:

  • .NET 10:无条件走 NativeAOT-LLVM,即使不带--native-aot标志也如此(此时 CLI 会提示 "Note: --native-aot is not needed with .NET 10");
  • .NET 8 +--native-aot:走 .NET 8 的 ILCompiler.LLVM 包路径;
  • .NET 8 不带标志:走原有wasi-experimentalworkload 的 Mono JIT 路径。

关键在于EXPERIMENTAL_WASM_AOT环境变量。CLI 在构建前会设置或移除它(见 crates/cli/src/tasks/csharp.rs):

  • Net8Aot/Net10Aot必须设置EXPERIMENTAL_WASM_AOT=1——因为SpacetimeDB.Runtime.targets中的 ILCompiler.LLVM 导入逻辑以该环境变量为开关,未设置时dotnet只会产出托管 DLL 而不是.wasm
  • Net8Jit必须移除该变量——防止 CI 等环境中全局设置导致 JIT 构建被错误切到 NativeAOT 模式。

从 MSBuild 侧看,crates/bindings-csharp/Runtime/build/SpacetimeDB.Runtime.targets是这套机制的落地文件,其中包含几个关键设计:

  1. 条件导入 ILCompiler.LLVM.targets:仅当EXPERIMENTAL_WASM_AOT == '1'且存在包时才导入;
  2. .NET 10 自动检测_UseNativeAotLlvm属性在TargetFramework.StartsWith('net10.')时自动为true,这就是 .NET 10 无需任何标志的原因;
  3. 固定 WASI 目标为 Preview 1:强制IlcLlvmTargetwasm32-unknown-wasip1,并关闭IsWasiProjectWasmGenerateAppBundle。SpacetimeDB 宿主只支持 WASI Preview 1(wasip1),因此 targets 中还会剥离.wit文件,防止 NativeAOT-LLVM 生成 WebAssembly Component Model(wasip2)导出导致运行失败;
  4. 声明宿主导入表:通过WasmImport列出spacetime_10.0~spacetime_10.5各版本模块宿主函数(table_id_from_namedatastore_insert_bsatnconsole_logprocedure_start_mut_tx等),AOT 模式使用NativeLibrary(绑定bindings.c),JIT 模式改用NativeFileReference

(见 crates/bindings-csharp/Runtime/build/SpacetimeDB.Runtime.targets)

前置条件

启用 NativeAOT-LLVM 前,需要准备:

  • .NET SDK 8.0.NET SDK 10.0
  • WASI SDK(首次 AOT 构建时自动下载)
  • (可选)Binaryen(wasm-opt),用于 WASM 优化

WASI SDK:自动下载机制

WASI SDK 是 NativeAOT-LLVM 编译的必需工具链。它由构建流程自动下载,默认存放位置如下:

平台下载位置
Windows%USERPROFILE%\.wasi-sdk\wasi-sdk-29
Linux/macOS~/.wasi-sdk/wasi-sdk-29

下载与解压逻辑由SpacetimeDB.Runtime.targets中的ObtainWasiSdk目标实现(见 SpacetimeDB.Runtime.targets),几个值得注意的实现细节:

  • 版本按目标框架选择.NET 10目标使用wasi-sdk-29,而.NET 8目标使用wasi-sdk-24
  • 按架构/系统拼装下载 URL:自动区分x86_64/arm64与 Windows/Linux/macOS;
  • 环境变量优先:如果WASI_SDK_PATH已设置且指向的bin/clang(Windows 下为clang.exe)存在,则跳过下载直接使用;
  • 跨平台解压:依赖 Windows 10+ 与各 Linux 发行版内置的tar完成解压;
  • 抑制 .NET 10 的硬编码版本检查:.NET 10 的WasiApp.targets要求精确的 wasi-sdk 版本(25.0),这里主动覆盖该检查以支持更新的 SDK。

如需覆盖默认位置,可用WASI_SDK_PATH环境变量:

# Windows $env:WASI_SDK_PATH="C:\Tools\wasi-sdk" # Linux/macOS export WASI_SDK_PATH=/opt/wasi-sdk

构建目标一:.NET 8.0 NativeAOT-LLVM(仅 Windows)

面向希望在 Windows 上使用 .NET 8.0 SDK 获得 NativeAOT-LLVM 编译能力的用户。

需求清单

  • .NET SDK 8.0
  • Windows 操作系统
  • 配置了 dotnet-experimental feed 的 NuGet.Config

项目配置(.csproj)

.csproj必须包含条件化的 LLVM 包引用。注意Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1'"这一开关,它保证只有在启用 AOT 时才会引入 ILCompiler 相关包:

<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <RuntimeIdentifier>wasi-wasm</RuntimeIdentifier> </PropertyGroup> <ItemGroup> <PackageReference Include="SpacetimeDB.Runtime" Version="2.2.*" /> </ItemGroup> <!-- Required for .NET 8 AOT builds --> <ItemGroup Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1'"> <PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" /> <PackageReference Include="runtime.$(NETCoreSdkPortableRuntimeIdentifier).Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" /> </ItemGroup> </Project>

其中RuntimeIdentifier固定为wasi-wasm,这与 NativeAOT-LLVM 面向 WASI 目标的定位一致(对应wasm32-unknown-wasip1目标三元组)。

NuGet.Config:dotnet-experimental feed

Microsoft.DotNet.ILCompiler.LLVM属于 .NET 官方实验性软件包,必须通过 dotnet-experimental 源获取。NuGet.Config需要同时配置包源与包源映射(packageSourceMapping),确保 ILCompiler 相关包只从实验源解析,其余包仍走 nuget.org:

<?xml version="1.0" encoding="utf-8"?> <configuration> <packageSources> <clear /> <add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" /> <add key="nuget.org" value="https://api.nuget.org/v3/index.json" /> </packageSources> <packageSourceMapping> <packageSource key="dotnet-experimental"> <package pattern="Microsoft.DotNet.ILCompiler.LLVM" /> <package pattern="runtime.*" /> </packageSource> <packageSource key="nuget.org"> <package pattern="*" /> </packageSource> </packageSourceMapping> </configuration>

激活 NativeAOT-LLVM(.NET 8)

共有三种方式启用 .NET 8 的 NativeAOT-LLVM 编译,三者本质相同——最终都是设置EXPERIMENTAL_WASM_AOT环境变量——但使用体验不同。

方式一:init时指定--native-aot

spacetime init --lang csharp --native-aot --dotnet-version 8 my-project

这种方式会创建出已按"方式三"配置好spacetime.json的项目,后续发布时始终采用 NativeAOT-LLVM,体验最一致。

方式二:publish时指定--native-aot

spacetime publish --native-aot my-database-name

方式三:spacetime.json配置

{ "module": "my-module", "native-aot": true }

native-aotdotnet-version都是模块级配置键,由spacetime publish命令的配置合并逻辑解析(见 crates/cli/src/subcommands/publish.rs)。CLI 在发布时会读取配置中的native_aot布尔值并写入publish_entry"native-aot": true字段。

手动dotnet build时的开关

NativeAOT-LLVM 依赖EXPERIMENTAL_WASM_AOT标志。调用spacetime publish时 CLI 会内部处理该变量,但如果想手动构建,需要显式传递:

dotnet build -f net8.0 -p:EXPERIMENTAL_WASM_AOT=1

[!IMPORTANT] 若不设置该变量,.csproj中的条件包引用不会生效,产物将是普通托管 DLL 而非.wasm。另外,CLI 在 .NET 8 AOT 构建前会删除缓存的project.assets.json强制重新 restore——因为此前若在未设置EXPERIMENTAL_WASM_AOT时 restore 过,缓存里缺少 ILCompiler 包,会导致dotnet publish悄悄回退到 Mono wasi-experimental 路径(见 crates/cli/src/tasks/csharp.rs)。


构建目标二:.NET 10.0+ NativeAOT-LLVM(Windows 与 Linux)

面向希望在 Windows或 Linux上获得 NativeAOT-LLVM 编译能力的用户。

需求清单

  • .NET SDK 10.0
  • Windows 或 Linux 操作系统
  • 配置了 dotnet-experimental feed 的 NuGet.Config

项目配置(.csproj)

.NET 10 的项目配置更简单——无需条件包引用。ILCompiler.LLVM 依赖对net10.0是无条件的,CLI 源码注释也指出 .NET 10 路径不需要删除project.assets.json强制重存(见 crates/cli/src/tasks/csharp.rs):

<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net10.0</TargetFramework> <RuntimeIdentifier>wasi-wasm</RuntimeIdentifier> </PropertyGroup> <ItemGroup> <PackageReference Include="SpacetimeDB.Runtime" Version="2.2.*" /> </ItemGroup> </Project>

NuGet.Config 配置与 .NET 8 相同:

<?xml version="1.0" encoding="utf-8"?> <configuration> <packageSources> <clear /> <add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" /> <add key="nuget.org" value="https://api.nuget.org/v3/index.json" /> </packageSources> <packageSourceMapping> <packageSource key="dotnet-experimental"> <package pattern="Microsoft.DotNet.ILCompiler.LLVM" /> <package pattern="runtime.*" /> </packageSource> <packageSource key="nuget.org"> <package pattern="*" /> </packageSource> </packageSourceMapping> </configuration>

global.json(按需配置)

如果 .NET 10 不是系统默认 SDK,需要创建global.json锁定版本:

{ "sdk": { "version": "10.0.100", "rollForward": "latestMinor" } }

使用spacetime init时若指定--dotnet-version 10,CLI 会自动生成该文件。构建时 CLI 会校验实际活动的 SDK 主版本与目标一致,若不一致会提示创建/更新global.json(见 crates/cli/src/tasks/csharp.rs)。

激活 NativeAOT-LLVM(.NET 10)

.NET 10 下 NativeAOT-LLVM 是默认且唯一的构建路径,无需额外标志即自动启用。也可以显式指定:

方式一:init时指定 .NET 10(推荐)

spacetime init --lang csharp --dotnet-version 10 my-project

方式二:使用--native-aot标志

spacetime init --lang csharp --native-aot my-project

方式三:spacetime.json配置

{ "module": "my-module", "native-aot": true }

[!NOTE]--dotnet-version参数只接受810,其他值(如9)会被 CLI 直接拒绝并提示 "Unsupported --dotnet-version"。省略该参数时,CLI 会按"默认 10,macOS 或仅装有 .NET 8 时回退到 8"的策略自动探测(见 crates/cli/src/common_args.rs 与 crates/cli/src/subcommands/init.rs)。


发布模块

配置完成后,照常发布即可:

spacetime publish my-database-name

CLI 会打印当前使用的构建路径,便于确认:

  • 输出"Using NativeAOT-LLVM compilation (experimental)"→ 正在使用 AOT 构建;
  • 输出标准信息 → 正在使用 JIT(Mono)构建。

发布时控制 .NET 版本

如需在发布时显式指定 .NET 版本:

# 强制 .NET 8 构建(AOT 必须配合 --native-aot) spacetime publish --dotnet-version 8 --native-aot my-database-name # 强制 .NET 10 构建(自动使用 AOT) spacetime publish --dotnet-version 10 my-database-name

故障排查

问题一:找不到 WASI SDK

报错

error : Could not find wasi-sdk. Either set $(WASI_SDK_PATH), or use workloads to get the sdk.

排查步骤

  1. WASI SDK 应在首次 AOT 构建时自动下载,确认~/.wasi-sdk(或%USERPROFILE%\.wasi-sdk)下是否有对应版本目录;
  2. 若自动下载失败,可从 wasi-sdk 官方 GitHub Releases 手动下载对应版本(.NET 8 对应 24、.NET 10 对应 29),解压到任意目录;
  3. 设置WASI_SDK_PATH环境变量指向该目录(该目录下需存在bin/clangbin/clang.exeSpacetimeDB.Runtime.targets会据此判定 SDK 是否有效);
  4. 重启终端 / IDE,使环境变量生效。

问题二:.NET 8 AOT 在 Linux 上失败

报错:缺少runtime.linux-x64.Microsoft.DotNet.ILCompiler.LLVM

原因:.NET 8 的 NativeAOT-LLVM 相关包只发布了 Windows 版本,Linux 上无法获取。

解决方案:改用 .NET 10 进行 Linux 下的 NativeAOT 构建:

spacetime init --lang csharp --dotnet-version 10 my-project

问题三:.NET 8 AOT 遇到 JsonSerializerContext 构建失败

报错:NativeAOT-LLVM 抛出含义不明的错误,例如:

EXEC : error : Object reference not set to an instance of an object. ...

原因:当模块定义了使用[JsonSourceGenerationOptions(PropertyNameCaseInsensitive = true)]JsonSerializerContext时,.NET 8 的 NativeAOT-LLVM 工具链可能失败。最新版 .NET 8 NativeAOT-LLVM 包停留在 2023 年 10 月,存在已知缺陷。

解决方案(按优先级):

  1. 优先迁移到 .NET 10NativeAOT-LLVM:

    spacetime init --lang csharp --dotnet-version 10 my-project
  2. 若必须停留在 .NET 8,改用默认的 JIT 构建路径(不要使用--native-aot);

  3. 作为兼容性 workaround,从JsonSourceGenerationOptions中移除PropertyNameCaseInsensitive = true,改为在调用点传入大小写不敏感选项:

    var result = JsonSerializer.Deserialize<T>( json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true } );

[!WARNING] 上述 workaround 可以编译并运行,但并非万无一失。在裁剪后的 AOT 构建中,该方案依赖反射,可能会产生IL2026警告。请将其视为"可能的兼容性方案"而非所有模块的保证修复。

问题四:JIT 构建报错——缺少 wasi-experimental workload

仅对 JIT 构建有效(NativeAOT 不适用),需要安装wasi-experimentalworkload:

dotnet workload install wasi-experimental

NativeAOT-LLVM 构建不使用该 workload,而是使用 WASI SDK。CLI 在 JIT 路径下会主动检查dotnet workload list输出中是否包含wasi-experimental,缺失时尝试自动安装,若因权限失败则给出提示(见 crates/cli/src/tasks/csharp.rs)。

问题五:Code generation failed

若出现 "Code generation failed for method" 类错误,按以下顺序检查:

  1. 确认NuGet.Config已包含dotnet-experimentalfeed;
  2. 对 .NET 8:确认.csproj中存在EXPERIMENTAL_WASM_AOT条件包引用,且构建时设置了EXPERIMENTAL_WASM_AOT=1
  3. 对 .NET 10:确认TargetFrameworknet10.0
  4. 若 .NET 10 不是默认 SDK,检查项目根目录是否存在内容正确的global.json

问题六:重复的 PackageReference 警告(NU1504)

.NET 8AOT 构建中出现 NU1504(重复包引用)警告属于预期行为,不阻塞构建,可忽略。


总结

NativeAOT-LLVM 为 SpacetimeDB C# 模块提供了将托管代码直接编译为原生 WASM 的路径,省去了 Mono 运行时解释开销。本文覆盖的完整决策链可归纳为:

  • 选 .NET 10(推荐):Windows 与 Linux 均支持,配置最简(无需条件包引用),NativeAOT 默认启用,且无 JsonSerializerContext 工具链缺陷;
  • 选 .NET 8:仅限 Windows,需要--native-aot显式激活、条件包引用与EXPERIMENTAL_WASM_AOT环境变量三件套配合;
  • 其余情况:macOS 用户、Linux + .NET 8 用户只能使用 JIT(Mono)路径,CLI 会在启用 AOT 时直接报错拦截。

更深入的实现细节可继续阅读仓库中的相关文件:CLI 构建任务与路径选择、平台校验与参数解析、publish 子命令的配置合并、init 子命令的参数定义,以及控制 MSBuild 行为的 SpacetimeDB.Runtime.targets。

【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询