|
| 1 | +# Dart Tooling |
| 2 | + |
| 3 | +> [!WARNING] |
| 4 | +> Compiling Dart to non-web WebAssembly targets is experimental. |
| 5 | +> This guide requires Dart version `3.14.0-251.0.dev` or later. |
| 6 | +
|
| 7 | +WebAssembly components in Dart can be built with the [wasm_tools package](https://github.com/simolus3/wasm.dart/) on pub.dev. |
| 8 | + |
| 9 | +This guide walks through building a Dart component that implements |
| 10 | +the `adder` world defined in the [`adder/world.wit` package][docs-adder]. |
| 11 | +The component will implement the `adder` world, which contains an `add` interface with an `add` function. |
| 12 | + |
| 13 | +Keep in mind that this is a basic intro. For more examples, please see the [dart-samples][examples] |
| 14 | +from the `wasm_tools` package or TODO: running a component. |
| 15 | + |
| 16 | +If you still have questions, feel free to open an issue on [the repository][wasm_tools] |
| 17 | +or reach out on [Zulip][chat]. |
| 18 | + |
| 19 | +## 1. Create your Dart project |
| 20 | + |
| 21 | +Begin by creating a fresh Dart project: |
| 22 | + |
| 23 | +```sh |
| 24 | +dart create -t cli dart_wasm_adder |
| 25 | +cd dart_wasm_adder |
| 26 | +``` |
| 27 | + |
| 28 | +## 2. Install the tools |
| 29 | + |
| 30 | +All tools requires to create WebAssembly components from Dart can be installed via pub: |
| 31 | + |
| 32 | +```sh |
| 33 | +dart pub add wasm_components dev:wasm_tools |
| 34 | +``` |
| 35 | + |
| 36 | +The `wasm_components` package provides runtime support for component models |
| 37 | +(like an allocator or async task management), while `wasm_tools` contains `witgen` |
| 38 | +and tools to compile Dart to components. |
| 39 | + |
| 40 | +## 3. Generate bindings for the Wasm component |
| 41 | + |
| 42 | +Since we will be implementing the [`adder` world][docs-adder], we can copy the WIT to our project. |
| 43 | +Create a file named `adder.wit` and paste the following code into it: |
| 44 | + |
| 45 | +```wit |
| 46 | +{{#include ../../../examples/tutorial/wit/adder/world.wit}} |
| 47 | +``` |
| 48 | + |
| 49 | +Generate Dart code and required metadata for the compiler with |
| 50 | + |
| 51 | +```sh |
| 52 | +dart run wasm_tools witgen -i adder.wit |
| 53 | +``` |
| 54 | + |
| 55 | +This generates: |
| 56 | + |
| 57 | +- `lib/src/components/docs_adder.dart` containing interfaces (only `add` in this example). |
| 58 | +- `lib/src/components/docs_adder_adder.dart` containing bindings for the `adder` world. |
| 59 | +- `lib/src/components/docs_adder_adder.json` containing metadata used to turn WebAssembly |
| 60 | + modules emitted by `dart2wasm` into components. |
| 61 | + |
| 62 | +The JSON file describe which component imports and exports a Dart program needs. |
| 63 | +The toolchain reads it via [link hooks]. All packages defining component imports |
| 64 | +or exports need one, including the `dart_wasm_adder` package. Create a |
| 65 | +`hook/link.dart` file with these contents: |
| 66 | + |
| 67 | +```dart |
| 68 | +import 'dart:convert'; |
| 69 | +import 'dart:io'; |
| 70 | +
|
| 71 | +import 'package:hooks/hooks.dart'; |
| 72 | +import 'package:wasm_tools/hooks.dart'; |
| 73 | +
|
| 74 | +void main(List<String> args) => link(args, (input, output) async { |
| 75 | + if (input.config.buildWasmComponent) { |
| 76 | + final abi = input.packageRoot.resolve('lib/src/components/docs_adder_adder.json'); |
| 77 | +
|
| 78 | + output.dependencies.add(abi); |
| 79 | + output.assets.webAssemblyComponents.add( |
| 80 | + WasmComponentAsset( |
| 81 | + encoded: json.decode( |
| 82 | + File(abi.toFilePath()).readAsStringSync(), |
| 83 | + ) as Map<String, Object?>, |
| 84 | + ), |
| 85 | + ); |
| 86 | + } |
| 87 | +}); |
| 88 | +``` |
| 89 | + |
| 90 | +## 4. Implement the `add` function |
| 91 | + |
| 92 | +The generated `adderComponent` function is used to define this component: |
| 93 | +It receives imported interfaces as parameters (in this case, there aren't any) |
| 94 | +and returns the exported interface. |
| 95 | + |
| 96 | +For this example, replace `bin/dart_wasm_adder.dart` with: |
| 97 | + |
| 98 | +```dart |
| 99 | +import 'package:dart_wasm_adder/src/components/docs_adder.dart'; |
| 100 | +import 'package:dart_wasm_adder/src/components/docs_adder_adder.dart'; |
| 101 | +
|
| 102 | +import 'package:wasm_components/wasm_components.dart'; |
| 103 | +
|
| 104 | +void main() { |
| 105 | + adderComponent((_) => const _Add()); |
| 106 | +} |
| 107 | +
|
| 108 | +final class const _Add() implements Add { |
| 109 | + @override |
| 110 | + int add({required int x, required int y}) { |
| 111 | + return x + y; |
| 112 | + } |
| 113 | +} |
| 114 | +``` |
| 115 | + |
| 116 | +## 5. Testing the `add` component |
| 117 | + |
| 118 | +With all generated bindings set up, the component can be compiled: |
| 119 | + |
| 120 | +```sh |
| 121 | +dart run wasm_tools compile bin/dart_wasm_adder.dart |
| 122 | +``` |
| 123 | + |
| 124 | +This creates `bin/dart_wasm_adder.wasm`, a component we can invoke from the |
| 125 | +CLI with [wasmtime]: |
| 126 | + |
| 127 | +```console |
| 128 | +$ wasmtime run --invoke 'add(1, 2)' bin/dart_wasm_adder.wasm |
| 129 | +3 |
| 130 | +``` |
| 131 | + |
| 132 | +With this, we have successfully built and run a basic WebAssembly component with Dart 🎉 |
| 133 | + |
| 134 | +[wasm_tools]: https://github.com/simolus3/wasm.dart/ |
| 135 | +[examples]: https://github.com/simolus3/wasm.dart/tree/main/pkg/wasm_tools/example |
| 136 | +[docs-adder]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit |
| 137 | +[chat]: https://bytecodealliance.zulipchat.com/#narrow/channel/394175-SIG-Guest-Languages/topic/Dart.20subgroup/with/614175797 |
| 138 | +[link hooks]: https://dart.dev/tools/hooks |
| 139 | +[wasmtime]: https://wasmtime.dev/ |
0 commit comments