Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
a8dac84
Forcing LLVM packages to always be available as it seems dotnet resto…
JasonAtClockwork Mar 5, 2026
adbcada
Updating nuget config writer script with `NativeAOT-LLM` support
rekhoff Mar 6, 2026
d5ef9fb
Created conditional for `EXPERIMENTAL_WASM_AOT` specific packages in …
rekhoff Mar 10, 2026
6f5df9b
Create NATIVEAOT-LLVM instructions for C# modules
rekhoff Mar 11, 2026
da2de3e
Add AOT path for sdk-test-cs to allow the new CI test to work
JasonAtClockwork Mar 11, 2026
839e8b9
Merge branch 'jlarabie/update-nativeaot-llvm-infrastructure' of https…
JasonAtClockwork Mar 11, 2026
e022173
Attempting to pin versions for CI test
JasonAtClockwork Mar 11, 2026
a0f68a6
Runtime on Linux falling back to 9.* oddly trying to clear it up
JasonAtClockwork Mar 11, 2026
4734312
Minimal requirement for sdk-test-cs.csproj
JasonAtClockwork Mar 11, 2026
87866c8
Merge branch 'tyler/update-nativeaot-llvm-infrastructure' into jlarab…
rekhoff Mar 12, 2026
52f726b
Update `sdk-test-cs` project file with NativeAOT-LLVM packages
rekhoff Mar 12, 2026
b37522f
Update `sdk-test-cs` project file to have `ILCompiler.LLMV` `Generate…
rekhoff Mar 12, 2026
9c0a27a
Update `modules/sdk-test-cs` CI job to run on Windows
rekhoff Mar 12, 2026
90ff17c
Add Emscripten install step to `modules/sdk-test-cs` CI job
rekhoff Mar 12, 2026
107e6ed
Updated Emscripten to NativeAOT-LLVM 8.0.0 compatible version
rekhoff Mar 12, 2026
69a0996
Using `wasi-sdk`'s clang for WASI targets
rekhoff Mar 12, 2026
73d81b6
Updating to newer version of Emscripten
rekhoff Mar 13, 2026
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
30 changes: 27 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -869,11 +869,35 @@ jobs:
exit 1
}

# NativeAOT-LLVM smoketest runs on Windows because runtime.linux-x64.Microsoft.DotNet.ILCompiler.LLVM
# does not have 8.0.0 versions available on the dotnet-experimental NuGet feed.
csharp-aot-smoketest:
needs: [lints]
runs-on: windows-latest
timeout-minutes: 15
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup dotnet
uses: actions/setup-dotnet@v3
with:
global-json-file: global.json

- name: Setup Emscripten
uses: mymindstorm/setup-emsdk@v14
with:
version: 4.0.23

- name: Smoketest C# AOT build (NativeAOT-LLVM)
shell: pwsh
run: |
cd modules/sdk-test-cs
EXPERIMENTAL_WASM_AOT=1 dotnet publish -c Release
test -f bin/Release/net8.0/wasi-wasm/publish/StdbModule.wasm
$env:EXPERIMENTAL_WASM_AOT = "1"
dotnet publish -c Release modules/sdk-test-cs
if (-not (Test-Path "modules/sdk-test-cs/bin/Release/net8.0/wasi-wasm/publish/StdbModule.wasm")) {
Write-Error "StdbModule.wasm not found"
exit 1
}

internal-tests:
name: Internal Tests
Expand Down
118 changes: 118 additions & 0 deletions crates/bindings-csharp/NATIVEAOT-LLVM.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# Converting a SpacetimeDB 2.0.x project to use NativeAOT-LLVM

This guide provides instructions on taking an existing C# module that targets the public-released SpacetimeDB CLI, and guides you through the necessary steps to enable `NativeAOT-LLVM` use.

## Overview
In order to use `NativeAOT-LLVM` on a C# module, we'll need to set the `EXPERIMENTAL_WASM_AOT` environment variable to `1` which SpacetimeDB will check during publishing of a module.
For the module to work, we'll also need the `NuGet.Config` and `.csproj` files with the required package sources and references.

### Prerequisites:
- **.NET SDK 8.x** (same version used by SpacetimeDB)
- **Emscripten SDK (EMSDK)** installed (must contain `upstream/emscripten/emcc.bat`)
- **(Optional) Binaryen (wasm-opt)** installed and on `PATH` (recommended: `version_116`)

## Steps

1. **Install EMSDK**
- Download and extract the `https://github.com/emscripten-core/emsdk` release.
- Example path: `D:\Tools\emsdk`

