11# C# Tooling
22
3- ## Building a Component with ` componentize-dotnet `
3+ WebAssembly components in C# can be built with [ componentize-dotnet] [ componentize-dotnet ] ,
4+ a a NuGet package that can be used to create a fully AOT-compiled
5+ component, giving .NET developers a component experience comparable to those in Rust and TinyGo.
6+
7+ [ componentize-dotnet ] : https://github.com/bytecodealliance/componentize-dotnet
48
5- [ componentize-dotnet] ( https://github.com/bytecodealliance/componentize-dotnet ) makes it easy to
6- compile your code to WebAssembly components using a single tool. This Bytecode Alliance project is a
7- NuGet package that can be used to create a fully AOT-compiled component, giving .NET developers a
8- component experience comparable to those in Rust and TinyGo.
9+ ## Building a Component with ` componentize-dotnet `
910
10- componentize-dotnet serves as a one-stop shop for .NET developers , wrapping several tools into one:
11+ [ ` componentize-dotnet ` ] [ componentize-dotnet ] serves as a one-stop shop, wrapping several tools into one:
1112
1213- [ NativeAOT-LLVM] ( https://github.com/dotnet/runtimelab/tree/feature/NativeAOT-LLVM ) (compilation)
1314- [ wit-bindgen] ( https://github.com/bytecodealliance/wit-bindgen ) (WIT imports and exports)
1415- [ wasm-tools] ( https://github.com/bytecodealliance/wasm-tools ) (component conversion)
1516- [ WASI SDK] ( https://github.com/WebAssembly/wasi-sdk ) (SDK used by NativeAOT-LLVM)
1617- [ Wac] ( https://github.com/bytecodealliance/wac ) (used to compose components)
1718
18- First, install the .NET SDK. For this walkthrough, we’ll use the [ .NET 10 SDK preview] ( https:// dotnet.microsoft.com/en-us/download/dotnet/10.0 ) .
19+ First, install the .NET SDK. For this walkthrough, we’ll use the [ .NET 10 SDK preview] [ dotnet-sdk ] .
1920You should also have [ wasmtime] ( https://wasmtime.dev/ ) installed so you can run the binary that you produce.
2021
22+ [ dotnet-sdk ] : https://dotnet.microsoft.com/en-us/download/dotnet/10.0
23+ [ wasmtime ] : https://wasmtime.dev/
24+
25+ ## 1. Create a new project
26+
2127Once you have the .NET SDK installed, create a new project:
2228
2329``` sh
@@ -26,10 +32,11 @@ dotnet new componentize.wasi.cli -o adder
2632cd adder
2733```
2834
29- Next, create or download the WIT world you would like to target. For this example we will use an
30- [ ` adder `
31- world] ( https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit )
32- with an ` add ` function:
35+ ## 2. Create or download your WIT world
36+
37+ Next, create or download the WIT world you would like to target.
38+
39+ For this example we will use the [ ` adder ` world] [ adder-world ] , with an ` add ` function (e.g. to ` wit/component.wit ` ):
3340
3441``` wit
3542package docs:adder@0.1.0;
@@ -47,32 +54,48 @@ In the `adder.csproj` project file, add a new `<ItemGroup>`:
4754
4855``` xml
4956<ItemGroup >
50- <Wit Update =" adder/world .wit" World =" example " />
57+ <Wit Update =" wit/component .wit" World =" adder " />
5158</ItemGroup >
5259```
5360
54- Since this component will only export a function dotnet considers this a library project. Let's update
55- the ` <OutputType> ` to be a library in the ` adder.csproj ` :
61+ Since this component will only export a function dotnet considers this a library project.
62+ Let's update the ` <OutputType> ` to be a library in the ` adder.csproj ` :
5663
57- ``` xml
58- <OutputType >Library</OutputType >
64+ ``` diff
65+ - <OutputType>Exe</OutputType>
66+ + <OutputType>Library</OutputType>
5967```
6068
61- And remove the ` Program.cs ` file:
69+ And remove the automatically generated ` Program.cs ` file:
6270
6371``` bash
6472rm Program.cs
6573```
6674
67- At this point, if you try to build the project with ` dotnet build ` , you'll get an error like "The name
68- 'ExampleWorldImpl' does not exist in the current context". This is because you've said you'll
69- provide an implementation, but haven't yet done so. To fix this, add the following code to your
70- project in a file called ` adder.cs ` :
75+ [ adder-world ] : https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit
76+
77+ ## 3. Write the implementation for the ` adder ` world
78+
79+ If you try to build the project with ` dotnet build ` , you'll get an error like the following:
80+
81+ ```
82+ ➜ dotnet build
83+ Restore complete (8.6s)
84+ You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy
85+ adder failed with 1 error(s) (25.6s)
86+ /path/to/adder/obj/Debug/net10.0/wasi-wasm/wit_bindgen/AdderWorld.wit.exports.docs.adder.v0_1_0.AddInterop.cs(15,19): error CS0103: The name 'AddImpl' does not exist in the current context
87+
88+ Build failed with 1 error(s) in 34.6s
89+ ```
90+
91+ This is because we've promised an implementation, but haven't yet written one for the ` adder ` world.
92+
93+ To fix this, add the following code to your in a file called ` Component.cs ` :
7194
7295``` csharp
73- namespace ExampleWorld ;
96+ namespace AdderWorld ;
7497
75- public class ExampleWorldImpl : IExampleWorld
98+ public class AdderWorldImpl : IAdderWorld
7699{
77100 public static uint Add (uint x , uint y )
78101 {
@@ -81,17 +104,58 @@ public class ExampleWorldImpl : IExampleWorld
81104}
82105```
83106
84- If we build it :
107+ Then, we can build our component :
85108
86109``` sh
87110dotnet build
88111```
89112
90113The component will be available at ` bin/Debug/net10.0/wasi-wasm/native/adder.wasm ` .
91114
115+ ### 5. (optional) the component from the example host
116+
117+ > [ !WARNING]
118+ > You must be careful to use a version of the adapter (` wasi_snapshot_preview1.wasm ` ) that is compatible with the version of
119+ > ` wasmtime ` that will be used, to ensure that WASI interface versions (and relevant implementation) match.
120+
121+ This repository contains an [ example WebAssembly host] [ example-host ] written in Rust that can run components that implement the ` adder ` world.
122+
123+ > [ !NOTE]
124+ > When hosts run components that use WASI interfaces, they must * explicitly* [ add WASI to the linker] [ add-to-linker ] to run the built component.
125+
126+ A successful run should show the following output:
127+
128+ ```
129+ cargo run --release -- 1 2 adder.component.wasm
130+ Compiling example-host v0.1.0 (/path/to/component-docs/component-model/examples/example-host)
131+ Finished `release` profile [optimized] target(s) in 7.85s
132+ Running `target/debug/example-host 1 2 /tmp/docs/c/adder.component.wasm`
133+ 1 + 2 = 3
134+ ```
135+
136+ If * not* configured correctly, you may see errors like the following:
137+
138+ ```
139+ cargo run --release -- 1 2 adder.component.wasm
140+ Compiling example-host v0.1.0 (/path/to/component-docs/component-model/examples/example-host)
141+ Finished `release` profile [optimized] target(s) in 7.85s
142+ Running `target/release/example-host 1 2 adder.component.wasm`
143+ Error: Failed to instantiate the example world
144+
145+ Caused by:
146+ 0: component imports instance `wasi:io/error@0.2.2`, but a matching implementation was not found in the linker
147+ 1: instance export `error` has the wrong type
148+ 2: resource implementation is missing
149+ ```
150+
151+ This kind of error normally indicates that the host in question does not contain satisfy WASI imports.
152+
153+ [ host ] : https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host
154+ [ add-to-linker ] : https://docs.wasmtime.dev/api/wasmtime_wasi/fn.add_to_linker_sync.html
155+
92156## Building a component that exports an interface
93157
94- The previous example uses a WIT file that exports a function. However, you'll often prefer to export an interface,
158+ The previous example uses a WIT file that exports a function. However, you'll often prefer to export an interface,
95159either to comply with an existing specification or to capture a set of functions and types that tend to go
96160together. Let's expand our ` example ` world to export an interface rather than directly
97161export the function. We are also adding the ` hostapp ` world to our WIT file which we will implement
@@ -116,8 +180,10 @@ world hostapp {
116180```
117181
118182If you peek at the bindings, you'll notice that we now implement a class for the ` add ` interface
119- rather than for the ` example ` world. This is a consistent pattern. As you export more interfaces
120- from your world, you implement more classes. Our ` adder.cs ` example gets the slight update of:
183+ rather than for the ` example ` world -- this is a consistent pattern. As you export more interfaces
184+ from your world, you implement more classes.
185+
186+ Our ` Component.cs ` example gets the slight update of:
121187
122188``` csharp
123189namespace ExampleWorld .wit .exports .example .component ;
@@ -152,7 +218,9 @@ our `adder` component and call the `add` function. We will later compose this co
152218the ` adder ` library component we just built.
153219
154220Now we will be taking the ` adder ` component and executing it from another WebAssembly component.
155- ` dotnet new componentize.wasi.cli ` creates a new project that creates an executable.
221+
222+ ` dotnet new componentize.wasi.cli ` creates a new project that creates an executable.
223+
156224Back out of the current project and create a new one:
157225
158226``` sh
@@ -211,7 +279,7 @@ Once again, compile your component with `dotnet build`:
211279$ dotnet build
212280Restore complete (0.4s)
213281You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy
214- host-app succeeded (1.1s) → bin/Debug/net9 .0/wasi-wasm/host-app.dll
282+ host-app succeeded (1.1s) → bin/Debug/net10 .0/wasi-wasm/host-app.dll
215283
216284Build succeeded in 2.5s
217285```
@@ -226,7 +294,10 @@ world, it needs to be composed the first component. You can compose your `host-a
226294your ` adder ` component by running [ ` wac plug ` ] ( https://github.com/bytecodealliance/wac ) :
227295
228296``` sh
229- wac plug bin/Debug/net10.0/wasi-wasm/native/host-app.wasm --plug ../adder/bin/Debug/net10.0/wasi-wasm/native/adder.wasm -o main.wasm
297+ wac plug \
298+ bin/Debug/net10.0/wasi-wasm/native/host-app.wasm \
299+ --plug ../adder/bin/Debug/net10.0/wasi-wasm/native/adder.wasm \
300+ -o main.wasm
230301```
231302
232303You can also automate the process by adding the following to your ` host-app.csproj ` :
@@ -237,7 +308,6 @@ You can also automate the process by adding the following to your `host-app.cspr
237308 <EntrypointComponent >bin/$(Configuration)/$(TargetFramework)/wasi-wasm/native/host-app.wasm</EntrypointComponent >
238309 <DependencyComponent >../adder/bin/$(Configuration)/$(TargetFramework)/wasi-wasm/native/adder.wasm</DependencyComponent >
239310 </PropertyGroup >
240-
241311 <MakeDir Directories =" dist" />
242312 <Exec Command =" $(WacExe) plug $(EntrypointComponent) --plug $(DependencyComponent) -o dist/main.wasm" />
243313</Target >
@@ -252,4 +322,6 @@ wasmtime run ./dist/main.wasm
2523221 + 2 = 3
253323```
254324
255- Checkout the [ componentize-dotnet docs] ( https://github.com/bytecodealliance/componentize-dotnet ) for more configurations options.
325+ Check out the [ componentize-dotnet docs] [ componentize-dotnet-docs ] for more configurations options.
326+
327+ [ componentize-dotnet-docs ] : https://github.com/bytecodealliance/componentize-dotnet
0 commit comments