Stream: git-wasmtime

Topic: wasmtime / issue #9996 WASI hello world via preview 2


view this post on Zulip Wasmtime GitHub notifications bot (Jan 13 2025 at 18:10):

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?

view this post on Zulip Wasmtime GitHub notifications bot (Jan 13 2025 at 20:52):

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!)

view this post on Zulip Wasmtime GitHub notifications bot (Jan 13 2025 at 20:53):

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!)

view this post on Zulip Wasmtime GitHub notifications bot (Jan 13 2025 at 20:53):

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!)

view this post on Zulip Wasmtime GitHub notifications bot (Jan 14 2025 at 08:03):

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:

view this post on Zulip Wasmtime GitHub notifications bot (Jan 15 2025 at 21:31):

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:

This might also require a --world argument to specify wasi:cli/run for example

view this post on Zulip Wasmtime GitHub notifications bot (Jan 17 2025 at 09:58):

rikhuijzer commented on issue #9996:

Is the following indeed the direction I should take? hello.wat here is the code you wrote with the 0.2.3 replaced by 0.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 to WASI/wasip2 because io doesn't include cli:

$ 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?

view this post on Zulip Wasmtime GitHub notifications bot (Jan 21 2025 at 15:38):

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 its wit 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)

view this post on Zulip Wasmtime GitHub notifications bot (Jan 22 2025 at 15:43):

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?

view this post on Zulip Wasmtime GitHub notifications bot (Jan 22 2025 at 15:43):

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