For those of you that are experimenting with building wasm components, there's the wasm-tools compose
command for stitching components together into a new component based off what's on your local file system. I've also put up https://wasmbuilder.app as a simple GUI for wiring up component instances explicitly using a graph representation (see the linked repo for instructions) . Hopefully they'll prove useful for someone
The cool thing about the GUI is that itself is implemented using a wasm component (via wit-bindgen's js host bindings); :turtle: s all the way down
@Peter Huene Do you expect any difficulties when generating Python bindings of a composed component?
I ask because when creating Python bindings for a simple (not composed) component, I do not face any problems. When I compose two components to one and do
python -m wasmtime.bindgen composed.wasm --out-dir python
I get an
[..]
wasm trap: wasm `unreachable` instruction executed
[..]
I tried to break it down to a minimal example but still I am not completely sure if I have a mistake in the way I use wasm-tools compose
(or sth. else)
i'm not familiar with the wasmtime python host bindings, so i can't say where the problem might lie from just this information
i'd test first on a single component that exports a single simple function, maybe returning an easily verifiable string value; then I would compose that component (as the import) with a component that imports an interface with the same simple function and exports the same function, implementing the export in terms of calling the import; to the host, it should return the same expected value
the bindings should actually be the same in both cases since the world would just contain the same exported function and no imports
so if it doesn't work, i'd expect a bug in wasmtime
if that simple case doesn't reproduce, then it's something specific to your component and we'd need to figure out what that trap represents
Agreed, I will do so.
I tried to follow your description. The result I get is the same as in my 'real world' project. I assembled this minimal example in this respository. Can you recommend any next steps? Should I open an issue at wasmtime?
Before I do so: in case you or anyone recognizes any mistakes in the example, please do not hesitate to leave me a note.
The issue probably belongs on the wasmtime-py repo; I can take a look on Monday to see what's going on
@Christoph Brewing so the trap is coming from this line, which indicates that wasmtime-py doesn't yet support handing a composed component that exchanges strings. @Alex Crichton will likely have more context than I regarding implementation status of wasmtime-py
Probably the best bet is to file an issue on the wasmtime-py repo linking to your repro repo
oh wow I didn't expect that unimplemented!
to get hit for a much longer time!
That shouldn't be the hardest thing in the world to implement but also not trivial. An issue would be much appreciated!
also if you're only getting unreachable
and no actual error message printed out then that's also a bug I need to fix
debug backtrace:
wasmtime._trap.Trap: error while executing at wasm backtrace:
0: 0x713c7d - panic_abort::__rust_start_panic::abort::hce48e00841fe5fb9
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/panic_abort/src/lib.rs:83:17 - __rust_start_panic
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/panic_abort/src/lib.rs:37:5
1: 0x7139bb - rust_panic
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/panicking.rs:740:9
2: 0x713982 - std::panicking::rust_panic_with_hook::h582baf536187e93d
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/panicking.rs:710:5
3: 0x7129fa - std::panicking::begin_panic_handler::{{closure}}::h10886906d9416d04
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/panicking.rs:577:13
4: 0x71295c - std::sys_common::backtrace::__rust_end_short_backtrace::hd12587e8e9c19331
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/sys_common/backtrace.rs:137:18
5: 0x712ff0 - rust_begin_unwind
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/std/src/panicking.rs:575:5
6: 0x71939c - core::panicking::panic_fmt::hb0157e5eb5d13797
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/core/src/panicking.rs:64:14
7: 0x719905 - core::panicking::panic::hd3974465c50a40c4
at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/core/src/panicking.rs:114:5
8: 0xbfa99 - bindgen::bindgen::Instantiator::global_initializer::ha042365cb24e1e89
at /Users/peterhuene/src/bytecodealliance/wasmtime-py/rust/bindgen/src/bindgen.rs:452:49
9: 0xbd94d - bindgen::bindgen::WasmtimePy::instantiate::h9843581d0d938b8c
at /Users/peterhuene/src/bytecodealliance/wasmtime-py/rust/bindgen/src/bindgen.rs:232:13
10: 0xbbdc2 - bindgen::bindgen::WasmtimePy::generate::h18c37b18ad175b4f
at /Users/peterhuene/src/bytecodealliance/wasmtime-py/rust/bindgen/src/bindgen.rs:150:9
11: 0x5bcc5 - <bindgen::bindings::PythonBindings as bindgen::bindings::WasmtimePy>::generate::h741c41ef729d8851
at /Users/peterhuene/src/bytecodealliance/wasmtime-py/rust/bindgen/src/lib.rs:48:13
12: 0x6673b - bindgen::bindings::call_generate::h1e265052a1f06de3
at /Users/peterhuene/src/bytecodealliance/wasmtime-py/rust/bindgen/src/lib.rs:38:5
13: 0x5bf94 - generate
at /Users/peterhuene/src/bytecodealliance/wasmtime-py/rust/bindgen/src/lib.rs:38:5
14: 0x7261ec - <unknown>!generate.command_export
Caused by:
wasm trap: wasm `unreachable` instruction executed
in theory the standard Rust unimplemented!()
panic message should be printed somewhere, but I may have forgotten to wire that up
it does: thread '<unnamed>' panicked at 'not implemented', bindgen/src/bindgen.rs:452:49
ah ok phew just making sure!
not that that's an "official" error mesage of course, just wanted to make sure that was printed
:heart: Thank you very much for your investigations! I created issue no. 143.
Thanks! I'll write up some more notes on the issue of what to do
I am about to compose an application comprising two components but the dependencies are not found.
The lower layer, called exporter, provides functionality in terms of multiple exported interfaces. The upper layer, called importer, is supposed to import lower layer's interfaces, do sth. with them and export another interface. I think of it as a facade like pattern. However, I do not arrive to compose the importer and exporter components:
[exporter component]
$ wasm-tools component wit exporter_component.wasm
interface ione {
greet: func(s: string) -> string
}
interface itwo {
greet: func(s: string) -> string
}
default world exporter {
export first: self.ione
export second: self.itwo
}
[importer component]
$ wasm-tools component wit importer_component.wasm
interface ione {
greet: func(s: string) -> string
}
interface itwo {
greet: func(s: string) -> string
}
default world importer {
import first: self.ione
import second: self.itwo
export something_else
}
When I try to compose
$ wasm-tools compose -o composed.wasm importer_component.wasm -p .
instance `first` will be imported because a dependency named `first` could not be found
instance `second` will be imported because a dependency named `second` could not be found
Error: no dependencies of component `importer_component.wasm` were found
In the past, I only had a single interface to be imported. I got around the no dependencies .. where found
error by naming the exporter according to the exported artefact, e.g. first
.
What do I have to do that the command wasm-tools compose
recognizes multiple exports?
By default, wasm-compose
looks for components with a filename matching the import name to satisfy an individual import; with this more complicated linking, you'll need to author a config file to feed the tool, which explicitly wires up the first
and second
exports from exporter_component
to the instantiation arguments of importer_component
. The config file syntax is documented here
you can also use https://wasmbuilder.app to quickly wire these things up manually in a composition
Excellent, that works! The following config file seems to do the trick:
dependencies:
a: importer_component.wasm
first:
path: exporter_component.wasm
second:
path: exporter_component.wasm
Right, I have tried your super convenient service .. was just thinking about an integration pipeline and so I am pretty glad to have checked this path as well.
I believe that config will create two different instances of the exporter component, though. there's a way to create a single instance of it and explicitly tell the composer to use an export from that instance as an instantiation argument for the root instance
Something like:
dependencies:
exporter:
path: exporter_component.wasm
instantiations:
$input:
arguments:
first:
instance: exporter
export: first
second:
instance: exporter
export: second
i believe that will create a single instance of the exporter component, if that's desired
Last updated: Nov 22 2024 at 16:03 UTC