2. **Set environment variables**

```powershell
$env:EXPERIMENTAL_WASM_AOT=1
$env:EMSDK="D:\Tools\emsdk"
```

3. **Ensure NuGet feed is configured**
NativeAOT-LLVM packages currently come from **dotnet-experimental**:
- Add the `dotnet-experimental` feed to a project-local `NuGet.Config`
```xml
<add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" />
```
This should be a `NuGet.Config` placed in the root directory of your module folder (next to the `.csproj`). You can simply add the above line to the `packageSources` of your existing file, or if you need to create a minimal one, you can use:

```xml
<?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>
</configuration>
```

4. **Ensure NativeAOT emits a `.wasm` output**
- For LLVM AOT builds, the CLI currently accepts `dotnet.wasm` under `bin/Release/net8.0/wasi-wasm/publish/`.
- In the module `.csproj`, ensure the AOT package references include:

```xml
<ItemGroup Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1'">
<PackageReference Include="Microsoft.NET.ILLink.Tasks" Version="8.0.0-*" Condition="'$(ILLinkTargetsPath)' == ''" />
<PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
<PackageReference Include="runtime.$(NETCoreSdkPortableRuntimeIdentifier).Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
</ItemGroup>
```

The contents of your `.csproj` should look something like this:

```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>wasi-wasm</RuntimeIdentifier>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SpacetimeDB.Runtime" Version="2.0.*" />
</ItemGroup>
<ItemGroup Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1'">
<PackageReference Include="Microsoft.NET.ILLink.Tasks" Version="8.0.0-*" Condition="'$(ILLinkTargetsPath)' == ''" />
<PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
<PackageReference Include="runtime.$(NETCoreSdkPortableRuntimeIdentifier).Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
</ItemGroup>
</Project>
```

- **NU1504 warning**: Because the runtime targets also add these LLVM packages, you may see a duplicate PackageReference warning. It is non-blocking.

5. **(Optional) Install wasm-opt (Binaryen)**
This step is optional, but provides performance improvements, and therefore is recommended.
- Download Binaryen `https://github.com/WebAssembly/binaryen/releases/tag/version_116` for Windows and extract it, e.g. `D:\Tools\binaryen`.
- Add `D:\Tools\binaryen\bin` to `PATH`.
- Verify:

```powershell
wasm-opt --version
```

6. **Publish module**
- Use the SpacetimeDB CLI to publish from the module directory.
- With `EXPERIMENTAL_WASM_AOT=1`, publish should attempt LLVM AOT.
- Ensure the local server is running if publishing to `local`.

## Troubleshooting

### Package source mapping enabled
If you have **package source mapping** enabled in `NuGet.Config`, you must add mappings for the LLVM packages or restores will fail.

```xml
<packageSourceMapping>
<packageSource key="dotnet-experimental">
<package pattern="Microsoft.DotNet.ILCompiler.LLVM" />
<package pattern="runtime.*.Microsoft.DotNet.ILCompiler.LLVM" />
</packageSource>
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
```

### wasi-experimental workload install fails
If the CLI cannot install the `wasi-experimental` workload automatically, install it manually:

```powershell
dotnet workload install wasi-experimental
```
8 changes: 8 additions & 0 deletions crates/bindings-csharp/Runtime/Runtime.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RootNamespace>SpacetimeDB</RootNamespace>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<RestoreAdditionalProjectSources Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1'">https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json;$(RestoreAdditionalProjectSources)</RestoreAdditionalProjectSources>
</PropertyGroup>

<ItemGroup>
Expand All @@ -25,6 +26,13 @@
<ProjectReference Include="../Codegen/Codegen.csproj" ReferenceOutputAssembly="false" />
</ItemGroup>

<!-- These must be explicit package dependencies so NuGet consumers can resolve the LLVM toolchain. -->
<ItemGroup Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1'">
<PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" IncludeAssets="All" />
<PackageReference Include="runtime.$(NETCoreSdkPortableRuntimeIdentifier).Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" IncludeAssets="All" />
<PackageReference Include="Microsoft.NET.ILLink.Tasks" Version="8.0.0-*" Condition="'$(ILLinkTargetsPath)' == ''" IncludeAssets="All" />
</ItemGroup>

