diff --git a/CHANGELOG.md b/CHANGELOG.md index b9175cd7d..b1ef098d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add warning message when using incompatible contract's ink! version - [#1334](https://github.com/paritytech/cargo-contract/pull/1334) - Bump `subxt` to `0.32.0` - [#1352](https://github.com/paritytech/cargo-contract/pull/1352) - Remove check for compatible `scale` and `scale-info` versions - [#1370](https://github.com/paritytech/cargo-contract/pull/1370) +- Add workspace support -[#1358](https://github.com/paritytech/cargo-contract/pull/1358) ## [4.0.0-alpha] diff --git a/crates/build/src/lib.rs b/crates/build/src/lib.rs index add3b2103..520290bb8 100644 --- a/crates/build/src/lib.rs +++ b/crates/build/src/lib.rs @@ -365,6 +365,7 @@ fn exec_cargo_for_onchain_target( manifest .with_replaced_lib_to_bin()? .with_profile_release_defaults(Profile::default_contract_release())? + .with_merged_workspace_dependencies(crate_metadata)? .with_empty_workspace(); Ok(()) })? diff --git a/crates/build/src/metadata.rs b/crates/build/src/metadata.rs index 94758d502..7644d7024 100644 --- a/crates/build/src/metadata.rs +++ b/crates/build/src/metadata.rs @@ -187,6 +187,7 @@ pub fn execute( lto: Some(Lto::Thin), ..Profile::default() })? + .with_merged_workspace_dependencies(crate_metadata)? .with_empty_workspace(); Ok(()) })? diff --git a/crates/build/src/workspace/manifest.rs b/crates/build/src/workspace/manifest.rs index 7c23701f3..c9ae6195e 100644 --- a/crates/build/src/workspace/manifest.rs +++ b/crates/build/src/workspace/manifest.rs @@ -23,7 +23,10 @@ use super::{ metadata, Profile, }; -use crate::OptimizationPasses; +use crate::{ + CrateMetadata, + OptimizationPasses, +}; use std::{ convert::TryFrom, @@ -341,6 +344,57 @@ impl Manifest { Ok(self) } + /// Merge the workspace dependencies with the crate dependencies. + pub fn with_merged_workspace_dependencies( + &mut self, + crate_metadata: &CrateMetadata, + ) -> Result<&mut Self> { + let workspace_manifest_path = + crate_metadata.cargo_meta.workspace_root.join("Cargo.toml"); + + // If the workspace manifest is the same as the crate manifest, there's not + // workspace to fix + if workspace_manifest_path == self.path.path { + return Ok(self) + } + + let workspace_toml = + fs::read_to_string(&workspace_manifest_path).context("Loading Cargo.toml")?; + let workspace_toml: value::Table = toml::from_str(&workspace_toml)?; + + let workspace_dependencies = workspace_toml + .get("workspace") + .ok_or_else(|| { + anyhow::anyhow!("[workspace] should exist in workspace manifest") + })? + .as_table() + .ok_or_else(|| anyhow::anyhow!("[workspace] should be a table"))? + .get("dependencies"); + + // If no workspace dependencies are defined, return + let Some(workspace_dependencies) = workspace_dependencies else { + return Ok(self) + }; + + let workspace_dependencies = + workspace_dependencies.as_table().ok_or_else(|| { + anyhow::anyhow!("[workspace.dependencies] should be a table") + })?; + + merge_workspace_with_crate_dependencies( + "dependencies", + &mut self.toml, + workspace_dependencies, + )?; + merge_workspace_with_crate_dependencies( + "dev-dependencies", + &mut self.toml, + workspace_dependencies, + )?; + + Ok(self) + } + /// Replace relative paths with absolute paths with the working directory. /// /// Enables the use of a temporary amended copy of the manifest. @@ -548,6 +602,77 @@ fn crate_type_exists(crate_type: &str, crate_types: &[value::Value]) -> bool { .any(|v| v.as_str().map_or(false, |s| s == crate_type)) } +fn merge_workspace_with_crate_dependencies( + section_name: &str, + crate_toml: &mut value::Table, + workspace_dependencies: &value::Table, +) -> Result<()> { + let Some(dependencies) = crate_toml.get_mut(section_name) else { + return Ok(()) + }; + + let table = dependencies + .as_table_mut() + .ok_or_else(|| anyhow::anyhow!("dependencies should be a table"))?; + + for (name, value) in table { + let Some(dependency) = value.as_table_mut() else { + continue + }; + + let is_workspace_dependency = dependency + .get_mut("workspace") + .unwrap_or(&mut toml::Value::Boolean(false)) + .as_bool() + .unwrap_or(false); + if !is_workspace_dependency { + continue + } + + let workspace_dependency = workspace_dependencies.get(name).ok_or_else(|| { + anyhow::anyhow!("'{}' is not a key in workspace_dependencies", name) + })?; + let workspace_dependency = match workspace_dependency { + toml::Value::Table(table) => table.to_owned(), + // If the workspace dependency is just a version string, we create a table + toml::Value::String(version) => { + let mut table = toml::value::Table::new(); + table.insert("version".to_string(), toml::Value::String(version.clone())); + table + } + // If the workspace dependency is invalid, we throw an error + _ => { + anyhow::bail!("Invalid workspace dependency for {}", name); + } + }; + + dependency.remove("workspace"); + for (key, value) in workspace_dependency { + if let Some(config) = dependency.get_mut(&key) { + // If it's an array we merge the values, + // otherwise we keep the crate value. + if let toml::Value::Array(value) = value { + if let toml::Value::Array(config) = config { + config.extend(value.clone()); + + let mut new_config = Vec::new(); + for v in config.iter() { + if !new_config.contains(v) { + new_config.push(v.clone()); + } + } + *config = new_config; + } + } + } else { + dependency.insert(key.clone(), value.clone()); + } + } + } + + Ok(()) +} + #[cfg(test)] mod test { use super::ManifestPath;