diff --git a/component-model/examples/composition/README.md b/component-model/examples/composition/README.md new file mode 100644 index 0000000..307f7c3 --- /dev/null +++ b/component-model/examples/composition/README.md @@ -0,0 +1,66 @@ +## Summary: +This directory contains an example where component can be written and built separately in different languages and then composed together. +There are multiple components in this repo : +add, calculator and app are components written in RUST. +sub is a component written in tinygo. + +calculator is a component that composes the components add and sub at compile time. +calculator is a library component and cannot be used as a standalone app. +Hence, the app component is built as a command component that composes the calculator component. + +## Prerequisites: + +### Install Wasmtime +curl https://wasmtime.dev/install.sh -sSf | bash + +### Update PATH +Wasmtime binary gets deployed at ~/.wasmtime directory. + +Update bash profile with wasmtime +```export PATH="~/.wasmtime/bin:$PATH"``` + + +### Install cargo component +```cargo install cargo-component``` + + +## Build and test + +### Build the components + +``` +cd calculator +cargo component build --release +cd .. +cd add +cargo component build --release +cd .. + +cd app +cargo component build --release +cd .. +``` + +### Subtractor component is a go component. Build it according to the readme in the sub directory to generate output component- sub-component.wasm. +``` +cd sub +``` + +### Compose the components add, sub and calculator +``` +cd .. +wasm-tools compose calculator/target/wasm32-wasi/release/calculator.wasm -d add/target/wasm32-wasi/release/add.wasm -d sub/sub-component.wasm -o composed-add-sub.wasm +``` + +### Generate app component using composed-add-sub.wasm +``` +wasm-tools compose app/target/wasm32-wasi/release/app.wasm -d composed-add-sub.wasm -o command.wasm +``` + + +### Run the command component using wasmtime to test it +``` +wasmtime run command.wasm 1 2 add + +wasmtime run command.wasm 1 2 sub +``` diff --git a/component-model/examples/composition/add/Cargo.lock b/component-model/examples/composition/add/Cargo.lock new file mode 100644 index 0000000..687bd29 --- /dev/null +++ b/component-model/examples/composition/add/Cargo.lock @@ -0,0 +1,25 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "add" +version = "0.1.0" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "wit-bindgen-rt" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0780cf7046630ed70f689a098cd8d56c5c3b22f2a7379bbdb088879963ff96" +dependencies = [ + "bitflags", +] diff --git a/component-model/examples/composition/add/Cargo.toml b/component-model/examples/composition/add/Cargo.toml new file mode 100644 index 0000000..2f68c74 --- /dev/null +++ b/component-model/examples/composition/add/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "add" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +wit-bindgen-rt = { version = "0.24.0", features = ["bitflags"] } + +[lib] +crate-type = ["cdylib"] + +[package.metadata.component] +package = "component:add" + +[package.metadata.component.dependencies] diff --git a/component-model/examples/composition/add/src/lib.rs b/component-model/examples/composition/add/src/lib.rs new file mode 100644 index 0000000..82ed761 --- /dev/null +++ b/component-model/examples/composition/add/src/lib.rs @@ -0,0 +1,14 @@ +#[allow(warnings)] +mod bindings; + +use bindings::exports::docs::adder::add::Guest; + +struct Component; + +impl Guest for Component { + fn add(x: u32, y: u32) -> u32 { + x + y + } +} + +bindings::export!(Component with_types_in bindings); diff --git a/component-model/examples/composition/add/wit/world.wit b/component-model/examples/composition/add/wit/world.wit new file mode 100644 index 0000000..640897d --- /dev/null +++ b/component-model/examples/composition/add/wit/world.wit @@ -0,0 +1,10 @@ +package docs:adder; + + +interface add { + add: func(a: u32, b: u32) -> u32; +} + +world adder { + export add; +} diff --git a/component-model/examples/composition/app/Cargo.lock b/component-model/examples/composition/app/Cargo.lock new file mode 100644 index 0000000..d5907ce --- /dev/null +++ b/component-model/examples/composition/app/Cargo.lock @@ -0,0 +1,253 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anstream" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" + +[[package]] +name = "app" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "wit-bindgen-rt", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "clap" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "proc-macro2" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "wit-bindgen-rt" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index/" +checksum = "3b0780cf7046630ed70f689a098cd8d56c5c3b22f2a7379bbdb088879963ff96" +dependencies = [ + "bitflags", +] diff --git a/component-model/examples/composition/app/Cargo.toml b/component-model/examples/composition/app/Cargo.toml new file mode 100644 index 0000000..32efe4e --- /dev/null +++ b/component-model/examples/composition/app/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "app" +version = "0.1.0" +edition = "2021" + +[package.metadata.component] +package = "component:app" + +[package.metadata.component.dependencies] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1" +clap = { version = "4.3.19", features = ["derive"] } +wit-bindgen-rt = { version = "0.24.0", features = ["bitflags"] } +[package.metadata.component.target] +path = "wit" + +[package.metadata.component.target.dependencies] +"component:calculator" = { path = "../calculator/wit" } +"docs:adder" = { path = "../add/wit" } +"docs:subtractor" = { path = "../sub/wit" } \ No newline at end of file diff --git a/component-model/examples/composition/app/src/main.rs b/component-model/examples/composition/app/src/main.rs new file mode 100644 index 0000000..a416f2e --- /dev/null +++ b/component-model/examples/composition/app/src/main.rs @@ -0,0 +1,51 @@ +#[allow(warnings)] +mod bindings; + +use clap::Parser; +use std::fmt; + +use bindings::component::calculator::{calculate, calculate::Op}; + +fn main() { + Command::parse().run() +} + +/// A CLI for executing mathematical expressions +/// using WebAssembly +#[derive(Parser)] +#[clap(name = "calculator", version = env!("CARGO_PKG_VERSION"))] +struct Command { + /// The first operand + x: u32, + /// The second operand + y: u32, + /// Expression operator + #[clap(value_parser = parse_operator)] + op: Op, +} + + +impl Command { + fn run(self) { + let res = calculate::eval_expression(self.op, self.x, self.y); + println!("{} {} {} = {res}", self.x, self.op, self.y); + } +} + +fn parse_operator(op: &str) -> anyhow::Result { + match op { + "add" => Ok(Op::Add), + "sub" => Ok(Op::Sub), + _ => anyhow::bail!("Unknown operation: {}", op), + + } +} + +impl fmt::Display for Op { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Op::Add => write!(f, "+"), + Op::Sub => write!(f, "-") + } + } +} \ No newline at end of file diff --git a/component-model/examples/composition/app/wit/world.wit b/component-model/examples/composition/app/wit/world.wit new file mode 100644 index 0000000..a78fd02 --- /dev/null +++ b/component-model/examples/composition/app/wit/world.wit @@ -0,0 +1,5 @@ +package docs:app; + +world app { + import component:calculator/calculate; +} diff --git a/component-model/examples/composition/calculator/Cargo.lock b/component-model/examples/composition/calculator/Cargo.lock new file mode 100644 index 0000000..e6dba97 --- /dev/null +++ b/component-model/examples/composition/calculator/Cargo.lock @@ -0,0 +1,25 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "calculator" +version = "0.1.0" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index/" +checksum = "3b0780cf7046630ed70f689a098cd8d56c5c3b22f2a7379bbdb088879963ff96" +dependencies = [ + "bitflags", +] diff --git a/component-model/examples/composition/calculator/Cargo.toml b/component-model/examples/composition/calculator/Cargo.toml new file mode 100644 index 0000000..65c52d4 --- /dev/null +++ b/component-model/examples/composition/calculator/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "calculator" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +wit-bindgen-rt = { version = "0.24.0", features = ["bitflags"] } + +[lib] +crate-type = ["cdylib"] + +[package.metadata.component] +package = "component:calculator" + +[package.metadata.component.target.dependencies] +"docs:adder" = { path = "../add/wit" } # directory containing the WIT package +"docs:subtractor" = { path = "../sub/wit" } # directory containing the WIT package + +[package.metadata.component.dependencies] diff --git a/component-model/examples/composition/calculator/src/lib.rs b/component-model/examples/composition/calculator/src/lib.rs new file mode 100644 index 0000000..b20b8fa --- /dev/null +++ b/component-model/examples/composition/calculator/src/lib.rs @@ -0,0 +1,18 @@ +#[allow(warnings)] +mod bindings; + +use bindings::exports::component::calculator::calculate::{Guest, Op}; +use bindings::docs::adder::add::add; +use bindings::docs::subtractor::sub::sub; +struct Component; + +impl Guest for Component { + fn eval_expression(op: Op, x: u32, y: u32) -> u32{ + match op { + Op::Add => add(x, y), + Op::Sub => sub(x, y), + } + } +} + +bindings::export!(Component with_types_in bindings); diff --git a/component-model/examples/composition/calculator/wit/world.wit b/component-model/examples/composition/calculator/wit/world.wit new file mode 100644 index 0000000..2e51cf9 --- /dev/null +++ b/component-model/examples/composition/calculator/wit/world.wit @@ -0,0 +1,15 @@ +package component:calculator; + +interface calculate { + enum op { + add, + sub, + } + eval-expression: func(op: op, x: u32, y: u32) -> u32; +} + +world calculator { + export calculate; + import docs:adder/add; + import docs:subtractor/sub; +} diff --git a/component-model/examples/composition/sub/README.md b/component-model/examples/composition/sub/README.md new file mode 100644 index 0000000..2536ec3 --- /dev/null +++ b/component-model/examples/composition/sub/README.md @@ -0,0 +1,12 @@ +tinygo build -o sub.wasm -target=wasi sub.go + +# convert to component using wasm-tools using the below 3 commands: +``` +export COMPONENT_ADAPTER_REACTOR=../wasi_snapshot_preview1.reactor.wasm +wasm-tools component embed --world subtractor wit/world.wit sub.wasm -o sub.embed.wasm +wasm-tools component new -o sub-component.wasm --adapt wasi_snapshot_preview1="$COMPONENT_ADAPTER_REACTOR" sub.embed.wasm + +``` + +# Validate the component by checking its imports: +```wasm-tools component wit sub.component.wasm``` \ No newline at end of file diff --git a/component-model/examples/composition/sub/sub.go b/component-model/examples/composition/sub/sub.go new file mode 100644 index 0000000..8cb34d9 --- /dev/null +++ b/component-model/examples/composition/sub/sub.go @@ -0,0 +1,10 @@ +package main + +//go:wasm-module sub +//export docs:subtractor/sub#sub +func sub(x, y int32) int32 { + return x - y +} + +// main is required for the `wasi` target, even if it isn't used. +func main() {} diff --git a/component-model/examples/composition/sub/wit/world.wit b/component-model/examples/composition/sub/wit/world.wit new file mode 100644 index 0000000..baf7c0f --- /dev/null +++ b/component-model/examples/composition/sub/wit/world.wit @@ -0,0 +1,9 @@ +package docs:subtractor; + +interface sub { + sub: func(a: u32, b: u32) -> u32; +} + +world subtractor { + export sub; +} \ No newline at end of file diff --git a/component-model/examples/composition/wasi_snapshot_preview1.command.wasm b/component-model/examples/composition/wasi_snapshot_preview1.command.wasm new file mode 100644 index 0000000..8d048d1 Binary files /dev/null and b/component-model/examples/composition/wasi_snapshot_preview1.command.wasm differ diff --git a/component-model/examples/composition/wasi_snapshot_preview1.reactor.wasm b/component-model/examples/composition/wasi_snapshot_preview1.reactor.wasm new file mode 100644 index 0000000..f20ca71 Binary files /dev/null and b/component-model/examples/composition/wasi_snapshot_preview1.reactor.wasm differ