├── .gitignore
├── ConsoleApp
├── Program.cs
└── ConsoleApp.csproj
├── StandaloneLibrary
├── Calculator.cs
└── StandaloneLibrary.csproj
├── nuget.config
├── MinimalDotNetWasmNativeAOT.sln
├── README.md
└── Directory.Build.targets
/.gitignore:
--------------------------------------------------------------------------------
1 | .vs/
2 | bin/
3 | obj/
4 |
--------------------------------------------------------------------------------
/ConsoleApp/Program.cs:
--------------------------------------------------------------------------------
1 | // See https://aka.ms/new-console-template for more information
2 | Console.WriteLine("Hello, World!");
3 |
--------------------------------------------------------------------------------
/StandaloneLibrary/Calculator.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 |
3 | namespace MyApp;
4 |
5 | public static class Calculator
6 | {
7 | [UnmanagedCallersOnly(EntryPoint = "add")]
8 | public static int Add(int a, int b) => a + b;
9 |
10 | [UnmanagedCallersOnly(EntryPoint = "subtract")]
11 | public static int Subtract(int a, int b) => a - b;
12 | }
13 |
--------------------------------------------------------------------------------
/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ConsoleApp/ConsoleApp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | exe
5 | net8.0
6 | enable
7 | enable
8 | true
9 | true
10 | false
11 | true
12 | wasi-wasm
13 | false
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/StandaloneLibrary/StandaloneLibrary.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Library
5 | net8.0
6 | enable
7 | enable
8 | true
9 | true
10 | false
11 | true
12 | wasi-wasm
13 | false
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/MinimalDotNetWasmNativeAOT.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.5.002.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp", "ConsoleApp\ConsoleApp.csproj", "{5A9121E0-8590-4D2F-98FB-A2DBB2B7260A}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StandaloneLibrary", "StandaloneLibrary\StandaloneLibrary.csproj", "{441CB253-0F77-44F2-81F4-C24864CCE521}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {5A9121E0-8590-4D2F-98FB-A2DBB2B7260A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {5A9121E0-8590-4D2F-98FB-A2DBB2B7260A}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {5A9121E0-8590-4D2F-98FB-A2DBB2B7260A}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {5A9121E0-8590-4D2F-98FB-A2DBB2B7260A}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {441CB253-0F77-44F2-81F4-C24864CCE521}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {441CB253-0F77-44F2-81F4-C24864CCE521}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {441CB253-0F77-44F2-81F4-C24864CCE521}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {441CB253-0F77-44F2-81F4-C24864CCE521}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {BA44930B-C0E3-41DC-B0F6-3CCFE423B3DC}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Minimal .NET -> WebAssembly via NativeAOT-LLVM
2 |
3 | This repo contains two examples.
4 |
5 | They are minimal in the sense of doing the smallest amount of stuff in the source code. However they are **not** minimal in terms of size - various things could be done to reduce the output size dramatically, such as stripping debug info or running `wasm-opt`.
6 |
7 | ## Prerequisites
8 |
9 | * .NET 8 SDK
10 | * To run the output on the command line, get [wasmtime](https://wasmtime.dev/) and put it on your PATH
11 |
12 | ## ConsoleApp
13 |
14 | The default "Hello World" app
15 |
16 | To build:
17 |
18 | * `cd ConsoleApp`
19 | * `dotnet build` (note: takes a few mins on the first run to acquire the SDKs, but is fast after)
20 | * You now have `bin/Debug/net8.0/wasi-wasm/publish/ConsoleApp.wasm`
21 |
22 | To run:
23 |
24 | * `wasmtime bin/Debug/net8.0/wasi-wasm/publish/ConsoleApp.wasm`
25 |
26 | ## StandaloneLibrary
27 |
28 | This produces a wasm module with an export called `add` you can invoke from some host environment.
29 |
30 | To build:
31 |
32 | * `cd StandaloneLibrary`
33 | * `dotnet build`
34 | * You now have `bin/Debug/net8.0/wasi-wasm/publish/StandaloneLibrary.wasm`
35 |
36 | Note that this is a "reactor" module, i.e., one with imports and exports but no entrypoint. So to run code from it, you have to call a named export and pass whatever parameters it wants, e.g., to run on the CLI:
37 |
38 | * `wasmtime bin/Debug/net8.0/wasi-wasm/publish/StandaloneLibrary.wasm --invoke add 123 456`
39 | * `wasmtime bin/Debug/net8.0/wasi-wasm/publish/StandaloneLibrary.wasm --invoke subtract 123 456`
40 |
41 | ... but more commonly, reactor modules are consumed by some hosting environment such as a web server that makes calls to your module's exports.
42 |
--------------------------------------------------------------------------------
/Directory.Build.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 | 20.0
11 | https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/wasi-sdk-20.0.m-mingw.tar.gz
12 | https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/wasi-sdk-20.0-linux.tar.gz
13 | https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/wasi-sdk-20.0-macos.tar.gz
14 | $([System.IO.Path]::Combine("$([System.Environment]::GetFolderPath(SpecialFolder.UserProfile))", ".wasi-sdk", "wasi-sdk-$(WasiSdkVersion)"))
15 |
16 | 3.1.23
17 | https://github.com/emscripten-core/emsdk/archive/refs/tags/$(EmSdkVersion).zip
18 | $([System.IO.Path]::Combine("$([System.Environment]::GetFolderPath(SpecialFolder.UserProfile))", ".emsdk", "emsdk-$(EmSdkVersion)"))
19 |
20 |
21 | $(CopyWasmToOutputDependsOn);
22 | ObtainWasiSdk;
23 | PrepareBuildWasmInputs;
24 | BuildWasm;
25 |
26 |
27 |
28 |
29 |
30 | $(EmscriptenRoot)
31 | $(WasiSdkRoot)
32 |
33 |
34 |
35 |
36 |
37 | $(IntermediateOutputPath)\emsdk-temp
38 |
39 |
40 |
41 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | $(IntermediateOutputPath)\wasi-sdk-temp
60 |
61 |
62 |
63 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------