From fd33cfe66864bbe87dd024ba990a6c5d4f9122e5 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 26 Aug 2025 09:15:28 -0700 Subject: [PATCH] Support lists of WIT directories on the CLI. Accept multiple WIT paths in the wit-bindgen CLI, using the same logic as the multiple WIT paths accepted by the `path` parameter in the Rust bindgen macro. Also, document that list-of-WIT interfaces, in the Rust macro and now the wit-bindgen CLI, have a topological ordering requirement which may be lifted in the future. --- Cargo.lock | 72 +++++++++++++++--------------- Cargo.toml | 14 +++--- crates/guest-rust/macro/src/lib.rs | 44 ++---------------- crates/guest-rust/src/lib.rs | 4 +- crates/test-helpers/src/lib.rs | 4 +- crates/test/src/lib.rs | 12 +++-- src/bin/wit-bindgen.rs | 24 ++++++---- 7 files changed, 76 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 55b191fd5..e5fea07c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -816,7 +816,7 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.237.0", + "wasm-encoder 0.238.0", "wit-bindgen-core", "wit-component", "wit-parser", @@ -999,9 +999,9 @@ checksum = "ddbd7f2a9e3635abe5d4df93b12cadc8d6818079785ee4fab3719ae3c85a064e" [[package]] name = "wasm-compose" -version = "0.237.0" +version = "0.238.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4691ea85473e81ea13f91088854186eb792710d7c1af8b07bc1ad1ab3a02404f" +checksum = "9c1268eae4d3f230a9881ea05e13216734c2d87ef842f1862efbc0abbe6c6b69" dependencies = [ "anyhow", "heck 0.4.1", @@ -1013,8 +1013,8 @@ dependencies = [ "serde_derive", "serde_yaml", "smallvec", - "wasm-encoder 0.237.0", - "wasmparser 0.237.0", + "wasm-encoder 0.238.0", + "wasmparser 0.238.0", "wat", ] @@ -1029,12 +1029,12 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.237.0" +version = "0.238.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efe92d1321afa53ffc88a57c497bb7330c3cf84c98ffdba4a4caf6a0684fad3c" +checksum = "50143b010bdc3adbd16275710f9085cc80d9c12cb869309a51a98ce2ff96558e" dependencies = [ "leb128fmt", - "wasmparser 0.237.0", + "wasmparser 0.238.0", ] [[package]] @@ -1055,14 +1055,14 @@ dependencies = [ [[package]] name = "wasm-metadata" -version = "0.237.0" +version = "0.238.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cc0b0a0c4f35ca6efa7a797671372915d4e9659dba2d59edc6fafc931d19997" +checksum = "a587a83ac49c2feb922b7ec5d504419320d5da41cf0726f44b2968c78fa2ee2a" dependencies = [ "anyhow", "indexmap", - "wasm-encoder 0.237.0", - "wasmparser 0.237.0", + "wasm-encoder 0.238.0", + "wasmparser 0.238.0", ] [[package]] @@ -1078,9 +1078,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.237.0" +version = "0.238.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d2a40ca0d2bdf4b0bf36c13a737d0b2c58e4c8aaefe1c57f336dd75369ca250" +checksum = "c0ad4ca2ecb86b79ea410cd970985665de1d05774b7107b214bc5852b1bcbad7" dependencies = [ "bitflags", "hashbrown", @@ -1091,22 +1091,22 @@ dependencies = [ [[package]] name = "wast" -version = "237.0.0" +version = "238.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf66f545acbd55082485cb9a6daab54579cb8628a027162253e8e9f5963c767" +checksum = "8c671ea796336ebaa49b963adb14cf13cb98de4e64d69ed4a16ace8c7b4db87b" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.1", - "wasm-encoder 0.237.0", + "wasm-encoder 0.238.0", ] [[package]] name = "wat" -version = "1.237.0" +version = "1.238.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27975186f549e4b8d6878b627be732863883c72f7bf4dcf8f96e5f8242f73da9" +checksum = "8de04a6a9c93aaae4de7bec6323bf11f810457b479f9f877e80d212fd77ffdbc" dependencies = [ "wast", ] @@ -1282,8 +1282,8 @@ dependencies = [ "clap", "heck 0.5.0", "indexmap", - "wasm-encoder 0.237.0", - "wasm-metadata 0.237.0", + "wasm-encoder 0.238.0", + "wasm-metadata 0.238.0", "wit-bindgen-core", "wit-component", ] @@ -1295,7 +1295,7 @@ dependencies = [ "anyhow", "clap", "env_logger", - "wasm-encoder 0.237.0", + "wasm-encoder 0.238.0", "wit-bindgen-c", "wit-bindgen-core", "wit-bindgen-cpp", @@ -1326,8 +1326,8 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.237.0", - "wasm-metadata 0.237.0", + "wasm-encoder 0.238.0", + "wasm-metadata 0.238.0", "wit-bindgen-c", "wit-bindgen-core", "wit-component", @@ -1341,7 +1341,7 @@ dependencies = [ "clap", "heck 0.5.0", "indexmap", - "wasm-metadata 0.237.0", + "wasm-metadata 0.238.0", "wit-bindgen-core", "wit-component", "wit-parser", @@ -1391,7 +1391,7 @@ dependencies = [ "serde_json", "syn", "test-helpers", - "wasm-metadata 0.237.0", + "wasm-metadata 0.238.0", "wit-bindgen", "wit-bindgen-core", "wit-bindgen-rt", @@ -1429,8 +1429,8 @@ dependencies = [ "wac-types", "wasi-preview1-component-adapter-provider", "wasm-compose", - "wasm-encoder 0.237.0", - "wasmparser 0.237.0", + "wasm-encoder 0.238.0", + "wasmparser 0.238.0", "wat", "wit-bindgen-csharp", "wit-component", @@ -1439,9 +1439,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.237.0" +version = "0.238.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb7674f76c10e82fe00b256a9d4ffb2b8d037d42ab8e9a83ebb3be35c9d0bf6" +checksum = "d577b6b6ca3d05cf2a0367e85b1cdfb269155022ba272ae5a0e14c1e1cb59e4d" dependencies = [ "anyhow", "bitflags", @@ -1450,18 +1450,18 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.237.0", - "wasm-metadata 0.237.0", - "wasmparser 0.237.0", + "wasm-encoder 0.238.0", + "wasm-metadata 0.238.0", + "wasmparser 0.238.0", "wat", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.237.0" +version = "0.238.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce2596a5bc7c24cc965b56ad6ff9e32394c4e401764f89620a888519c6e849ab" +checksum = "d28fd1ea7579c62574b01b413d80293a0a3f3076d387752cd823a3b0e43e96f0" dependencies = [ "anyhow", "id-arena", @@ -1472,5 +1472,5 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.237.0", + "wasmparser 0.238.0", ] diff --git a/Cargo.toml b/Cargo.toml index fce12efa0..fad2d58c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,13 +33,13 @@ prettyplease = "0.2.20" syn = { version = "2.0.89", features = ["printing"] } futures = "0.3.31" -wat = "1.237.0" -wasmparser = "0.237.0" -wasm-encoder = "0.237.0" -wasm-metadata = { version = "0.237.0", default-features = false } -wit-parser = "0.237.0" -wit-component = "0.237.0" -wasm-compose = "0.237.0" +wat = "1.238.0" +wasmparser = "0.238.0" +wasm-encoder = "0.238.0" +wasm-metadata = { version = "0.238.0", default-features = false } +wit-parser = "0.238.0" +wit-component = "0.238.0" +wasm-compose = "0.238.0" wit-bindgen-core = { path = 'crates/core', version = '0.44.0' } wit-bindgen-c = { path = 'crates/c', version = '0.44.0' } diff --git a/crates/guest-rust/macro/src/lib.rs b/crates/guest-rust/macro/src/lib.rs index a984dc96e..ef23291f9 100644 --- a/crates/guest-rust/macro/src/lib.rs +++ b/crates/guest-rust/macro/src/lib.rs @@ -52,7 +52,7 @@ struct Config { /// The source of the wit package definition enum Source { - /// A path to a wit directory + /// A list of paths to wit directories Paths(Vec), /// Inline sources have an optional path to a directory of their dependencies Inline(String, Option>), @@ -174,9 +174,10 @@ impl Parse for Config { )])); } } - let (resolve, pkgs, files) = + let (resolve, main_packages, files) = parse_source(&source, &features).map_err(|err| anyhow_to_syn(call_site, err))?; - let world = select_world(&resolve, &pkgs, world.as_deref()) + let world = resolve + .select_world(&main_packages, world.as_deref()) .map_err(|e| anyhow_to_syn(call_site, e))?; Ok(Config { opts, @@ -188,43 +189,6 @@ impl Parse for Config { } } -fn select_world( - resolve: &Resolve, - pkgs: &[PackageId], - world: Option<&str>, -) -> anyhow::Result { - if pkgs.len() == 1 { - resolve.select_world(pkgs[0], world) - } else { - assert!(!pkgs.is_empty()); - match world { - Some(name) => { - if !name.contains(":") { - anyhow::bail!( - "with multiple packages a fully qualified \ - world name must be specified" - ) - } - - // This will ignore the package argument due to the fully - // qualified name being used. - resolve.select_world(pkgs[0], world) - } - None => { - let worlds = pkgs - .iter() - .filter_map(|p| resolve.select_world(*p, None).ok()) - .collect::>(); - match &worlds[..] { - [] => anyhow::bail!("no packages have a world"), - [world] => Ok(*world), - _ => anyhow::bail!("multiple packages have a world, must specify which to use"), - } - } - } - } -} - /// Parse the source fn parse_source( source: &Option, diff --git a/crates/guest-rust/src/lib.rs b/crates/guest-rust/src/lib.rs index 99d6cbb9f..3cb9efbd1 100644 --- a/crates/guest-rust/src/lib.rs +++ b/crates/guest-rust/src/lib.rs @@ -652,7 +652,9 @@ /// // ["../path/to/wit1", "../path/to/wit2"] /// // Usually used in testing, our test suite may want to generate code /// // from wit files located in multiple paths within a single mod, and we -/// // don't want to copy these files again. +/// // don't want to copy these files again. Currently these locations must +/// // be ordered, as later paths can't contain dependencies on earlier +/// // paths. This restriction may be lifted in the future. /// path: "../path/to/wit", /// /// // Enables passing "inline WIT". If specified this is the default diff --git a/crates/test-helpers/src/lib.rs b/crates/test-helpers/src/lib.rs index 6470f8086..f25088697 100644 --- a/crates/test-helpers/src/lib.rs +++ b/crates/test-helpers/src/lib.rs @@ -143,9 +143,9 @@ pub fn run_component_codegen_test( fn parse_wit(path: &Path) -> (Resolve, WorldId) { let mut resolve = Resolve::default(); let (pkg, _files) = resolve.push_path(path).unwrap(); - let world = resolve.select_world(pkg, None).unwrap_or_else(|_| { + let world = resolve.select_world(&[pkg], None).unwrap_or_else(|_| { // note: if there are multiples worlds in the wit package, we assume the "imports" world - resolve.select_world(pkg, Some("imports")).unwrap() + resolve.select_world(&[pkg], Some("imports")).unwrap() }); (resolve, world) } diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index 468eaa309..a7c7e3bce 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -342,7 +342,7 @@ impl Runner<'_> { let mut worlds = Vec::new(); let mut push_world = |kind: Kind, name: &str| -> Result<()> { - let world = resolve.select_world(pkg, Some(name)).with_context(|| { + let world = resolve.select_world(&[pkg], Some(name)).with_context(|| { format!("failed to find expected `{name}` world to generate bindings") })?; worlds.push((world, kind)); @@ -582,8 +582,12 @@ impl Runner<'_> { let mut resolve = wit_parser::Resolve::default(); let (pkg, _) = resolve.push_path(test).context("failed to load WIT")?; let world = resolve - .select_world(pkg, None) - .or_else(|err| resolve.select_world(pkg, Some("imports")).map_err(|_| err)) + .select_world(&[pkg], None) + .or_else(|err| { + resolve + .select_world(&[pkg], Some("imports")) + .map_err(|_| err) + }) .context("failed to select a world for bindings generation")?; let world = resolve.worlds[world].name.clone(); @@ -992,7 +996,7 @@ status: {}", let (pkg, _) = resolve .push_path(&compile.component.bindgen.wit_path) .context("failed to load WIT")?; - let world = resolve.select_world(pkg, Some(&compile.component.bindgen.world))?; + let world = resolve.select_world(&[pkg], Some(&compile.component.bindgen.world))?; let mut module = fs::read(&p1).context("failed to read wasm file")?; let encoded = wit_component::metadata::encode(&resolve, world, StringEncoding::UTF8, None)?; diff --git a/src/bin/wit-bindgen.rs b/src/bin/wit-bindgen.rs index 0c909439e..d1d3666a8 100644 --- a/src/bin/wit-bindgen.rs +++ b/src/bin/wit-bindgen.rs @@ -85,14 +85,18 @@ struct Common { #[clap(long = "out-dir")] out_dir: Option, - /// Location of WIT file(s) to generate bindings for. + /// Locations of WIT file(s) to generate bindings for. /// - /// This path can be either a directory containing `*.wit` files, a `*.wit` - /// file itself, or a `*.wasm` file which is a wasm-encoded WIT package. - /// Most of the time it's likely to be a directory containing `*.wit` files - /// with an optional `deps` folder inside of it. + /// These paths can be either directories containing `*.wit` files, `*.wit` + /// files themselves, or `*.wasm` files which are wasm-encoded WIT packages. + /// Most of the time they're likely to be directories containing `*.wit` + /// files with optional `deps` folders inside of them. + /// + /// Currently these locations must be ordered, as later paths can't contain + /// dependencies on earlier paths. This restriction may be lifted in the + /// future. #[clap(value_name = "WIT", index = 1)] - wit: PathBuf, + wit: Vec, /// Optionally specified world that bindings are generated for. /// @@ -219,8 +223,12 @@ fn gen_world( resolve.features.insert(feature.to_string()); } } - let (pkg, _files) = resolve.push_path(&opts.wit)?; - let world = resolve.select_world(pkg, opts.world.as_deref())?; + let mut main_packages = Vec::new(); + for wit in &opts.wit { + let (pkg, _files) = resolve.push_path(wit)?; + main_packages.push(pkg); + } + let world = resolve.select_world(&main_packages, opts.world.as_deref())?; generator.generate(&resolve, world, files)?; Ok(())