From 3ea9ec640371e5a4f51bcfbf7ee4c3df68b2647e Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Mon, 26 Aug 2024 11:19:04 -0500 Subject: [PATCH 1/5] locked/unlocked deps --- design/mvp/WIT.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/design/mvp/WIT.md b/design/mvp/WIT.md index 43a1c1e7..49cf97cb 100644 --- a/design/mvp/WIT.md +++ b/design/mvp/WIT.md @@ -661,6 +661,60 @@ world w2 { > configure that a `use`'d interface is a particular import or a particular > export. +## Unlocked Imports (semver) + +When working with a registry, the keyword `unlocked-dep` is available to specify version requirements as ranges. + +```wit +world w { + unlocked-dep foo:bar@{>=x.x.x =x.x.x =1.0.0}>") +``` + +A wasm component binary that has unlocked imports is referred to as an unlocked component. In general, unlocked components are what would be published to a registry. Registry aware tools have a command `lock` that will produce a "locked" component when it resolves dependency versions. As an example, today the warg cli has a [lock command](https://github.com/bytecodealliance/registry/blob/main/src/commands/lock.rs). This "locked" component will use locked import statements, rather than unlocked import statements, with pinned version numbers and integrity hashes discovered during package resolution, as can be seen below. + +```wat +(import "locked-dep=,integrity=") +``` + +Locked components aren't runnable unless they are being run by a registry aware runtime. They serve the role of a reproducible deployment artifact. In order to run them with a runtime that is not registry aware, one would need to use a `bundle` command, also made available by registry aware toolchains, that will inline component definitions where locked imports exist in a locked component. The warg cli also has a reference implementation of a [bundle command](https://github.com/bytecodealliance/registry/blob/main/src/commands/bundle.rs). ## WIT Functions [functions]: #wit-functions From 0b4ea580a9c61cf88b70e1e2fde35bf66fa01d8d Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Mon, 9 Sep 2024 08:16:19 -0500 Subject: [PATCH 2/5] Update design/mvp/WIT.md Co-authored-by: Luke Wagner --- design/mvp/WIT.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/design/mvp/WIT.md b/design/mvp/WIT.md index 49cf97cb..2a478dac 100644 --- a/design/mvp/WIT.md +++ b/design/mvp/WIT.md @@ -674,8 +674,7 @@ world w { The binary format has a corresponding [import definition](Explainer.md#import-and-export-definitions) and this WIT syntax informs bindgen tooling that it should be used. -The key idea here is to be able to specify a dependency on a _component_, rather than on a wit interface. Sometimes, as a component author, the goal is to have a dynamic import, where at a time after development is done, one of many implementations of a wit interface is specified, so that in essence your dependency makes your component configurable. This workflow is well documented across a variety of tools. Unlocked imports, on the other hand, are available for -specifying a dependency on a specific implementation of an interface, with a semver range +The point of `unlocked-dep` is to specify a dependency on a _component implementation_ (or a semver *range* of implementations), rather than on an abstract WIT interface (with an unspecified implementation). ### Example Unlocked Workflow Each language has its own toolchain for creating wasm components that should feel familiar to users of that language. As an example, somebody authoring a rust component would add the component they're interested in to their `Cargo.toml`. From 31547a03ef12b73fe94200d174602224719c8af5 Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Mon, 9 Sep 2024 08:16:31 -0500 Subject: [PATCH 3/5] Update design/mvp/WIT.md Co-authored-by: Luke Wagner --- design/mvp/WIT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/design/mvp/WIT.md b/design/mvp/WIT.md index 2a478dac..10cf40fd 100644 --- a/design/mvp/WIT.md +++ b/design/mvp/WIT.md @@ -661,7 +661,7 @@ world w2 { > configure that a `use`'d interface is a particular import or a particular > export. -## Unlocked Imports (semver) +## Unlocked Dependency Imports When working with a registry, the keyword `unlocked-dep` is available to specify version requirements as ranges. From 01f76d1a0c1b6e9592a0e391b00c005c839e7118 Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Mon, 9 Sep 2024 08:24:40 -0500 Subject: [PATCH 4/5] Apply suggestions from code review Co-authored-by: Luke Wagner --- design/mvp/WIT.md | 65 +++++++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/design/mvp/WIT.md b/design/mvp/WIT.md index 10cf40fd..32830978 100644 --- a/design/mvp/WIT.md +++ b/design/mvp/WIT.md @@ -676,44 +676,65 @@ bindgen tooling that it should be used. The point of `unlocked-dep` is to specify a dependency on a _component implementation_ (or a semver *range* of implementations), rather than on an abstract WIT interface (with an unspecified implementation). -### Example Unlocked Workflow -Each language has its own toolchain for creating wasm components that should feel familiar to users of that language. As an example, somebody authoring a rust component would add the component they're interested in to their `Cargo.toml`. +### Example Unlocked Dependency Workflow +Let's say someone authoring a Rust component `my:component` targeting the `wasi:http/proxy` world adds a dependency on another component `foo:bar` by adding the following (hypothetical) lines to their `Cargo.toml`: ``` -"foo:bar" = "x.x.x" +[package.metadata.component.target] +target = "wasi:http/proxy@0.2.0" + +[package.metadata.component.dependencies] +"foo:bar" = "1.2" +The language toolchain would first generate a new world that augments the target world with an additional unlocked dependency: +```wit +package my:component; +world generated-world { + include wasi:http/proxy@0.2.0; + unlocked-dep foo:bar@{>=1.2.0}; +} ``` -Say that the exports of this component match the exports of some `world exports`. Then the wit used for the component being authored would end up as follows: +Now say that `foo:bar@1.2.0` implements the following `exports` interface: +```wit +package foo:bar@1.2.0; +interface exports { + calc: func(x: u64) -> u64; +} +``` +The next step is to expand `generated-world` with nested packages fetched from all the relevant registries, producing an all-in-one WIT file with no external references: ```wit -package my:component +package my:component; + +package wasi:http@0.2.0 { + ... + world proxy { ... } +} -package foo:bar { +package foo:bar@1.2.0 { interface exports { - nest some:other/interfacename - ... + calc: func(x: u64) -> u64; } } -world w { - unlocked-dep foo:bar/exports@{>=x.x.x =1.2.0}; } ``` - - -Once this wit is synthesized by the language toolchain based on the language's package file, bindings can be generated and a wasm binary can be compiled which will contain unlocked imports as defined in the [import definition section](Explainer.md#import-and-export-definitions) of the explainer. Below is an example of the unlocked imports that will be present in the `wat` - +Note that `exports` is an arbitrary name and can be anything because the WIT bindings generation will always strip off the final interface name, leaving only the package name. In particular, the Component Model type for this world is: ```wat -(import "unlocked-dep==1.0.0}>") -``` - -A wasm component binary that has unlocked imports is referred to as an unlocked component. In general, unlocked components are what would be published to a registry. Registry aware tools have a command `lock` that will produce a "locked" component when it resolves dependency versions. As an example, today the warg cli has a [lock command](https://github.com/bytecodealliance/registry/blob/main/src/commands/lock.rs). This "locked" component will use locked import statements, rather than unlocked import statements, with pinned version numbers and integrity hashes discovered during package resolution, as can be seen below. - -```wat -(import "locked-dep=,integrity=") +(component + (import "wasi:http/types@0.2.0" (instance ...)) + (import "wasi:http/outgoing-handler@0.2.0" (instance ...)) + (import "unlocked-dep==1.2.0}>" (instance + (export "calc" (func (param "x" u64) (result u64))) + )) + (export "wasi:http/incoming-handler@0.2.0" (instance ...)) +) ``` -Locked components aren't runnable unless they are being run by a registry aware runtime. They serve the role of a reproducible deployment artifact. In order to run them with a runtime that is not registry aware, one would need to use a `bundle` command, also made available by registry aware toolchains, that will inline component definitions where locked imports exist in a locked component. The warg cli also has a reference implementation of a [bundle command](https://github.com/bytecodealliance/registry/blob/main/src/commands/bundle.rs). +A wasm component that contains `unlocked-dep` imports is referred to as an "unlocked component". Unlocked components are what you normally would want to publish to a registry, since it allows users of the unlocked component to perform the final dependency solving across a DAG of components. ## WIT Functions [functions]: #wit-functions From 0ae3a335edd4da8190c0c0b4e298dcc984ea4943 Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Mon, 9 Sep 2024 09:27:08 -0400 Subject: [PATCH 5/5] intro fix --- design/mvp/WIT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/design/mvp/WIT.md b/design/mvp/WIT.md index 32830978..7ee0df20 100644 --- a/design/mvp/WIT.md +++ b/design/mvp/WIT.md @@ -663,7 +663,7 @@ world w2 { ## Unlocked Dependency Imports -When working with a registry, the keyword `unlocked-dep` is available to specify version requirements as ranges. +When working with a registry, the keyword `unlocked-dep` is available to specify dependencies with package name and version requirements. For example: ```wit world w {