Repository: bytecodealliance/wasm-tools
Pull request #1675 (2024-07-15 20:56:52)

[wit-parser] select default world among multiple packages
Previously, `Resolve::select_world` would return an error if multiple packages were provided and no world was specified.  Now, we return `Ok` in this case as long as exactly one package has exactly one world.

dicej: I've added a happy path test.  I'll add a couple of sad path ones, too.
dicej: Ugh, forgot to check in a file, sorry.

Patch:
diff --git a/crates/wit-parser/src/resolve.rs b/crates/wit-parser/src/resolve.rs
index 27f85db1..91c25b47 100644
--- a/crates/wit-parser/src/resolve.rs
+++ b/crates/wit-parser/src/resolve.rs
@@ -1024,25 +1024,32 @@ impl Resolve {
             }
             None => match packages {
                 [] => bail!("no packages were specified nor is a world specified"),
-                [one] => {
-                    let pkg = &self.packages[*one];
-                    match pkg.worlds.len() {
-                        0 => bail!("no worlds found in package `{}`", pkg.name),
-                        1 => return Ok(*pkg.worlds.values().next().unwrap()),
+                [..] => {
+                    let worlds = packages
+                        .iter()
+                        .flat_map(|pkg| {
+                            self.packages[*pkg]
+                                .worlds
+                                .values()
+                                .map(|world| (*pkg, *world))
+                        })
+                        .collect::<Vec<_>>();
+
+                    match &worlds[..] {
+                        [] => bail!("none of the specified packages contain a world"),
+                        [(_, world)] => return Ok(*world),
                         _ => bail!(
-                            "multiple worlds found in package `{}`, one must be explicitly chosen:{}",
-                            pkg.name,
-                            pkg.worlds.keys().map(|name| format!("\n  {name}")).collect::<String>()
+                            "multiple worlds found; one must be explicitly chosen:{}",
+                            worlds
+                                .iter()
+                                .map(|(pkg, world)| format!(
+                                    "\n  {}/{}",
+                                    self.packages[*pkg].name, self.worlds[*world].name
+                                ))
+                                .collect::<String>()
                         ),
                     }
                 }
-                [..] => {
-                    bail!(
-                        "the supplied WIT source files describe multiple packages; \
-                         please provide a fully-qualified world-specifier select \
-                         a world amongst these packages"
-                    )
-                }
             },
         };
         let pkg = &self.packages[pkg];


Test patch:
diff --git a/tests/cli/multiple-packages-multiple-worlds-in-package.wit b/tests/cli/multiple-packages-multiple-worlds-in-package.wit
new file mode 100644
index 00000000..eacfd470
--- /dev/null
+++ b/tests/cli/multiple-packages-multiple-worlds-in-package.wit
@@ -0,0 +1,24 @@
+// FAIL: component embed --dummy --wat %
+
+package test:foo1 {
+  interface bar {
+    baz: func(s: string) -> string;
+  }
+}
+
+package test:foo2 {
+  interface bar {
+    baz: func(s: string) -> string;
+  }
+}
+
+package test:hola {
+  world hello {
+    import test:foo1/bar;
+    export test:foo2/bar;
+  }
+  world some-other-world {
+    import test:foo1/bar;
+    export test:foo2/bar;
+  }
+}
diff --git a/tests/cli/multiple-packages-multiple-worlds-in-package.wit.stderr b/tests/cli/multiple-packages-multiple-worlds-in-package.wit.stderr
new file mode 100644
index 00000000..7eeb6c05
--- /dev/null
+++ b/tests/cli/multiple-packages-multiple-worlds-in-package.wit.stderr
@@ -0,0 +1,3 @@
+error: multiple worlds found; one must be explicitly chosen:
+  test:hola/hello
+  test:hola/some-other-world
diff --git a/tests/cli/multiple-packages-multiple-worlds.wit b/tests/cli/multiple-packages-multiple-worlds.wit
new file mode 100644
index 00000000..792a4353
--- /dev/null
+++ b/tests/cli/multiple-packages-multiple-worlds.wit
@@ -0,0 +1,24 @@
+// FAIL: component embed --dummy --wat %
+
+package test:foo1 {
+  interface bar {
+    baz: func(s: string) -> string;
+  }
+}
+
+package test:foo2 {
+  interface bar {
+    baz: func(s: string) -> string;
+  }
+  world some-other-world {
+    import test:foo1/bar;
+    export bar;
+  }
+}
+
+package test:hola {
+  world hello {
+    import test:foo1/bar;
+    export test:foo2/bar;
+  }
+}
diff --git a/tests/cli/multiple-packages-multiple-worlds.wit.stderr b/tests/cli/multiple-packages-multiple-worlds.wit.stderr
new file mode 100644
index 00000000..728b9c8e
--- /dev/null
+++ b/tests/cli/multiple-packages-multiple-worlds.wit.stderr
@@ -0,0 +1,3 @@
+error: multiple worlds found; one must be explicitly chosen:
+  test:foo2/some-other-world
+  test:hola/hello
diff --git a/tests/cli/multiple-packages-one-world.wit b/tests/cli/multiple-packages-one-world.wit
new file mode 100644
index 00000000..66788ecf
--- /dev/null
+++ b/tests/cli/multiple-packages-one-world.wit
@@ -0,0 +1,20 @@
+// RUN: component embed --dummy --wat %
+
+package test:foo1 {
+  interface bar {
+    baz: func(s: string) -> string;
+  }
+}
+
+package test:foo2 {
+  interface bar {
+    baz: func(s: string) -> string;
+  }
+}
+
+package test:hola {
+  world hello {
+    import test:foo1/bar;
+    export test:foo2/bar;    
+  }
+}
diff --git a/tests/cli/multiple-packages-one-world.wit.stdout b/tests/cli/multiple-packages-one-world.wit.stdout
new file mode 100644
index 00000000..c2926ef1
--- /dev/null
+++ b/tests/cli/multiple-packages-one-world.wit.stdout
@@ -0,0 +1,17 @@
+(module
+  (type (;0;) (func (param i32 i32 i32)))
+  (type (;1;) (func (param i32 i32) (result i32)))
+  (type (;2;) (func (param i32 i32 i32 i32) (result i32)))
+  (import "test:foo1/bar" "baz" (func (;0;) (type 0)))
+  (func (;1;) (type 1) (param i32 i32) (result i32)
+    unreachable
+  )
+  (func (;2;) (type 2) (param i32 i32 i32 i32) (result i32)
+    unreachable
+  )
+  (memory (;0;) 0)
+  (export "test:foo2/bar#baz" (func 1))
+  (export "memory" (memory 0))
+  (export "cabi_realloc" (func 2))
+  (@custom "component-type" (after code) "/00asm/0d/00/01/00/00/19/16wit-component-encoding/04/00/07d/01A/02/01A/04/01B/02/01@/01/01ss/00s/04/00/03baz/01/00/03/01/0dtest:foo1/bar/05/00/01B/02/01@/01/01ss/00s/04/00/03baz/01/00/04/01/0dtest:foo2/bar/05/01/04/01/0ftest:hola/hello/04/00/0b/0b/01/00/05hello/03/00/00/00//09producers/01/0cprocessed-by/01/0dwit-component/070.213.0")
+)