From 550fc65e031781d9443056b95b37b56b3f647627 Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Wed, 28 Feb 2024 17:00:12 +0100 Subject: [PATCH 1/2] Update cargo component deps automatically --- wasm-rpc-stubgen/src/cargo.rs | 73 ++++++++++++++++++++++++----- wasm-rpc-stubgen/src/compilation.rs | 14 ++++++ wasm-rpc-stubgen/src/main.rs | 31 +++++++++++- wasm-rpc-stubgen/src/wit.rs | 13 ++++- 4 files changed, 117 insertions(+), 14 deletions(-) diff --git a/wasm-rpc-stubgen/src/cargo.rs b/wasm-rpc-stubgen/src/cargo.rs index 780352ad..dda2eefc 100644 --- a/wasm-rpc-stubgen/src/cargo.rs +++ b/wasm-rpc-stubgen/src/cargo.rs @@ -13,36 +13,38 @@ // limitations under the License. use crate::stub::StubDefinition; -use anyhow::{anyhow, bail}; +use crate::wit; +use anyhow::{anyhow, bail, Context}; use cargo_toml::{ Dependency, DependencyDetail, DepsSet, Edition, Inheritable, LtoSetting, Manifest, Profile, Profiles, StripSetting, }; use golem_wasm_rpc::WASM_RPC_VERSION; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, HashMap, HashSet}; use std::fs; +use std::path::Path; use toml::Value; -#[derive(Serialize, Default)] +#[derive(Serialize, Deserialize, Default)] struct MetadataRoot { component: Option, } -#[derive(Serialize)] +#[derive(Serialize, Deserialize)] struct ComponentMetadata { - package: String, + package: Option, target: ComponentTarget, } -#[derive(Serialize)] +#[derive(Serialize, Deserialize)] struct ComponentTarget { - world: String, + world: Option, path: String, dependencies: HashMap, } -#[derive(Serialize)] +#[derive(Serialize, Deserialize)] struct WitDependency { path: String, } @@ -91,12 +93,12 @@ pub fn generate_cargo_toml(def: &StubDefinition) -> anyhow::Result<()> { let metadata = MetadataRoot { component: Some(ComponentMetadata { - package: format!( + package: Some(format!( "{}:{}", def.root_package_name.namespace, def.root_package_name.name - ), + )), target: ComponentTarget { - world: def.target_world_name()?, + world: Some(def.target_world_name()?), path: "wit".to_string(), dependencies: wit_dependencies, }, @@ -168,3 +170,52 @@ pub fn generate_cargo_toml(def: &StubDefinition) -> anyhow::Result<()> { fs::write(def.target_cargo_path(), cargo_toml)?; Ok(()) } + +pub fn is_cargo_component_toml(path: &Path) -> anyhow::Result { + let manifest: Manifest = Manifest::from_path_with_metadata(path)?; + + if let Some(package) = manifest.package { + if let Some(metadata) = package.metadata { + if metadata.component.is_some() { + return Ok(true); + } + } + } + + Ok(false) +} + +pub fn add_dependencies_to_cargo_toml(cargo_path: &Path, names: &[String]) -> anyhow::Result<()> { + let mut manifest: Manifest = Manifest::from_path_with_metadata(cargo_path)?; + if let Some(ref mut package) = manifest.package { + if let Some(ref mut metadata) = package.metadata { + if let Some(ref mut component) = metadata.component { + let existing: HashSet<_> = component.target.dependencies.keys().cloned().collect(); + for name in names { + if !existing.contains(name) { + let relative_path = format!("wit/deps/{}", name); + let path = cargo_path + .parent() + .context("Parent directory of Cargo.toml")? + .join(&relative_path); + let package_name = wit::get_package_name(&path)?; + + component.target.dependencies.insert( + format!("{}:{}", package_name.namespace, package_name.name), + WitDependency { + path: relative_path, + }, + ); + } + } + + let cargo_toml = toml::to_string(&manifest)?; + + println!("Writing updated Cargo.toml to {:?}", cargo_path); + fs::write(cargo_path, cargo_toml)?; + } + } + } + + Ok(()) +} diff --git a/wasm-rpc-stubgen/src/compilation.rs b/wasm-rpc-stubgen/src/compilation.rs index 30358380..fc1c917b 100644 --- a/wasm-rpc-stubgen/src/compilation.rs +++ b/wasm-rpc-stubgen/src/compilation.rs @@ -1,3 +1,17 @@ +// Copyright 2024 Golem Cloud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use cargo_component::config::{CargoArguments, Config}; use cargo_component::{load_component_metadata, load_metadata, run_cargo_command}; use cargo_component_core::terminal::{Color, Terminal, Verbosity}; diff --git a/wasm-rpc-stubgen/src/main.rs b/wasm-rpc-stubgen/src/main.rs index f86e6cf5..b7ebbb5d 100644 --- a/wasm-rpc-stubgen/src/main.rs +++ b/wasm-rpc-stubgen/src/main.rs @@ -23,7 +23,7 @@ use crate::compilation::compile; use crate::rust::generate_stub_source; use crate::stub::StubDefinition; use crate::wit::{copy_wit_files, generate_stub_wit, verify_action, WitAction}; -use anyhow::Context; +use anyhow::{anyhow, Context}; use clap::Parser; use fs_extra::dir::CopyOptions; use heck::ToSnakeCase; @@ -81,6 +81,8 @@ struct AddStubDependencyArgs { dest_wit_root: PathBuf, #[clap(short, long)] overwrite: bool, + #[clap(short, long)] + update_cargo_toml: bool, } #[tokio::main] @@ -208,10 +210,35 @@ fn add_stub_dependency(args: AddStubDependencyArgs) -> anyhow::Result<()> { } if proceed { - for action in actions { + for action in &actions { action.perform(&args.dest_wit_root)?; } } + if let Some(target_parent) = args.dest_wit_root.parent() { + let target_cargo_toml = target_parent.join("Cargo.toml"); + if target_cargo_toml.exists() + && target_cargo_toml.is_file() + && cargo::is_cargo_component_toml(&target_cargo_toml).is_ok() + { + if !args.update_cargo_toml { + eprintln!("Warning: the newly copied dependencies have to be added to {}. Use the --update-cargo-toml flag to update it automatically.", target_cargo_toml.to_string_lossy()); + } else { + let mut names = Vec::new(); + for action in actions { + names.push(action.get_dep_dir_name()?); + } + cargo::add_dependencies_to_cargo_toml(&target_cargo_toml, &names)?; + } + } else if args.update_cargo_toml { + return Err(anyhow!( + "Cannot update {:?} file because it does not exist or is not a file", + target_cargo_toml + )); + } + } else if args.update_cargo_toml { + return Err(anyhow!("Cannot update the Cargo.toml file because parent directory of the destination WIT root does not exist.")); + } + Ok(()) } diff --git a/wasm-rpc-stubgen/src/wit.rs b/wasm-rpc-stubgen/src/wit.rs index c3fc8cdf..995e7fb3 100644 --- a/wasm-rpc-stubgen/src/wit.rs +++ b/wasm-rpc-stubgen/src/wit.rs @@ -294,7 +294,7 @@ pub fn get_dep_dirs(wit_root: &Path) -> anyhow::Result> { } pub fn get_package_name(wit: &Path) -> anyhow::Result { - let pkg = UnresolvedPackage::parse_file(wit)?; + let pkg = UnresolvedPackage::parse_path(wit)?; Ok(pkg.name) } @@ -369,6 +369,17 @@ impl WitAction { Ok(()) } + + pub fn get_dep_dir_name(&self) -> anyhow::Result { + match self { + WitAction::CopyDepDir { source_dir } => Ok(source_dir + .file_name() + .context("Get wit dependency directory name")? + .to_string_lossy() + .to_string()), + WitAction::CopyDepWit { dir_name, .. } => Ok(dir_name.clone()), + } + } } pub fn verify_action( From c7f926cc97a4aa5c3edf2c9158e14fdf59f04fbb Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Wed, 28 Feb 2024 17:02:17 +0100 Subject: [PATCH 2/2] Update README --- README.md | 4 +++- wasm-rpc-stubgen/README.md | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f285fca4..c5a99cbd 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ Options: -s, --stub-wit-root -d, --dest-wit-root -o, --overwrite + -u, --update-cargo-toml -h, --help Print help -V, --version Print version ``` @@ -117,4 +118,5 @@ The command merges a generated RPC stub as a WIT dependency into an other compon - `stub-wit-root`: The WIT root generated by either `generate` or `build` command - `dest-wit-root`: The WIT root of the component where the stub should be added as a dependency - `overwrite`: This command would not do anything if it detects that it would change an existing WIT file's contents at - the destination. With this flag, it can be forced to overwrite those files. \ No newline at end of file + the destination. With this flag, it can be forced to overwrite those files. +- `update-cargo-toml`: Enables updating the Cargo.toml file in the parent directory of `dest-wit-root` with the copied dependencies.ation. With this flag, it can be forced to overwrite those files. \ No newline at end of file diff --git a/wasm-rpc-stubgen/README.md b/wasm-rpc-stubgen/README.md index eeed1a89..5e990317 100644 --- a/wasm-rpc-stubgen/README.md +++ b/wasm-rpc-stubgen/README.md @@ -70,6 +70,7 @@ Options: -s, --stub-wit-root -d, --dest-wit-root -o, --overwrite + -u, --update-cargo-toml -h, --help Print help -V, --version Print version ``` @@ -79,4 +80,5 @@ The command merges a generated RPC stub as a WIT dependency into an other compon - `stub-wit-root`: The WIT root generated by either `generate` or `build` command - `dest-wit-root`: The WIT root of the component where the stub should be added as a dependency - `overwrite`: This command would not do anything if it detects that it would change an existing WIT file's contents at - the destination. With this flag, it can be forced to overwrite those files. \ No newline at end of file + the destination. With this flag, it can be forced to overwrite those files. +- `update-cargo-toml`: Enables updating the Cargo.toml file in the parent directory of `dest-wit-root` with the copied dependencies. \ No newline at end of file