Skip to content
Draft
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
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
4 changes: 3 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,7 @@
<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)' == ''" />
</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