Skip to content

Commit bf164cd

Browse files
refactor(lang/csharp): update csharp docs
Signed-off-by: Victor Adossi <vadossi@cosmonic.com>
1 parent cdb4a51 commit bf164cd

1 file changed

Lines changed: 104 additions & 32 deletions

File tree

  • component-model/src/language-support

component-model/src/language-support/csharp.md

Lines changed: 104 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,29 @@
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].
1920
You 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+
2127
Once you have the .NET SDK installed, create a new project:
2228

2329
```sh
@@ -26,10 +32,11 @@ dotnet new componentize.wasi.cli -o adder
2632
cd 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
3542
package 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
6472
rm 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
87110
dotnet build
88111
```
89112

90113
The 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,
95159
either to comply with an existing specification or to capture a set of functions and types that tend to go
96160
together. Let's expand our `example` world to export an interface rather than directly
97161
export 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

118182
If 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
123189
namespace ExampleWorld.wit.exports.example.component;
@@ -152,7 +218,9 @@ our `adder` component and call the `add` function. We will later compose this co
152218
the `adder` library component we just built.
153219

154220
Now 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+
156224
Back 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
212280
Restore complete (0.4s)
213281
You 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

216284
Build succeeded in 2.5s
217285
```
@@ -226,7 +294,10 @@ world, it needs to be composed the first component. You can compose your `host-a
226294
your `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

232303
You 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
252322
1 + 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

Comments
 (0)