Stream: general

Topic: WIT (bindgen) versioning


view this post on Zulip CircuitSacul (Mar 03 2024 at 01:36):

I noticed that the WIT format allows for a package name with a version. Is it possible to define multiple versions at once, and allow them to both function at the same time?

Initially I was thinking that I would store the expected "api version" alongside the binary, and then, based on that, decide which wit file to use (named things like apiv1.wit, apiv2.wit, etc.). But if I'm able to avoid that by using package versions, that would be nice.

The problem is, I'm struggling to find much info on it. Any direction on this would be appreciated.

view this post on Zulip Alex Crichton (Mar 03 2024 at 17:40):

Yes, multiple versions of the same package are allowed. You'll need to split them across folders, though, as each package has a single logical version and packages are defined in the same directory right now, so you'd need something like deps/v1/myapi.wit and deps/v2/myapi.wit

view this post on Zulip CircuitSacul (Mar 03 2024 at 19:40):

is it possible to combine this with wasmtime::component::bindgen!()? I want to detect the version expected by a wasm binary and then use the correct apis, if possible

view this post on Zulip Alex Crichton (Mar 03 2024 at 19:51):

yes, you'll get a trait-per-version to implement and corresponding add_to_linker functions

view this post on Zulip CircuitSacul (Mar 03 2024 at 19:54):

Awesome. One last question, if the host system has v1.1.0, but the guest code uses a previous 1.0.0, will it still work? Like, if I make changes to the API that are backward-compatible, is it ok if the versions don't match exactly (like minor/patch bumps)? Or do I need to keep every specific version until I'm ready to deprecate/remove it?

view this post on Zulip Alex Crichton (Mar 03 2024 at 20:05):

With this PR, yes, but that PR isn't in 18.0.2, it'll be in 19.0.0

This commit is an implementation of #7860 for Wasmtime where wasmtime::component::Linker is now "semver aware". This means that it assumes that hosts are always managing WIT interfaces in a semver-...

view this post on Zulip CircuitSacul (Mar 03 2024 at 20:07):

Got it, thanks. Is there any roadmap, or release schedule, for 19.0.0 that you're aware of?

view this post on Zulip Alex Crichton (Mar 03 2024 at 20:12):

you can read about the release process here, and the answer you're looking for is "March 20"

view this post on Zulip CircuitSacul (Mar 04 2024 at 14:40):

I'm struggling to get this to work with wasmtime bindgen. I've tried this:

wit/deps/
    v0/
        api.wit
     v1/
        api.wit
bindgen!()

But it just errors out:

error: failed to parse package: /Users/circuitsacul/src/wasmbot/wasmbot/app/runtime/wit

       Caused by:
           no `package` header was found in any WIT file for this package

Both the api.wit files have a package header (one is @0.1.0, the other is @1.0.0). I also tried adding another top-level wit file and useing the api package, but it couldn't find the package by name.

What does work is using bindgen!() and referring to the path specifically, but I can't do that for two versions of the same package because the generated code would conflict (in particular, the struct name and the package:name).

What am I doing wrong here?

view this post on Zulip CircuitSacul (Mar 04 2024 at 14:58):

Ok I think I have a solution. I'll see if it actually works later when I try to run some wasm, but this is what I did:

wit/
    v0/api.wit (@0.1.0)
    v1/api.wit (@1.0.0)
mod v0 {
    use wasmtime::component::bindgen;

    bindgen!({
        path: "wit/v0",
        async: true
    });
}
mod v1 {
    use wasmtime::component::bindgen;

    bindgen!({
        path: "wit/v1",
        async: true
    });
}

struct State;

impl v0::wasmbot::api::host::Host for State {}
impl v1::wasmbot::api::host::Host for State {}

Now I just need to figure out how to determine which bindings to use. I suspect that v0::Api::instantiate_async will return an error if the guest package is using a newer version of the api? Or should I just store the expected package version separately?

view this post on Zulip Alex Crichton (Mar 04 2024 at 15:29):

Oh that works as well, yeah, but the first error is that, for now at least, you need at least a dummy *.wit file in the "root" folder as wit/$something.wit, and then the deps folder will get read

view this post on Zulip CircuitSacul (Mar 04 2024 at 15:54):

I get the same error doing that. Does something special need to be written in the dummy wit file?

view this post on Zulip CircuitSacul (Mar 04 2024 at 15:56):

# wit/deps/v0/api.wit
package wasmbot:api@0.1.0;

world api {
    import host: interface {}

    export guest: interface {
        on-deploy: func();
        on-ws-msg: func();
        on-timer: func();
    }
}
# wit/dummy.wit (empty)
bindgen!({ async: true });
error: failed to parse package: /Users/circuitsacul/src/wasmbot/wasmbot/app/runtime/wit

       Caused by:
           no `package` header was found in any WIT file for this package

view this post on Zulip Alex Crichton (Mar 04 2024 at 15:57):

ah yeah, sorry, you'll need at least a dummy package foo:bar; in the dummy.witf ile

view this post on Zulip Alex Crichton (Mar 04 2024 at 15:57):

(we hope to lift these restrictions one day)

view this post on Zulip CircuitSacul (Mar 04 2024 at 15:58):

Doing that gives this error

error: no worlds found in package `dummy:dummy`

Trying bindgen!({world: "api"}) just says that the world "api" doesn't exist in this package

view this post on Zulip CircuitSacul (Mar 04 2024 at 15:59):

I think it's fine though, my method worked and I don't see any big downsides to that approach

view this post on Zulip Alex Crichton (Mar 04 2024 at 16:01):

ah yes, for that you'd specify world: "wasmbot:api/api@0.1.0"

view this post on Zulip Alex Crichton (Mar 04 2024 at 16:01):

but yeah if the other approach works for you no need to switch


Last updated: Oct 23 2024 at 20:03 UTC