<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="" />
<None Include="build/*" Pack="true" PackagePath="build" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<Import
Project="$(PkgMicrosoft_DotNet_ILCompiler_LLVM)\build\Microsoft.DotNet.ILCompiler.LLVM.targets"
Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1' and '$(ILCompilerTargetsPath)' == '' and '$(PkgMicrosoft_DotNet_ILCompiler_LLVM)' != '' and Exists('$(PkgMicrosoft_DotNet_ILCompiler_LLVM)\build\Microsoft.DotNet.ILCompiler.LLVM.targets')" />

<ItemGroup Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1'">
<NativeLibrary Include="$(MSBuildThisFileDirectory)..\bindings.c" />
<UnmanagedEntryPointsAssembly Include="SpacetimeDB.Runtime" />
Expand Down Expand Up @@ -42,10 +46,6 @@
<WasmImport Include="spacetime_10.4!datastore_index_scan_point_bsatn" />
<WasmImport Include="spacetime_10.4!datastore_delete_by_index_scan_point_bsatn" />

<PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
<PackageReference Include="runtime.$(NETCoreSdkPortableRuntimeIdentifier).Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
<PackageReference Include="Microsoft.NET.ILLink.Tasks" Version="8.0.0-*" Condition="'$(ILLinkTargetsPath)' == ''" />

<CustomLinkerArg Include="-DEXPERIMENTAL_WASM_AOT" />
</ItemGroup>

Expand Down
6 changes: 5 additions & 1 deletion modules/sdk-test-cs/sdk-test-cs.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,9 @@
<ProjectReference Include="../../crates/bindings-csharp/Codegen/Codegen.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="../../crates/bindings-csharp/Runtime/Runtime.csproj" />
</ItemGroup>

<ItemGroup Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1'">
<PackageReference Include="Microsoft.NET.ILLink.Tasks" Version="8.0.0-*" Condition="'$(ILLinkTargetsPath)' == ''" />
<PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" GeneratePathProperty="true" />
<PackageReference Include="runtime.$(NETCoreSdkPortableRuntimeIdentifier).Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
</ItemGroup>
</Project>
14 changes: 14 additions & 0 deletions sdks/csharp/tools~/write-nuget-config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ cat >NuGet.Config <<EOF
<configuration>
<packageSources>
<clear />
<!-- Experimental NuGet feed for Microsoft.DotNet.ILCompiler.LLVM packages -->
<add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" />
<!-- Local NuGet repositories -->
<add key="Local SpacetimeDB.BSATN.Runtime" value="${SPACETIMEDB_REPO_PATH}/crates/bindings-csharp/BSATN.Runtime/bin/Release" />
<!-- We need to override the module runtime as well because the examples use it -->
Expand All @@ -30,6 +32,11 @@ cat >NuGet.Config <<EOF
<packageSource key="Local SpacetimeDB.Runtime">
<package pattern="SpacetimeDB.Runtime" />
</packageSource>
<!-- Experimental packages for NativeAOT-LLVM compilation -->
<packageSource key="dotnet-experimental">
<package pattern="Microsoft.DotNet.ILCompiler.LLVM" />
<package pattern="runtime.*" />
</packageSource>
<!-- Fallback for other packages (e.g. test deps). -->
<packageSource key="nuget.org">
<package pattern="*" />
Expand All @@ -43,6 +50,8 @@ cat >"${SPACETIMEDB_REPO_PATH}/NuGet.Config" <<EOF
<configuration>
<packageSources>
<clear />
<!-- Experimental NuGet feed for Microsoft.DotNet.ILCompiler.LLVM packages -->
<add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" />
<!-- Local NuGet repositories -->
<add key="Local SpacetimeDB.BSATN.Runtime" value="crates/bindings-csharp/BSATN.Runtime/bin/Release" />
<!-- We need to override the module runtime as well because the examples use it -->
Expand All @@ -58,6 +67,11 @@ cat >"${SPACETIMEDB_REPO_PATH}/NuGet.Config" <<EOF
<packageSource key="Local SpacetimeDB.Runtime">
<package pattern="SpacetimeDB.Runtime" />
</packageSource>
<!-- Experimental packages for NativeAOT-LLVM compilation -->
<packageSource key="dotnet-experimental">
<package pattern="Microsoft.DotNet.ILCompiler.LLVM" />
<package pattern="runtime.*" />
</packageSource>
<!-- Fallback for other packages (e.g. test deps). -->
<packageSource key="nuget.org">
<package pattern="*" />
Expand Down
Loading