rikhuijzer opened issue #9996:
There is a nice "hello world" example in the docs:
(module ;; Import the required fd_write WASI function which will write the given io vectors to stdout ;; The function signature for fd_write is: ;; (File Descriptor, *iovs, iovs_len, *nwritten) -> Returns 0 on success, nonzero on error (import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32))) (memory 1) (export "memory" (memory 0)) ;; Write 'hello world\n' to memory at an offset of 8 bytes ;; Note the trailing newline which is required for the text to appear (data (i32.const 8) "hello world\n") (func $main (export "_start") ;; Creating a new io vector within linear memory (i32.store (i32.const 0) (i32.const 8)) ;; iov.iov_base - This is a pointer to the start of the 'hello world\n' string (i32.store (i32.const 4) (i32.const 12)) ;; iov.iov_len - The length of the 'hello world\n' string (call $fd_write (i32.const 1) ;; file_descriptor - 1 for stdout (i32.const 0) ;; *iovs - The pointer to the iov array, which is stored at memory location 0 (i32.const 1) ;; iovs_len - We're printing 1 string stored in an iov - so one. (i32.const 20) ;; nwritten - A place in memory to store the number of bytes written ) drop ;; Discard the number of bytes written from the top of the stack ) )
I've just tested this with wasmtime 28.0.0 and it works fine (prints "hello world" to stdout).
However, this example is using the "wasi_snapshot_preview1" API while preview 2 is currently stable. I think this example can probably be rewritten using output-stream write from streams.wit, but I'm a bit at a loss where to find code related to this.
Could anyone give some pointers on how to print via preview 2?
alexcrichton commented on issue #9996:
I think you probably want this:
(module (import "wasi:cli/stdout@0.2.3" "get-stdout" (func $get-stdout (result i32))) (import "wasi:io/streams@0.2.3" "[method]output-stream.blocking-write-and-flush" (func $blocking-write-and-flush (param i32 i32 i32 i32))) (memory (export "memory") 17) (func (export "wasi:cli/run@0.2.3#run") (result i32) (local i32 i32 i32) call $get-stdout i32.const 100 i32.const 15 local.get 0 call $blocking-write-and-flush i32.const 0 ) (data (i32.const 100) "Hello, world!\n") )
Execute this with:
$ wasm-tools component embed ./wasi.wit foo.wat | wasm-tools component new -o component.wasm $ wasmtime component.wasm
This is more component-model level documentation rather than Wasmtime documentation which is why it's not currently in this repository (it'd be a good addition to https://component-model.bytecodealliance.org/ probably!)
alexcrichton edited a comment on issue #9996:
I think you probably want this:
(module (import "wasi:cli/stdout@0.2.3" "get-stdout" (func $get-stdout (result i32))) (import "wasi:io/streams@0.2.3" "[method]output-stream.blocking-write-and-flush" (func $blocking-write-and-flush (param i32 i32 i32 i32))) (memory (export "memory") 1) (func (export "wasi:cli/run@0.2.3#run") (result i32) (local i32 i32 i32) call $get-stdout i32.const 100 i32.const 15 local.get 0 call $blocking-write-and-flush i32.const 0 ) (data (i32.const 100) "Hello, world!\n") )
Execute this with:
$ wasm-tools component embed ./wasi.wit foo.wat | wasm-tools component new -o component.wasm $ wasmtime component.wasm
This is more component-model level documentation rather than Wasmtime documentation which is why it's not currently in this repository (it'd be a good addition to https://component-model.bytecodealliance.org/ probably!)
alexcrichton edited a comment on issue #9996:
I think you probably want this:
(module (import "wasi:cli/stdout@0.2.3" "get-stdout" (func $get-stdout (result i32))) (import "wasi:io/streams@0.2.3" "[method]output-stream.blocking-write-and-flush" (func $blocking-write-and-flush (param i32 i32 i32 i32))) (memory (export "memory") 1) (func (export "wasi:cli/run@0.2.3#run") (result i32) call $get-stdout i32.const 100 i32.const 15 i32.const 96 call $blocking-write-and-flush i32.const 0 ) (data (i32.const 100) "Hello, world!\n") )
Execute this with:
$ wasm-tools component embed ./wasi.wit foo.wat | wasm-tools component new -o component.wasm $ wasmtime component.wasm
This is more component-model level documentation rather than Wasmtime documentation which is why it's not currently in this repository (it'd be a good addition to https://component-model.bytecodealliance.org/ probably!)
rikhuijzer commented on issue #9996:
Thank you Alex for the help. I've spent now an hour going through the various projects in order to figure out what should be inside "wasi.wit". I've looked at wit-deps and tried hello-wasi-http, but get errors like even when I have included all the wit files from hello-wasi-http.
error: package not found --> ./world.wit:4:11 | 4 | include wasi:http/proxy@0.2.0; | ^--------
Anyway no need to debug my attempts. How would you go about creating
wasi.wit
?(it'd be a good addition to https://component-model.bytecodealliance.org/ probably!)
I'll happily add it once I've got something working :+1:
alexcrichton commented on issue #9996:
There's a number of ways to get the WIT right now but you're right in that most aren't well documented and there's not necessarily a canonical way of doing so AFAIK for this sort of CLI use case. One way though is to:
- Clone https://github.com/WebAssembly/WASI
- Checkout the tag you want (e.g. 0.2.2)
- Use the
wasip2
directory as thewasi.wit
input (e.g. pass in a directory, not a WIT file).This might also require a
--world
argument to specifywasi:cli/run
for example
rikhuijzer commented on issue #9996:
- Clone https://github.com/WebAssembly/WASI
- Checkout the tag you want (e.g. 0.2.2)
- Use the wasip2 directory as the wasi.wit input (e.g. pass in a directory, not a WIT file).
Is the following indeed the direction I should take?
hello.wat
here is the code you wrote with the0.2.3
replaced by0.2.2
:$ wasm-tools component embed WASI/wasip2/io hello.wat # this at least doesn't crash error: cannot print binary wasm output to a terminal, pass the `-t` flag to print the text format $ wasm-tools component embed WASI/wasip2/io hello.wat | wasm-tools component new -o component.wasm error: failed to encode a component from module Caused by: 0: failed to decode world from module 1: module was not valid 2: failed to resolve import `wasi:cli/stdout@0.2.2::get-stdout` 3: module requires an import interface named `wasi:cli/stdout@0.2.2`
So I should point
embed
toWASI/wasip2
becauseio
doesn't includecli
:$ wasm-tools component embed WASI/wasip2/ hello.wat error: failed to resolve directory while parsing WIT for path [WASI/wasip2/] Caused by: 0: failed to parse package: WASI/wasip2/ 1: no `package` header was found in any WIT file for this package error: expected at least one module field --> <stdin>:1:1 | 1 | | ^ $ wasm-tools --version wasm-tools 1.223.0
Now I wonder whether the thing I'm asking for in this issue is actually an aim of wasmtime? Is getting this to work an aim of the wasmtime project or am I trying to do the wrong thing here? If yes? How what problems should I use wasmtime for?
alexcrichton commented on issue #9996:
I cobbled together
wasi.wit
manually myself, and it's not easily present in the upstream repos unfortunately. The easiest solution might be to ignore my advice and use https://github.com/WebAssembly/wasi-cli/ instead with itswit
directory.As for the moer general question: while showing things in
*.wat
is "neat" and informational, it's not really how anyone actually develops. It's very useful for understanding things and how the pieces all fit together but the experience isn't particularly smooth because no other tooling works the same way.For example writing a "hello, world!" in all languages typically doesn't even involve using WIT at all. The WASI APIs are bound in either third-party libraries (like the
wasi
crate in Rust) or language standard libraries (e.g. wasi-libc using preview2). In that sense the story for implementing things for developers is different than that of understanding the fundamentals of what's in play here (or at least that's my own read on the mismatch here)
rikhuijzer closed issue #9996:
There is a nice "hello world" example in the docs:
(module ;; Import the required fd_write WASI function which will write the given io vectors to stdout ;; The function signature for fd_write is: ;; (File Descriptor, *iovs, iovs_len, *nwritten) -> Returns 0 on success, nonzero on error (import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32))) (memory 1) (export "memory" (memory 0)) ;; Write 'hello world\n' to memory at an offset of 8 bytes ;; Note the trailing newline which is required for the text to appear (data (i32.const 8) "hello world\n") (func $main (export "_start") ;; Creating a new io vector within linear memory (i32.store (i32.const 0) (i32.const 8)) ;; iov.iov_base - This is a pointer to the start of the 'hello world\n' string (i32.store (i32.const 4) (i32.const 12)) ;; iov.iov_len - The length of the 'hello world\n' string (call $fd_write (i32.const 1) ;; file_descriptor - 1 for stdout (i32.const 0) ;; *iovs - The pointer to the iov array, which is stored at memory location 0 (i32.const 1) ;; iovs_len - We're printing 1 string stored in an iov - so one. (i32.const 20) ;; nwritten - A place in memory to store the number of bytes written ) drop ;; Discard the number of bytes written from the top of the stack ) )
I've just tested this with wasmtime 28.0.0 and it works fine (prints "hello world" to stdout).
However, this example is using the "wasi_snapshot_preview1" API while preview 2 is currently stable. I think this example can probably be rewritten using output-stream write from streams.wit, but I'm a bit at a loss where to find code related to this.
Could anyone give some pointers on how to print via preview 2?
rikhuijzer commented on issue #9996:
I cobbled together wasi.wit manually myself, and it's not easily present in the upstream repos unfortunately. The easiest solution might be to ignore my advice and use https://github.com/WebAssembly/wasi-cli/ instead with its wit directory.
Nice! That works. For other people who might stumble into this issue later, the following works:
$ cat hello.wat (module (import "wasi:cli/stdout@0.2.3" "get-stdout" (func $get-stdout (result i32))) (import "wasi:io/streams@0.2.3" "[method]output-stream.blocking-write-and-flush" (func $blocking-write-and-flush (param i32 i32 i32 i32))) (memory (export "memory") 1) (func (export "wasi:cli/run@0.2.3#run") (result i32) call $get-stdout i32.const 100 i32.const 15 i32.const 96 call $blocking-write-and-flush i32.const 0 ) (data (i32.const 100) "Hello, world!\n") ) $ git clone https://github.com/WebAssembly/wasi-cli.git # commit d4fddec $ wasm-tools --version wasm-tools 1.223 $ wasmtime --version wasmtime 28.0.0 $ wasm-tools component embed wasi-cli/wit/ hello.wat --world command | wasm-tools component new -o component.wasm $ wasmtime component.wasm Hello, world!
As for the moer general question: while showing things in *.wat is "neat" and informational, it's not really how anyone actually develops. It's very useful for understanding things and how the pieces all fit together but the experience isn't particularly smooth because no other tooling works the same way.
Thank you. Much appreciated. I also watched your talk at https://youtu.be/0J99OLlKrtg and things make much more sense to me now.
I'll see whether I can find a nice place to add this example at https://component-model.bytecodealliance.org/.
Last updated: Jan 24 2025 at 00:11 UTC