diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fbd40338..e115aa16 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -54,12 +54,10 @@ jobs: - name: Build WASM support (Windows) if: runner.os == 'Windows' - run: | - ./build.bat + run: ./build.bat - name: Build WASM support (Unix) if: runner.os == 'Linux' || runner.os == 'macOS' - run: | - ./build.sh + run: ./build.sh - name: Run tests run: cargo test --all --verbose diff --git a/Cargo.lock b/Cargo.lock index b079125a..c01916c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -125,6 +125,21 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b3d0060af21e8d11a926981cc00c6c1541aa91dd64b9f881985c3da1094425f" +[[package]] +name = "assert_cmd" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed72493ac66d5804837f480ab3766c72bdfab91a65e565fc54fa9e42db0073a8" +dependencies = [ + "anstyle", + "bstr", + "doc-comment", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + [[package]] name = "async-stream" version = "0.3.5" @@ -266,6 +281,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bstr" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + [[package]] name = "bumpalo" version = "3.14.0" @@ -284,6 +310,15 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +[[package]] +name = "camino" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +dependencies = [ + "serde", +] + [[package]] name = "cap-fs-ext" version = "2.0.1" @@ -361,6 +396,64 @@ dependencies = [ "winx", ] +[[package]] +name = "cargo-platform" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-pulumi" +version = "0.1.0" +dependencies = [ + "anyhow", + "assert_cmd", + "cargo_metadata 0.18.1", + "clap", + "fs_extra", + "itertools 0.12.1", + "log", + "normpath", + "petgraph", + "predicates", + "serde", + "serde_json", + "simple_logger", + "testdir", + "wasmtime", + "wasmtime-wasi", +] + +[[package]] +name = "cargo_metadata" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "cc" version = "1.0.83" @@ -435,6 +528,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -621,6 +724,15 @@ dependencies = [ "uuid", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + [[package]] name = "derivative" version = "2.2.0" @@ -638,6 +750,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7" +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "digest" version = "0.10.7" @@ -689,6 +807,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "either" version = "1.9.0" @@ -749,6 +873,15 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + [[package]] name = "fnv" version = "1.0.7" @@ -775,6 +908,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures" version = "0.3.30" @@ -1173,6 +1312,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.10" @@ -1382,6 +1530,30 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "normpath" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5831952a9476f2fed74b77d74182fa5ddc4d21c72ec45a333b250e3ed0272804" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1392,6 +1564,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-traits" version = "0.2.17" @@ -1411,6 +1589,15 @@ dependencies = [ "libc", ] +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + [[package]] name = "object" version = "0.32.2" @@ -1527,12 +1714,48 @@ version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "predicates" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" +dependencies = [ + "anstyle", + "difflib", + "float-cmp", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" + +[[package]] +name = "predicates-tree" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "prettyplease" version = "0.2.16" @@ -1966,6 +2189,9 @@ name = "semver" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +dependencies = [ + "serde", +] [[package]] name = "serde" @@ -2050,6 +2276,18 @@ dependencies = [ "dirs", ] +[[package]] +name = "simple_logger" +version = "4.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e7e46c8c90251d47d08b28b8a419ffb4aede0f87c2eea95e17d1d5bacbf3ef1" +dependencies = [ + "colored", + "log", + "time", + "windows-sys 0.48.0", +] + [[package]] name = "slab" version = "0.4.9" @@ -2136,6 +2374,20 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sysinfo" +version = "0.26.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c18a6156d1f27a9592ee18c1a846ca8dd5c258b7179fc193ae87c74ebb666f5" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "winapi", +] + [[package]] name = "system-interface" version = "0.26.1" @@ -2171,6 +2423,26 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + +[[package]] +name = "testdir" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee79e927b64d193f5abb60d20a0eb56be0ee5a242fdeb8ce3bf054177006de52" +dependencies = [ + "anyhow", + "backtrace", + "cargo_metadata 0.14.2", + "once_cell", + "sysinfo", + "whoami", +] + [[package]] name = "thiserror" version = "1.0.56" @@ -2211,6 +2483,39 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -2579,6 +2884,15 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "want" version = "0.3.1" @@ -2620,6 +2934,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" version = "0.2.90" @@ -3101,6 +3421,16 @@ dependencies = [ "wast 70.0.2", ] +[[package]] +name = "web-sys" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "which" version = "4.4.2" @@ -3113,6 +3443,17 @@ dependencies = [ "rustix", ] +[[package]] +name = "whoami" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" +dependencies = [ + "redox_syscall", + "wasite", + "web-sys", +] + [[package]] name = "wiggle" version = "18.0.3" diff --git a/Cargo.toml b/Cargo.toml index 173a152b..484480a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,8 @@ members = [ "pulumi_wasm_rust_macro", "pulumi_wasm_rust", "wasm_common", - "examples/simple" + "examples/simple", + "cargo-pulumi" ] [workspace.package] @@ -34,7 +35,7 @@ wit-bindgen-rt = "0.22.0" rmp = "0.8.12" rmp-serde = "1.1.2" rmpv = "1.0.1" -serde = "1.0.197" +serde = { version = "1.0.197", features = ["derive"] } tonic = { version = "0.11.0", default-features = false } serde_json = "1.0.114" wasmtime = "18.0.3" @@ -54,4 +55,11 @@ proc-macro2 = "1.0.79" pulumi_wasm_rust = { path = "pulumi_wasm_rust" } pulumi_wasm_rust_macro = { path = "pulumi_wasm_rust_macro" } wasm_common = { path = "wasm_common" } -pulumi_wasm_provider_random_rust = { path = "providers/pulumi_wasm_provider_random_rust" } \ No newline at end of file +pulumi_wasm_provider_random_rust = { path = "providers/pulumi_wasm_provider_random_rust" } +assert_cmd = "2.0.14" +predicates = "3.1.0" +normpath = "1.2" +simple_logger = "4.3.3" +petgraph = "0.6.4" +cargo_metadata = "0.18.1" +itertools = "0.12.1" \ No newline at end of file diff --git a/cargo-pulumi/Cargo.toml b/cargo-pulumi/Cargo.toml new file mode 100644 index 00000000..b5226419 --- /dev/null +++ b/cargo-pulumi/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "cargo-pulumi" +version.workspace = true +edition.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow.workspace = true +clap.workspace = true +serde_json.workspace = true +serde.workspace = true +normpath.workspace = true +log.workspace = true +simple_logger.workspace = true +petgraph.workspace = true +cargo_metadata.workspace = true +itertools.workspace = true + +[dev-dependencies] +assert_cmd.workspace = true +predicates.workspace = true +wasmtime.workspace = true +wasmtime-wasi.workspace = true +testdir = "0.9.1" +fs_extra = "1.3.0" \ No newline at end of file diff --git a/cargo-pulumi/src/graph.rs b/cargo-pulumi/src/graph.rs new file mode 100644 index 00000000..27068741 --- /dev/null +++ b/cargo-pulumi/src/graph.rs @@ -0,0 +1,48 @@ +// From https://github.com/sfackler/cargo-tree/blob/master/src/graph.rs +use std::collections::HashMap; + +use anyhow::Error; +use cargo_metadata::{DependencyKind, Metadata, Package, PackageId}; +use petgraph::graph::NodeIndex; +use petgraph::stable_graph::StableGraph; + +#[derive(Debug)] +pub struct Graph { + pub graph: StableGraph, + pub nodes: HashMap, + pub root: Option, +} + +pub fn build(metadata: Metadata) -> Result { + let resolve = metadata.resolve.unwrap(); + + let mut graph = Graph { + graph: StableGraph::new(), + nodes: HashMap::new(), + root: resolve.root, + }; + + for package in metadata.packages { + let id = package.id.clone(); + let index = graph.graph.add_node(package); + graph.nodes.insert(id, index); + } + + for node in resolve.nodes { + + let from = graph.nodes[&node.id]; + for dep in node.deps { + + let to = graph.nodes[&dep.pkg]; + for kind in dep.dep_kinds { + if kind.kind != DependencyKind::Normal { + continue; + } + + graph.graph.add_edge(from, to, kind.kind); + } + } + } + + Ok(graph) +} \ No newline at end of file diff --git a/cargo-pulumi/src/main.rs b/cargo-pulumi/src/main.rs new file mode 100644 index 00000000..3531f9f2 --- /dev/null +++ b/cargo-pulumi/src/main.rs @@ -0,0 +1,218 @@ +use std::collections::{HashSet, VecDeque}; +use std::path::{Path, PathBuf}; +use std::process::Command; + +use anyhow::{anyhow, Error}; +use cargo_metadata::{MetadataCommand, Package}; +use clap::Parser; +use itertools::Itertools; +use log::{debug, info}; +use normpath::PathExt; +use petgraph::visit::{Dfs, Walker}; +use crate::metadata::GetRelatedCrate; + +mod metadata; +mod graph; + +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +struct App { + #[clap(short, long)] + package: Option, +} + + +fn main() -> Result<(), Error> { + simple_logger::SimpleLogger::new().env().init().unwrap(); + + let args = App::parse(); + + let path = Path::new("./Cargo.toml"); + + if std::fs::metadata(path).is_err() { + return Err(Error::msg("Command run in directory that is not cargo project")); + } + + let path = path.normalize()?; + + let metadata = MetadataCommand::new() + .exec()?; + + let target = metadata.target_directory.clone(); + + let package = match args.package { + None => { + let package = metadata.packages.iter().find(|p| + if let Ok(p) = Path::new(&p.manifest_path).normalize() { + p == path + } else { + false + } + ); + match package { + None => return Err(Error::msg("Cannot find current package in workspace")), + Some(p) => p + } + } + Some(package_name) => { + let package = metadata.packages.iter().find(|p| p.name == package_name); + match package { + None => return Err(Error::msg(format!("Cannot find package [{package_name}] in workspace"))), + Some(p) => p + } + } + }; + let package = package.clone(); + + let g = graph::build(metadata)?; + + let node = g.nodes.get(&package.id).unwrap(); + + let dfs = Dfs::new(&g.graph, *node); + + let wasm_dependencies: Vec<_> = dfs.iter(&g.graph).flat_map(|nx| { + g.graph[nx].get_related_crate().clone() + }).collect(); + + if wasm_dependencies.is_empty() { + return Err(anyhow!("Cannot find WASM dependencies for package {}", &package.name)); + } + + debug!("WASM dependencies: {:?}", wasm_dependencies); + + let mut all_packages = HashSet::new(); + all_packages.extend([package.name.clone()]); + all_packages.extend(wasm_dependencies.iter().map(|s| s.to_string())); + + info!("Compiling {:?}", all_packages); + + compile_wasm_packages(&all_packages)?; + + let wasm_files_location = target.clone().into_std_path_buf().join("wasm32-wasi").join("debug"); + + let packages = sort_packages(&package, all_packages); + + let wasm_files = convert_package_to_location(&wasm_files_location, packages)?; + + let current_composite = combine_wasm_files(&wasm_files_location, wasm_files)?; + + let final_composite = copy_final_composite_to_composed_wasm(wasm_files_location, current_composite)?; + + info!("Final composite: {:?}", final_composite); + + Ok(()) +} + +fn convert_package_to_location(wasm_files_location: &Path, packages: Vec) -> Result, Error> { + packages.iter() + .map(|file_name| { + let wasm_file = wasm_files_location.join(format!("{}.wasm", &file_name)); + if !wasm_file.exists() { + Err(anyhow!("Cannot find WASM file for package {}. Tried {:?}", &file_name, &wasm_file)) + } else { + Ok(replace_underscores_with_dashes(&wasm_file)) + } + }).collect::, _>>() +} + +fn sort_packages(package: &Package, all_packages: HashSet) -> Vec { + + #[derive(Debug, PartialOrd, PartialEq, Eq, Ord, Hash)] + enum PackageType { + Main(String), + Provider(String), + Other(String), + } + + impl PackageType { + fn get_name(&self) -> &str { + match self { + PackageType::Main(name) => name, + PackageType::Provider(name) => name, + PackageType::Other(name) => name + } + } + } + + all_packages.iter() + .map(|p| { + if p.contains("provider") { + PackageType::Provider(p.clone()) + } else if p == &package.name { + PackageType::Main(p.clone()) + } else { + PackageType::Other(p.clone()) + } + }) + .sorted() + .map(|p| p.get_name().to_string()) + .collect() +} + +fn combine_wasm_files(wasm_files_location: &Path, packages: Vec) -> Result { + let mut packages = VecDeque::from(packages); + + info!("Packages: {:?}", packages); + + let wasm_file = match packages.pop_front() { + Some(location) => { location } + a => panic!("Expected main package, got {:?}", a) + }; + + let mut current_composite = wasm_file; + let mut i = 0; + + while let Some(package) = packages.pop_front() { + info!("Merging package: {:?}", package.file_name().unwrap()); + let composite = wasm_files_location.join(format!("composed{}.wasm", i)); + info!("Composing {:?} and {:?} into {:?}", ¤t_composite, &package, &composite); + let output = Command::new("wasm-tools") + .args(["compose", "-o", &composite.to_str().unwrap(), current_composite.to_str().unwrap(), "-d", package.to_str().unwrap()]) + .output()?; + if !output.status.success() { + return Err(anyhow!("Failed to compose {:?} and {:?}: {}", ¤t_composite, &package, String::from_utf8_lossy(&output.stderr))); + } + current_composite = composite; + i += 1; + } + Ok(current_composite) +} + +fn copy_final_composite_to_composed_wasm(wasm_files_location: PathBuf, current_composite: PathBuf) -> Result { + let final_composite = wasm_files_location.join("composed.wasm"); + + std::fs::copy(current_composite, &final_composite)?; + Ok(final_composite) +} + +fn compile_wasm_packages(all_packages: &HashSet) -> Result<(), Error> { + let binding = all_packages.iter().map(|d| vec!["-p", d]).collect::>(); + let flags = binding.iter().flatten().collect::>(); + + let output = Command::new("cargo") + .args(["component", "build"]) + .args(&flags) + .spawn()? + .wait_with_output()?; + + if !output.status.success() { + return Err(anyhow!("Failed to build {:?}: {}", all_packages, String::from_utf8_lossy(&output.stderr))); + } + Ok(()) +} + +fn replace_underscores_with_dashes(file: &Path) -> PathBuf { + if let Some(file_name) = file.file_name() { + if file_name.to_str().unwrap().contains('_') { + let new_file_name = file_name.to_str().unwrap().replace('_', "-"); + let new_file = file.with_file_name(new_file_name); + println!("{:?} -> {:?}", file, new_file); + std::fs::copy(file, &new_file).unwrap(); + new_file + } else { + file.to_path_buf() + } + } else { + file.to_path_buf() + } +} diff --git a/cargo-pulumi/src/metadata.rs b/cargo-pulumi/src/metadata.rs new file mode 100644 index 00000000..34a17eab --- /dev/null +++ b/cargo-pulumi/src/metadata.rs @@ -0,0 +1,85 @@ +pub trait GetRelatedCrate { + fn get_related_crate(&self) -> Option; +} + +impl GetRelatedCrate for cargo_metadata::Package { + fn get_related_crate(&self) -> Option { + // Check if the metadata contains an object named "pulumi" + if let Some(pulumi) = self.metadata.get("pulumi").and_then(|v| v.as_object()) { + // If it does, check if "pulumi" contains an object named "related_crate" + if let Some(related_crate) = pulumi.get("related_crate").and_then(|v| v.as_str()) { + // If it does, return the string value of "related_crate" + return Some(related_crate.to_string()); + } + } + // If either "pulumi" or "related_crate" do not exist, or if "related_crate" is not a string, return None + None + } +} + +#[cfg(test)] +mod tests { + use super::*; + use cargo_metadata::{Package}; + use serde_json::json; + + #[test] + fn get_related_crate_returns_none_when_no_metadata() { + let package = create_package(json!({})); + assert_eq!(package.get_related_crate(), None); + } + + #[test] + fn get_related_crate_returns_none_when_no_pulumi_in_metadata() { + let package = create_package(json!({"not_pulumi": {}})); + assert_eq!(package.get_related_crate(), None); + } + + #[test] + fn get_related_crate_returns_none_when_no_related_crate_in_pulumi() { + let package = create_package(json!({"pulumi": {"not_related_crate": "value"}})); + assert_eq!(package.get_related_crate(), None); + } + + #[test] + fn get_related_crate_returns_none_when_related_crate_is_not_string() { + let package = create_package(json!({"pulumi": {"related_crate": 123}})); + assert_eq!(package.get_related_crate(), None); + } + + #[test] + fn get_related_crate_returns_related_crate_when_present() { + let package = create_package(json!({"pulumi": {"related_crate": "value"}})); + assert_eq!(package.get_related_crate(), Some("value".to_string())); + } + + fn create_package(metadata: serde_json::Value) -> Package { + let package_json = json!({ + "name": "", + "version": "0.0.0", + "id": "id", + "license": serde_json::Value::Null, + "license_file": serde_json::Value::Null, + "description": serde_json::Value::Null, + "source": serde_json::Value::Null, + "dependencies": [], + "targets": [], + "features": {}, + "manifest_path": "", + "metadata": metadata, + "links": serde_json::Value::Null, + "authors": [], + "categories": [], + "keywords": [], + "readme": serde_json::Value::Null, + "repository": serde_json::Value::Null, + "homepage": serde_json::Value::Null, + "documentation": serde_json::Value::Null, + "edition": "2021", + "publish": serde_json::Value::Null, + "default_run": serde_json::Value::Null, + "rust_version": serde_json::Value::Null + }); + serde_json::from_value(package_json).unwrap() + } +} \ No newline at end of file diff --git a/cargo-pulumi/tests/fixtures/example/.gitignore b/cargo-pulumi/tests/fixtures/example/.gitignore new file mode 100644 index 00000000..183126af --- /dev/null +++ b/cargo-pulumi/tests/fixtures/example/.gitignore @@ -0,0 +1,3 @@ +target/ +*.wasm +.idea/ diff --git a/cargo-pulumi/tests/fixtures/example/Cargo.lock b/cargo-pulumi/tests/fixtures/example/Cargo.lock new file mode 100644 index 00000000..85e2cc2c --- /dev/null +++ b/cargo-pulumi/tests/fixtures/example/Cargo.lock @@ -0,0 +1,346 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "common" +version = "0.1.0" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "common_lib" +version = "0.1.0" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "example" +version = "0.1.0" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "indexmap" +version = "2.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +dependencies = [ + "equivalent", + "hashbrown", + "serde", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "main" +version = "0.1.0" +dependencies = [ + "common_lib", + "provider_a_lib", + "wit-bindgen-rt", +] + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "provider_a" +version = "0.1.0" +dependencies = [ + "common_lib", + "wit-bindgen-rt", +] + +[[package]] +name = "provider_a_lib" +version = "0.1.0" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + +[[package]] +name = "spdx" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ef1a0fa1e39ac22972c8db23ff89aea700ab96aa87114e1fb55937a631a0c9" +dependencies = [ + "smallvec", +] + +[[package]] +name = "syn" +version = "2.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +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 = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "wasm-encoder" +version = "0.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9c7d2731df60006819b013f64ccc2019691deccf6e11a1804bc850cd6748f1a" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-metadata" +version = "0.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fd83062c17b9f4985d438603cde0a5e8c5c8198201a6937f778b607924c7da2" +dependencies = [ + "anyhow", + "indexmap", + "serde", + "serde_derive", + "serde_json", + "spdx", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708" +dependencies = [ + "bitflags", + "indexmap", + "semver", +] + +[[package]] +name = "wit-bindgen" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "288f992ea30e6b5c531b52cdd5f3be81c148554b09ea416f058d16556ba92c27" +dependencies = [ + "bitflags", + "wit-bindgen-rt", + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e85e72719ffbccf279359ad071497e47eb0675fe22106dea4ed2d8a7fcb60ba4" +dependencies = [ + "anyhow", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb8738270f32a2d6739973cbbb7c1b6dd8959ce515578a6e19165853272ee64" + +[[package]] +name = "wit-bindgen-rust" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a39a15d1ae2077688213611209849cad40e9e5cccf6e61951a425850677ff3" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d376d3ae5850526dfd00d937faea0d81a06fa18f7ac1e26f386d760f241a8f4b" +dependencies = [ + "anyhow", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "421c0c848a0660a8c22e2fd217929a0191f14476b68962afd2af89fd22e39825" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196d3ecfc4b759a8573bf86a9b3f8996b304b3732e4c7de81655f875f6efdca6" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] diff --git a/cargo-pulumi/tests/fixtures/example/Cargo.toml b/cargo-pulumi/tests/fixtures/example/Cargo.toml new file mode 100644 index 00000000..c35c1378 --- /dev/null +++ b/cargo-pulumi/tests/fixtures/example/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "example" +version = "0.1.0" +edition = "2021" + +[workspace] + +members = [ + "main", + "provider_a", + "provider_a_lib", + "common", + "common_lib" +] diff --git a/cargo-pulumi/tests/fixtures/example/common/Cargo.toml b/cargo-pulumi/tests/fixtures/example/common/Cargo.toml new file mode 100644 index 00000000..59e0a278 --- /dev/null +++ b/cargo-pulumi/tests/fixtures/example/common/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "common" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wit-bindgen-rt = "0.22.0" + +[package.metadata.component] +target = { path = "../service.wit", world = "common" } diff --git a/cargo-pulumi/tests/fixtures/example/common/src/bindings.rs b/cargo-pulumi/tests/fixtures/example/common/src/bindings.rs new file mode 100644 index 00000000..146d1a67 --- /dev/null +++ b/cargo-pulumi/tests/fixtures/example/common/src/bindings.rs @@ -0,0 +1,123 @@ +// Generated by `wit-bindgen` 0.20.0. DO NOT EDIT! +// Options used: +pub mod exports { + pub mod example { + pub mod service { + + #[allow(clippy::all)] + pub mod common_interface { + #[used] + #[doc(hidden)] + #[cfg(target_arch = "wasm32")] + static __FORCE_SECTION_REF: fn() = + super::super::super::super::__link_custom_section_describing_imports; + use super::super::super::super::_rt; + + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_run_common_cabi() -> *mut u8 { + let result0 = T::run_common(); + let ptr1 = _RET_AREA.0.as_mut_ptr().cast::(); + let vec2 = (result0.into_bytes()).into_boxed_slice(); + let ptr2 = vec2.as_ptr().cast::(); + let len2 = vec2.len(); + ::core::mem::forget(vec2); + *ptr1.add(4).cast::() = len2; + *ptr1.add(0).cast::<*mut u8>() = ptr2.cast_mut(); + ptr1 + } + + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn __post_return_run_common(arg0: *mut u8) { + let l0 = *arg0.add(0).cast::<*mut u8>(); + let l1 = *arg0.add(4).cast::(); + _rt::cabi_dealloc(l0, l1, 1); + } + pub trait Guest { + fn run_common() -> _rt::String; + } + #[doc(hidden)] + + macro_rules! __export_example_service_common_interface_0_1_0_cabi{ + ($ty:ident with_types_in $($path_to_types:tt)*) => (const _: () = { + + + #[export_name = "example:service/common-interface@0.1.0#run-common"] + unsafe extern "C" fn export_run_common() -> *mut u8 { + $($path_to_types)*::_export_run_common_cabi::<$ty>() + } + + #[export_name = "cabi_post_example:service/common-interface@0.1.0#run-common"] + unsafe extern "C" fn _post_return_run_common(arg0: *mut u8,) { + $($path_to_types)*::__post_return_run_common::<$ty>(arg0) + } + };); + } + #[doc(hidden)] + pub(crate) use __export_example_service_common_interface_0_1_0_cabi; + + #[repr(align(4))] + struct _RetArea([::core::mem::MaybeUninit; 8]); + static mut _RET_AREA: _RetArea = _RetArea([::core::mem::MaybeUninit::uninit(); 8]); + } + } + } +} +mod _rt { + pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { + if size == 0 { + return; + } + let layout = alloc::Layout::from_size_align_unchecked(size, align); + alloc::dealloc(ptr as *mut u8, layout); + } + pub use alloc_crate::alloc; + pub use alloc_crate::string::String; + extern crate alloc as alloc_crate; +} + +/// Generates `#[no_mangle]` functions to export the specified type as the +/// root implementation of all generated traits. +/// +/// For more information see the documentation of `wit_bindgen::generate!`. +/// +/// ```rust +/// # macro_rules! export{ ($($t:tt)*) => (); } +/// # trait Guest {} +/// struct MyType; +/// +/// impl Guest for MyType { +/// // ... +/// } +/// +/// export!(MyType); +/// ``` +#[allow(unused_macros)] +#[doc(hidden)] + +macro_rules! __export_common_impl { + ($ty:ident) => (self::export!($ty with_types_in self);); + ($ty:ident with_types_in $($path_to_types_root:tt)*) => ( + $($path_to_types_root)*::exports::example::service::common_interface::__export_example_service_common_interface_0_1_0_cabi!($ty with_types_in $($path_to_types_root)*::exports::example::service::common_interface); + ) +} +#[doc(inline)] +pub(crate) use __export_common_impl as export; + +#[cfg(target_arch = "wasm32")] +#[link_section = "component-type:wit-bindgen:0.20.0:common:encoded world"] +#[doc(hidden)] +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 229] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07i\x01A\x02\x01A\x02\x01\ +B\x02\x01@\0\0s\x04\0\x0arun-common\x01\0\x04\x01&example:service/common-interfa\ +ce@0.1.0\x05\0\x04\x01\x1cexample:service/common@0.1.0\x04\0\x0b\x0c\x01\0\x06co\ +mmon\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x070.201.0\ +\x10wit-bindgen-rust\x060.20.0"; + +#[inline(never)] +#[doc(hidden)] +#[cfg(target_arch = "wasm32")] +pub fn __link_custom_section_describing_imports() { + wit_bindgen_rt::maybe_link_cabi_realloc(); +} diff --git a/cargo-pulumi/tests/fixtures/example/common/src/lib.rs b/cargo-pulumi/tests/fixtures/example/common/src/lib.rs new file mode 100644 index 00000000..d14f360e --- /dev/null +++ b/cargo-pulumi/tests/fixtures/example/common/src/lib.rs @@ -0,0 +1,13 @@ +use crate::bindings::exports::example::service::common_interface; + +mod bindings; +bindings::export!(Component with_types_in bindings); + +struct Component; + +impl common_interface::Guest for Component { + fn run_common() -> String { + return "run_common".to_string(); + } +} + diff --git a/cargo-pulumi/tests/fixtures/example/common_lib/Cargo.toml b/cargo-pulumi/tests/fixtures/example/common_lib/Cargo.toml new file mode 100644 index 00000000..92116897 --- /dev/null +++ b/cargo-pulumi/tests/fixtures/example/common_lib/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "common_lib" +version = "0.1.0" +edition = "2021" + +[package.metadata.pulumi] +related_crate = "common" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +wit-bindgen = "0.22.0" diff --git a/cargo-pulumi/tests/fixtures/example/common_lib/src/lib.rs b/cargo-pulumi/tests/fixtures/example/common_lib/src/lib.rs new file mode 100644 index 00000000..fa993dbd --- /dev/null +++ b/cargo-pulumi/tests/fixtures/example/common_lib/src/lib.rs @@ -0,0 +1,13 @@ +// mod bindings; + +mod bindings { + wit_bindgen::generate!({ + path: "../service.wit", + world: "common-lib" + }); +} + +pub fn run_common() -> String { + let result = crate::bindings::example::service::common_interface::run_common(); + return format!("Hello from common-lib: {}", result); +} \ No newline at end of file diff --git a/cargo-pulumi/tests/fixtures/example/config.yml b/cargo-pulumi/tests/fixtures/example/config.yml new file mode 100644 index 00000000..1f072ac2 --- /dev/null +++ b/cargo-pulumi/tests/fixtures/example/config.yml @@ -0,0 +1,4 @@ +dependencies: + # "example:service/my-resource@0.1.0": target\wasm32-wasi\debug\bimpl.wasm + "example:service/a-interface@0.1.0": target\wasm32-wasi\debug\aimpl.wasm + "example:service/b-interface@0.1.0": target\wasm32-wasi\debug\bimpl.wasm \ No newline at end of file diff --git a/cargo-pulumi/tests/fixtures/example/main/Cargo.toml b/cargo-pulumi/tests/fixtures/example/main/Cargo.toml new file mode 100644 index 00000000..b62a455b --- /dev/null +++ b/cargo-pulumi/tests/fixtures/example/main/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "main" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wit-bindgen-rt = "0.22.0" +provider_a_lib = { path = "../provider_a_lib" } +common_lib = { path = "../common_lib" } + +[package.metadata.component] +target = { path = "../service.wit", world = "main" } diff --git a/cargo-pulumi/tests/fixtures/example/main/src/bindings.rs b/cargo-pulumi/tests/fixtures/example/main/src/bindings.rs new file mode 100644 index 00000000..489b534e --- /dev/null +++ b/cargo-pulumi/tests/fixtures/example/main/src/bindings.rs @@ -0,0 +1,209 @@ +// Generated by `wit-bindgen` 0.20.0. DO NOT EDIT! +// Options used: +pub mod example { + pub mod service { + + #[allow(clippy::all)] + pub mod a_interface { + #[used] + #[doc(hidden)] + #[cfg(target_arch = "wasm32")] + static __FORCE_SECTION_REF: fn() = + super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + #[allow(unused_unsafe, clippy::all)] + pub fn run_a() -> _rt::String { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 8]); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "example:service/a-interface@0.1.0")] + extern "C" { + #[link_name = "run-a"] + fn wit_import(_: *mut u8); + } + + #[cfg(not(target_arch = "wasm32"))] + fn wit_import(_: *mut u8) { + unreachable!() + } + wit_import(ptr0); + let l1 = *ptr0.add(0).cast::<*mut u8>(); + let l2 = *ptr0.add(4).cast::(); + let len3 = l2; + let bytes3 = _rt::Vec::from_raw_parts(l1.cast(), len3, len3); + _rt::string_lift(bytes3) + } + } + } + + #[allow(clippy::all)] + pub mod common_interface { + #[used] + #[doc(hidden)] + #[cfg(target_arch = "wasm32")] + static __FORCE_SECTION_REF: fn() = + super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + #[allow(unused_unsafe, clippy::all)] + pub fn run_common() -> _rt::String { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 8]); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "example:service/common-interface@0.1.0")] + extern "C" { + #[link_name = "run-common"] + fn wit_import(_: *mut u8); + } + + #[cfg(not(target_arch = "wasm32"))] + fn wit_import(_: *mut u8) { + unreachable!() + } + wit_import(ptr0); + let l1 = *ptr0.add(0).cast::<*mut u8>(); + let l2 = *ptr0.add(4).cast::(); + let len3 = l2; + let bytes3 = _rt::Vec::from_raw_parts(l1.cast(), len3, len3); + _rt::string_lift(bytes3) + } + } + } + } +} +pub mod exports { + pub mod example { + pub mod service { + + #[allow(clippy::all)] + pub mod main_interface { + #[used] + #[doc(hidden)] + #[cfg(target_arch = "wasm32")] + static __FORCE_SECTION_REF: fn() = + super::super::super::super::__link_custom_section_describing_imports; + use super::super::super::super::_rt; + + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_main_cabi() -> *mut u8 { + let result0 = T::main(); + let ptr1 = _RET_AREA.0.as_mut_ptr().cast::(); + let vec2 = (result0.into_bytes()).into_boxed_slice(); + let ptr2 = vec2.as_ptr().cast::(); + let len2 = vec2.len(); + ::core::mem::forget(vec2); + *ptr1.add(4).cast::() = len2; + *ptr1.add(0).cast::<*mut u8>() = ptr2.cast_mut(); + ptr1 + } + + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn __post_return_main(arg0: *mut u8) { + let l0 = *arg0.add(0).cast::<*mut u8>(); + let l1 = *arg0.add(4).cast::(); + _rt::cabi_dealloc(l0, l1, 1); + } + pub trait Guest { + fn main() -> _rt::String; + } + #[doc(hidden)] + + macro_rules! __export_example_service_main_interface_0_1_0_cabi{ + ($ty:ident with_types_in $($path_to_types:tt)*) => (const _: () = { + + + #[export_name = "example:service/main-interface@0.1.0#main"] + unsafe extern "C" fn export_main() -> *mut u8 { + $($path_to_types)*::_export_main_cabi::<$ty>() + } + + #[export_name = "cabi_post_example:service/main-interface@0.1.0#main"] + unsafe extern "C" fn _post_return_main(arg0: *mut u8,) { + $($path_to_types)*::__post_return_main::<$ty>(arg0) + } + };); + } + #[doc(hidden)] + pub(crate) use __export_example_service_main_interface_0_1_0_cabi; + + #[repr(align(4))] + struct _RetArea([::core::mem::MaybeUninit; 8]); + static mut _RET_AREA: _RetArea = _RetArea([::core::mem::MaybeUninit::uninit(); 8]); + } + } + } +} +mod _rt { + pub use alloc_crate::string::String; + pub use alloc_crate::vec::Vec; + pub unsafe fn string_lift(bytes: Vec) -> String { + if cfg!(debug_assertions) { + String::from_utf8(bytes).unwrap() + } else { + String::from_utf8_unchecked(bytes) + } + } + pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { + if size == 0 { + return; + } + let layout = alloc::Layout::from_size_align_unchecked(size, align); + alloc::dealloc(ptr as *mut u8, layout); + } + extern crate alloc as alloc_crate; + pub use alloc_crate::alloc; +} + +/// Generates `#[no_mangle]` functions to export the specified type as the +/// root implementation of all generated traits. +/// +/// For more information see the documentation of `wit_bindgen::generate!`. +/// +/// ```rust +/// # macro_rules! export{ ($($t:tt)*) => (); } +/// # trait Guest {} +/// struct MyType; +/// +/// impl Guest for MyType { +/// // ... +/// } +/// +/// export!(MyType); +/// ``` +#[allow(unused_macros)] +#[doc(hidden)] + +macro_rules! __export_main_impl { + ($ty:ident) => (self::export!($ty with_types_in self);); + ($ty:ident with_types_in $($path_to_types_root:tt)*) => ( + $($path_to_types_root)*::exports::example::service::main_interface::__export_example_service_main_interface_0_1_0_cabi!($ty with_types_in $($path_to_types_root)*::exports::example::service::main_interface); + ) +} +#[doc(inline)] +pub(crate) use __export_main_impl as export; + +#[cfg(target_arch = "wasm32")] +#[link_section = "component-type:wit-bindgen:0.20.0:main:encoded world"] +#[doc(hidden)] +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 340] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xd9\x01\x01A\x02\x01\ +A\x06\x01B\x02\x01@\0\0s\x04\0\x05run-a\x01\0\x03\x01!example:service/a-interfac\ +e@0.1.0\x05\0\x01B\x02\x01@\0\0s\x04\0\x0arun-common\x01\0\x03\x01&example:servi\ +ce/common-interface@0.1.0\x05\x01\x01B\x02\x01@\0\0s\x04\0\x04main\x01\0\x04\x01\ +$example:service/main-interface@0.1.0\x05\x02\x04\x01\x1aexample:service/main@0.\ +1.0\x04\0\x0b\x0a\x01\0\x04main\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0d\ +wit-component\x070.201.0\x10wit-bindgen-rust\x060.20.0"; + +#[inline(never)] +#[doc(hidden)] +#[cfg(target_arch = "wasm32")] +pub fn __link_custom_section_describing_imports() { + wit_bindgen_rt::maybe_link_cabi_realloc(); +} diff --git a/cargo-pulumi/tests/fixtures/example/main/src/lib.rs b/cargo-pulumi/tests/fixtures/example/main/src/lib.rs new file mode 100644 index 00000000..b7d563e7 --- /dev/null +++ b/cargo-pulumi/tests/fixtures/example/main/src/lib.rs @@ -0,0 +1,15 @@ +use crate::bindings::exports::example::service::main_interface::Guest; + +mod bindings; + +struct Component; + +impl Guest for Component { + fn main() -> String { + let res1 = provider_a_lib::run_a(); + let res2 = common_lib::run_common(); + format!("Hello from main: [{}] [{}]", res1, res2) + } +} + +bindings::export!(Component with_types_in bindings); \ No newline at end of file diff --git a/cargo-pulumi/tests/fixtures/example/provider_a/Cargo.toml b/cargo-pulumi/tests/fixtures/example/provider_a/Cargo.toml new file mode 100644 index 00000000..9667b5e8 --- /dev/null +++ b/cargo-pulumi/tests/fixtures/example/provider_a/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "provider_a" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wit-bindgen-rt = "0.22.0" +common_lib = { path = "../common_lib" } + +[package.metadata.component] +target = { path = "../service.wit", world = "provider-a" } diff --git a/cargo-pulumi/tests/fixtures/example/provider_a/src/bindings.rs b/cargo-pulumi/tests/fixtures/example/provider_a/src/bindings.rs new file mode 100644 index 00000000..c10c031f --- /dev/null +++ b/cargo-pulumi/tests/fixtures/example/provider_a/src/bindings.rs @@ -0,0 +1,172 @@ +// Generated by `wit-bindgen` 0.20.0. DO NOT EDIT! +// Options used: +pub mod example { + pub mod service { + + #[allow(clippy::all)] + pub mod common_interface { + #[used] + #[doc(hidden)] + #[cfg(target_arch = "wasm32")] + static __FORCE_SECTION_REF: fn() = + super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + #[allow(unused_unsafe, clippy::all)] + pub fn run_common() -> _rt::String { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 8]); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "example:service/common-interface@0.1.0")] + extern "C" { + #[link_name = "run-common"] + fn wit_import(_: *mut u8); + } + + #[cfg(not(target_arch = "wasm32"))] + fn wit_import(_: *mut u8) { + unreachable!() + } + wit_import(ptr0); + let l1 = *ptr0.add(0).cast::<*mut u8>(); + let l2 = *ptr0.add(4).cast::(); + let len3 = l2; + let bytes3 = _rt::Vec::from_raw_parts(l1.cast(), len3, len3); + _rt::string_lift(bytes3) + } + } + } + } +} +pub mod exports { + pub mod example { + pub mod service { + + #[allow(clippy::all)] + pub mod a_interface { + #[used] + #[doc(hidden)] + #[cfg(target_arch = "wasm32")] + static __FORCE_SECTION_REF: fn() = + super::super::super::super::__link_custom_section_describing_imports; + use super::super::super::super::_rt; + + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_run_a_cabi() -> *mut u8 { + let result0 = T::run_a(); + let ptr1 = _RET_AREA.0.as_mut_ptr().cast::(); + let vec2 = (result0.into_bytes()).into_boxed_slice(); + let ptr2 = vec2.as_ptr().cast::(); + let len2 = vec2.len(); + ::core::mem::forget(vec2); + *ptr1.add(4).cast::() = len2; + *ptr1.add(0).cast::<*mut u8>() = ptr2.cast_mut(); + ptr1 + } + + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn __post_return_run_a(arg0: *mut u8) { + let l0 = *arg0.add(0).cast::<*mut u8>(); + let l1 = *arg0.add(4).cast::(); + _rt::cabi_dealloc(l0, l1, 1); + } + pub trait Guest { + fn run_a() -> _rt::String; + } + #[doc(hidden)] + + macro_rules! __export_example_service_a_interface_0_1_0_cabi{ + ($ty:ident with_types_in $($path_to_types:tt)*) => (const _: () = { + + + #[export_name = "example:service/a-interface@0.1.0#run-a"] + unsafe extern "C" fn export_run_a() -> *mut u8 { + $($path_to_types)*::_export_run_a_cabi::<$ty>() + } + + #[export_name = "cabi_post_example:service/a-interface@0.1.0#run-a"] + unsafe extern "C" fn _post_return_run_a(arg0: *mut u8,) { + $($path_to_types)*::__post_return_run_a::<$ty>(arg0) + } + };); + } + #[doc(hidden)] + pub(crate) use __export_example_service_a_interface_0_1_0_cabi; + + #[repr(align(4))] + struct _RetArea([::core::mem::MaybeUninit; 8]); + static mut _RET_AREA: _RetArea = _RetArea([::core::mem::MaybeUninit::uninit(); 8]); + } + } + } +} +mod _rt { + pub use alloc_crate::string::String; + pub use alloc_crate::vec::Vec; + pub unsafe fn string_lift(bytes: Vec) -> String { + if cfg!(debug_assertions) { + String::from_utf8(bytes).unwrap() + } else { + String::from_utf8_unchecked(bytes) + } + } + pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { + if size == 0 { + return; + } + let layout = alloc::Layout::from_size_align_unchecked(size, align); + alloc::dealloc(ptr as *mut u8, layout); + } + extern crate alloc as alloc_crate; + pub use alloc_crate::alloc; +} + +/// Generates `#[no_mangle]` functions to export the specified type as the +/// root implementation of all generated traits. +/// +/// For more information see the documentation of `wit_bindgen::generate!`. +/// +/// ```rust +/// # macro_rules! export{ ($($t:tt)*) => (); } +/// # trait Guest {} +/// struct MyType; +/// +/// impl Guest for MyType { +/// // ... +/// } +/// +/// export!(MyType); +/// ``` +#[allow(unused_macros)] +#[doc(hidden)] + +macro_rules! __export_provider_a_impl { + ($ty:ident) => (self::export!($ty with_types_in self);); + ($ty:ident with_types_in $($path_to_types_root:tt)*) => ( + $($path_to_types_root)*::exports::example::service::a_interface::__export_example_service_a_interface_0_1_0_cabi!($ty with_types_in $($path_to_types_root)*::exports::example::service::a_interface); + ) +} +#[doc(inline)] +pub(crate) use __export_provider_a_impl as export; + +#[cfg(target_arch = "wasm32")] +#[link_section = "component-type:wit-bindgen:0.20.0:provider-a:encoded world"] +#[doc(hidden)] +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 294] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xa5\x01\x01A\x02\x01\ +A\x04\x01B\x02\x01@\0\0s\x04\0\x0arun-common\x01\0\x03\x01&example:service/commo\ +n-interface@0.1.0\x05\0\x01B\x02\x01@\0\0s\x04\0\x05run-a\x01\0\x04\x01!example:\ +service/a-interface@0.1.0\x05\x01\x04\x01\x20example:service/provider-a@0.1.0\x04\ +\0\x0b\x10\x01\0\x0aprovider-a\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0d\ +wit-component\x070.201.0\x10wit-bindgen-rust\x060.20.0"; + +#[inline(never)] +#[doc(hidden)] +#[cfg(target_arch = "wasm32")] +pub fn __link_custom_section_describing_imports() { + wit_bindgen_rt::maybe_link_cabi_realloc(); +} diff --git a/cargo-pulumi/tests/fixtures/example/provider_a/src/lib.rs b/cargo-pulumi/tests/fixtures/example/provider_a/src/lib.rs new file mode 100644 index 00000000..d8b4983b --- /dev/null +++ b/cargo-pulumi/tests/fixtures/example/provider_a/src/lib.rs @@ -0,0 +1,14 @@ +use crate::bindings::exports::example::service::a_interface::Guest; + +mod bindings; + +struct Component; + +impl Guest for Component { + fn run_a() -> String { + let result = common_lib::run_common(); + return format!("Hello from provider-a: {}", result); + } +} + +bindings::export!(Component with_types_in bindings); \ No newline at end of file diff --git a/cargo-pulumi/tests/fixtures/example/provider_a_lib/Cargo.toml b/cargo-pulumi/tests/fixtures/example/provider_a_lib/Cargo.toml new file mode 100644 index 00000000..efc38b10 --- /dev/null +++ b/cargo-pulumi/tests/fixtures/example/provider_a_lib/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "provider_a_lib" +version = "0.1.0" +edition = "2021" + +[package.metadata.pulumi] +related_crate = "provider_a" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/referen + +[dependencies] +wit-bindgen = "0.22.0" diff --git a/cargo-pulumi/tests/fixtures/example/provider_a_lib/src/lib.rs b/cargo-pulumi/tests/fixtures/example/provider_a_lib/src/lib.rs new file mode 100644 index 00000000..bbf250ab --- /dev/null +++ b/cargo-pulumi/tests/fixtures/example/provider_a_lib/src/lib.rs @@ -0,0 +1,13 @@ +// mod bindings; + +mod bindings { + wit_bindgen::generate!({ + path: "../service.wit", + world: "provider-a-lib" + }); +} + +pub fn run_a() -> String { + let result = crate::bindings::example::service::a_interface::run_a(); + return format!("Hello from provider-a-lib: {}", result); +} \ No newline at end of file diff --git a/cargo-pulumi/tests/fixtures/example/runner.bat b/cargo-pulumi/tests/fixtures/example/runner.bat new file mode 100644 index 00000000..4dbc46e4 --- /dev/null +++ b/cargo-pulumi/tests/fixtures/example/runner.bat @@ -0,0 +1,8 @@ +cargo component build -p main -p aimpl -p bimpl || exit 1 + +wasm-tools compose -o target/wasm32-wasi/debug/composed1.wasm target/wasm32-wasi/debug/main.wasm -d target/wasm32-wasi/debug/aimpl.wasm || exit 1 +wasm-tools compose -o target/wasm32-wasi/debug/composed2.wasm target/wasm32-wasi/debug/composed1.wasm -d target/wasm32-wasi/debug/bimpl.wasm || exit 1 + +wasm-tools component wit target/wasm32-wasi/debug/composed2.wasm || exit 1 + +echo "DONE" \ No newline at end of file diff --git a/cargo-pulumi/tests/fixtures/example/service.wit b/cargo-pulumi/tests/fixtures/example/service.wit new file mode 100644 index 00000000..ab5f6975 --- /dev/null +++ b/cargo-pulumi/tests/fixtures/example/service.wit @@ -0,0 +1,40 @@ +package example:service@0.1.0; + +world runner { + export main-interface; +} + +world main { + export main-interface; + import a-interface; + import common-interface; +} + +world provider-a { + export a-interface; + import common-interface; +} + +world provider-a-lib { + import a-interface; +} + +world common { + export common-interface; +} + +world common-lib { + import common-interface; +} + +interface main-interface { + main: func() -> string; +} + +interface a-interface { + run-a: func() -> string; +} + +interface common-interface { + run-common: func() -> string; +} diff --git a/cargo-pulumi/tests/fixtures/example/src/bindings.rs b/cargo-pulumi/tests/fixtures/example/src/bindings.rs new file mode 100644 index 00000000..2b08204e --- /dev/null +++ b/cargo-pulumi/tests/fixtures/example/src/bindings.rs @@ -0,0 +1,18 @@ +// Generated by `wit-bindgen` 0.20.0. DO NOT EDIT! +// Options used: + +#[cfg(target_arch = "wasm32")] +#[link_section = "component-type:wit-bindgen:0.20.0:example:encoded world"] +#[doc(hidden)] +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 161] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07$\x01A\x02\x01A\0\x04\ +\x01\x19component:example/example\x04\0\x0b\x0d\x01\0\x07example\x03\0\0\0G\x09p\ +roducers\x01\x0cprocessed-by\x02\x0dwit-component\x070.201.0\x10wit-bindgen-rust\ +\x060.20.0"; + +#[inline(never)] +#[doc(hidden)] +#[cfg(target_arch = "wasm32")] +pub fn __link_custom_section_describing_imports() { + wit_bindgen_rt::maybe_link_cabi_realloc(); +} diff --git a/cargo-pulumi/tests/fixtures/example/src/lib.rs b/cargo-pulumi/tests/fixtures/example/src/lib.rs new file mode 100644 index 00000000..e7a11a96 --- /dev/null +++ b/cargo-pulumi/tests/fixtures/example/src/lib.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/cargo-pulumi/tests/fixtures/no_project/.gitkeep b/cargo-pulumi/tests/fixtures/no_project/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/cargo-pulumi/tests/test.rs b/cargo-pulumi/tests/test.rs new file mode 100644 index 00000000..1a714dca --- /dev/null +++ b/cargo-pulumi/tests/test.rs @@ -0,0 +1,143 @@ +use std::process::Command; +use assert_cmd::prelude::*; // Add methods on commands + +use anyhow::{Error}; +use fs_extra::dir::CopyOptions; +use wasmtime::component::{Component, Linker, ResourceTable}; +use wasmtime::Store; +use wasmtime_wasi::preview2::WasiCtx; +use wasmtime_wasi::preview2::WasiCtxBuilder; +use wasmtime_wasi::preview2::WasiView; +use crate::server::Runner; + +mod server { + wasmtime::component::bindgen!({ + path: "tests/fixtures/example/service.wit", + world: "runner" + }); +} + +#[test] +fn errors_out_when_cargo_toml_not_available() -> Result<(), Error> { + let s = Command::cargo_bin("cargo-pulumi")? + .current_dir("tests/fixtures/no_project") + .assert() + .failure() + .to_string(); + + assert!(s.contains("Command run in directory that is not cargo project"), "Output was: {}", s); + Ok(()) +} + +#[test] +fn errors_out_in_invalid_package() -> Result<(), Error> { + let dir = testdir::testdir!(); + fs_extra::dir::copy("tests/fixtures/example", &dir, &CopyOptions::new())?; + + let s = Command::cargo_bin("cargo-pulumi")? + .args(["-p", "invalid_package"]) + .current_dir(dir.join("example")) + .assert() + .failure() + .to_string(); + + assert!(s.contains("Cannot find package [invalid_package] in workspace"), "Output was: {}", s); + Ok(()) +} + +#[test] +fn run_from_subdirectory() -> Result<(), Error> { + let dir = testdir::testdir!(); + fs_extra::dir::copy("tests/fixtures/example", &dir, &CopyOptions::new())?; + + Command::new("cargo") + .args(["component", "build", "-p", "common", "-p", "provider_a", "-p", "main"]) + .current_dir(dir.join("example")) + .assert() + .success(); + + Command::cargo_bin("cargo-pulumi")? + .current_dir(dir.join("example").join("main")) + .assert() + .success(); + + let (mut store, plugin) = create_engine(dir.join("example/target/wasm32-wasi/debug/composed.wasm").to_str().unwrap())?; + let result = plugin.example_service_main_interface().call_main(&mut store)?; + + assert_eq!(result, "Hello from main: [Hello from provider-a-lib: Hello from provider-a: Hello from common-lib: run_common] [Hello from common-lib: run_common]".to_string()); + + Ok(()) +} + +#[test] +fn run_from_main_directory() -> Result<(), Error> { + let dir = testdir::testdir!(); + fs_extra::dir::copy("tests/fixtures/example", &dir, &CopyOptions::new())?; + + Command::new("cargo") + .args(["component", "build", "-p", "common", "-p", "provider_a", "-p", "main"]) + .current_dir(dir.join("example")) + .assert() + .success(); + + Command::cargo_bin("cargo-pulumi")? + .args(["-p", "main"]) + .current_dir(dir.join("example")) + .assert() + .success(); + + let (mut store, plugin) = create_engine(dir.join("example/target/wasm32-wasi/debug/composed.wasm").to_str().unwrap())?; + let result = plugin.example_service_main_interface().call_main(&mut store)?; + + assert_eq!(result, "Hello from main: [Hello from provider-a-lib: Hello from provider-a: Hello from common-lib: run_common] [Hello from common-lib: run_common]".to_string()); + + Ok(()) +} + +struct SimplePluginCtx { + table: ResourceTable, + context: WasiCtx, +} + +impl WasiView for SimplePluginCtx { + fn table(&mut self) -> &mut ResourceTable { + &mut self.table + } + + fn ctx(&mut self) -> &mut WasiCtx { + &mut self.context + } +} + +fn create_engine(file: &str) -> Result<(Store, Runner), Error> { + let mut engine_config = wasmtime::Config::new(); + engine_config.wasm_component_model(true); + engine_config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable); + engine_config.debug_info(true); + + let engine = wasmtime::Engine::new(&engine_config).unwrap(); + + let mut linker: Linker = Linker::new(&engine); + + wasmtime_wasi::preview2::command::sync::add_to_linker(&mut linker).unwrap(); + + let table = ResourceTable::new(); + + let wasi_ctx = WasiCtxBuilder::new() + .inherit_stdin() + .inherit_stdout() + .build(); + + let mut store = Store::new( + &engine, + SimplePluginCtx { + table, + context: wasi_ctx + }, + ); + + let component = Component::from_file(&engine, file)?; + + let (plugin, _instance) = Runner::instantiate(&mut store, &component, &linker)?; + Ok((store, plugin)) +} diff --git a/config.yml b/config.yml new file mode 100644 index 00000000..5ba2c8e1 --- /dev/null +++ b/config.yml @@ -0,0 +1,14 @@ +#instantiations: +# component:pulumi-wasm/pulumi-provider-random-interface@0.1.0: +# dependency: pulumi-wasm-provider-random +#definitions: +# - 'target\wasm32-wasi\debug\pulumi-wasm-provider-random.wasm' +# - 'target\wasm32-wasi\debug\pulumi-wasm.wasm' + +dependencies: + component:pulumi-wasm/pulumi-provider-random-interface@0.1.0: 'target\wasm32-wasi\debug\pulumi-wasm-provider-random.wasm' +# component:pulumi-wasm/register-interface@0.1.0: 'target\wasm32-wasi\debug\pulumi-wasm.wasm' +# component:pulumi-wasm/output-interface@0.1.0: 'target\wasm32-wasi\debug\pulumi-wasm.wasm' +# component:pulumi-wasm/function-reverse-callback@0.1.0: 'target\wasm32-wasi\debug\pulumi-wasm.wasm' +#definitions: +# - 'target\wasm32-wasi\debug\pulumi-wasm.wasm' \ No newline at end of file diff --git a/docs/cargo-pulumi.md b/docs/cargo-pulumi.md new file mode 100644 index 00000000..fe3faa03 --- /dev/null +++ b/docs/cargo-pulumi.md @@ -0,0 +1,5 @@ +## cargo pulumi + +Command that compiles and combines Pulumi WASM components into a single WASM file. You can either run it from workspace +subdirectory using `cargo pulumi` or from root using `cargo pulumi -p `. In both cases file named `composed.wasm` +will be created in `target/wasm32-wasi/debug` directory. \ No newline at end of file diff --git a/docs/crates.md b/docs/crates.md index e6b66562..548466c9 100644 --- a/docs/crates.md +++ b/docs/crates.md @@ -32,6 +32,10 @@ after [#5](https://github.com/andrzejressel/pulumi-wasm/issues/5) generated. Rust library that provides a high-level and typesafe API for `Pulumi WASM Provider Random` WASM component. Currently handwritten - after [#5](https://github.com/andrzejressel/pulumi-wasm/issues/5) generated. +### cargo pulumi + +Cargo subcommands that compile and combine Pulumi WASM components into a single WASM file. + ### examples/simple Currently the only example. It's a simple Pulumi program that uses `Pulumi WASM Provider Random Rust` to generate random numbers. diff --git a/providers/pulumi_wasm_provider_random/src/bindings.rs b/providers/pulumi_wasm_provider_random/src/bindings.rs index c335bad2..be35eae6 100644 --- a/providers/pulumi_wasm_provider_random/src/bindings.rs +++ b/providers/pulumi_wasm_provider_random/src/bindings.rs @@ -407,6 +407,10 @@ pub mod exports { use super::super::super::super::_rt; pub type Output = super::super::super::super::component::pulumi_wasm::output_interface::Output; + /// variant either-u32 { + /// literal(option), + /// res(option>), + /// } pub struct RandomStringArgs<'a> { pub name: _rt::String, pub length: &'a Output, @@ -640,8 +644,8 @@ pub(crate) use __export_pulumi_provider_random_impl as export; #[cfg(target_arch = "wasm32")] #[link_section = "component-type:wit-bindgen:0.20.0:pulumi-provider-random:encoded world"] #[doc(hidden)] -pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1073] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xa4\x07\x01A\x02\x01\ +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1030] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xf9\x06\x01A\x02\x01\ A\x07\x01B\x16\x04\0\x06output\x03\x01\x01p}\x01i\0\x01@\x01\x05value\x01\0\x02\x04\ \0\x13[constructor]output\x01\x03\x01h\0\x01@\x02\x04self\x04\x0dfunction-names\0\ \x02\x04\0\x12[method]output.map\x01\x05\x01k\x01\x01@\x01\x04self\x04\0\x06\x04\ @@ -655,15 +659,14 @@ tput-interface@0.1.0\x05\0\x02\x03\0\0\x06output\x01B\x0b\x02\x03\x02\x01\x01\x0 ld\x03\0\x03\x01p\x04\x01r\x03\x04types\x04names\x06object\x05\x04\0\x19register\ -resource-request\x03\0\x06\x01i\x01\x01@\x01\x07request\x07\0\x08\x04\0\x08regi\ ster\x01\x09\x03\x01.component:pulumi-wasm/register-interface@0.1.0\x05\x02\x01B\ -\x10\x02\x03\x02\x01\x01\x04\0\x06output\x03\0\0\x01ky\x01h\x01\x01k\x03\x01q\x02\ -\x07literal\x01\x02\0\x03res\x01\x04\0\x04\0\x0aeither-u32\x03\0\x05\x01r\x02\x04\ -names\x06length\x03\x04\0\x12random-string-args\x03\0\x07\x01i\x01\x01r\x01\x06r\ -esult\x09\x04\0\x14random-string-result\x03\0\x0a\x01@\x01\x04args\x08\0\x0b\x04\ -\0\x14create-random-string\x01\x0c\x01@\0\x01\0\x04\0\x10handle-functions\x01\x0d\ -\x04\x01 Result { use crate::server::exports::component::pulumi_wasm::function_reverse_callback::FunctionInvocationResult; - use std::borrow::BorrowMut; let functions = plugin .component_pulumi_wasm_function_reverse_callback() diff --git a/pulumi_wasm_runner/src/pulumi.rs b/pulumi_wasm_runner/src/pulumi.rs index 612913ea..ef7e982e 100644 --- a/pulumi_wasm_runner/src/pulumi.rs +++ b/pulumi_wasm_runner/src/pulumi.rs @@ -4,7 +4,6 @@ use std::rc::Rc; use anyhow::Error; use async_trait::async_trait; -use log::Log; use prost::Message; use wasmtime::component::{Component, Instance, Linker, ResourceTable}; use wasmtime::Store; diff --git a/pulumi_wasm_rust/Cargo.toml b/pulumi_wasm_rust/Cargo.toml index b92b733b..a4adbc0e 100644 --- a/pulumi_wasm_rust/Cargo.toml +++ b/pulumi_wasm_rust/Cargo.toml @@ -3,6 +3,9 @@ name = "pulumi_wasm_rust" version.workspace = true edition.workspace = true +[package.metadata.pulumi] +related_crate = "pulumi_wasm" + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/wits/world.wit b/wits/world.wit index 0dd7e4a4..fabf6706 100644 --- a/wits/world.wit +++ b/wits/world.wit @@ -121,10 +121,10 @@ interface pulumi-provider-random-interface { use output-interface.{output}; - variant either-u32 { - literal(option), - res(option>), - } +// variant either-u32 { +// literal(option), +// res(option>), +// } record random-string-args { name: string,