From a498da5e5acea645fb04a3453429160cc9d451ba Mon Sep 17 00:00:00 2001 From: "Simon B. Gasse" Date: Tue, 30 Apr 2024 17:15:07 +0200 Subject: [PATCH 1/4] Add flag to exclude specified workspace members There may be workspace members which have to stay in sync with other workspaces and should be excluded from the auto-inheritance. This commits allows to specify such workspace members by their package name. --- src/lib.rs | 17 +++++++++++++++-- src/main.rs | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cb9483d..ca253b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ use crate::dedup::MinimalVersionSet; use anyhow::Context; use cargo_manifest::{Dependency, DependencyDetail, DepsSet, Manifest}; use guppy::VersionReq; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; use std::fmt::Formatter; use toml_edit::{Array, Key}; @@ -15,6 +15,9 @@ pub struct AutoInheritConf { help = "Represents inherited dependencies as `package.workspace = true` if possible." )] pub prefer_simple_dotted: bool, + /// Package name(s) of workspace member(s) to exclude. + #[arg(short, long)] + exclude: Vec, } /// Rewrites a `path` dependency as being absolute, based on a given path @@ -76,7 +79,7 @@ macro_rules! get_either_table_mut { }; } -pub fn auto_inherit(conf: &AutoInheritConf) -> Result<(), anyhow::Error> { +pub fn auto_inherit(conf: AutoInheritConf) -> Result<(), anyhow::Error> { let metadata = guppy::MetadataCommand::new().exec().context( "Failed to execute `cargo metadata`. Was the command invoked inside a Rust project?", )?; @@ -96,6 +99,7 @@ pub fn auto_inherit(conf: &AutoInheritConf) -> Result<(), anyhow::Error> { workspace_root ) }; + let excluded = BTreeSet::from_iter(conf.exclude); let mut package_name2specs: BTreeMap = BTreeMap::new(); if let Some(deps) = &mut workspace.dependencies { @@ -106,7 +110,12 @@ pub fn auto_inherit(conf: &AutoInheritConf) -> Result<(), anyhow::Error> { for member_id in graph.workspace().member_ids() { let package = graph.metadata(member_id)?; assert!(package.in_workspace()); + let mut manifest: Manifest = { + if excluded.contains(package.name()) { + println!("Excluded package `{}`", package.name()); + continue; + } let contents = fs_err::read_to_string(package.manifest_path().as_std_path()) .context("Failed to read root manifest")?; toml::from_str(&contents).context("Failed to parse root manifest")? @@ -193,6 +202,10 @@ pub fn auto_inherit(conf: &AutoInheritConf) -> Result<(), anyhow::Error> { // Inherit new "shared" dependencies in each member's manifest for member_id in graph.workspace().member_ids() { let package = graph.metadata(member_id)?; + if excluded.contains(package.name()) { + continue; + } + let manifest_contents = fs_err::read_to_string(package.manifest_path().as_std_path()) .context("Failed to read root manifest")?; let manifest: Manifest = diff --git a/src/main.rs b/src/main.rs index a58f6ea..b71e492 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,5 +19,5 @@ pub enum CargoInvocation { fn main() -> Result<(), anyhow::Error> { let cli = CliWrapper::parse(); let CargoInvocation::AutoInherit(conf) = cli.command; - auto_inherit(&conf) + auto_inherit(conf) } From 92c761236792b78dddc06006f9688de7661bf2da Mon Sep 17 00:00:00 2001 From: Henk Oordt Date: Wed, 30 Oct 2024 12:21:55 +0100 Subject: [PATCH 2/4] Read excluded workspace members from workspace metadata --- Cargo.lock | 5 ++--- Cargo.toml | 6 +++++- README.md | 27 +++++++++++++++++++++++++++ src/lib.rs | 48 +++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 77 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b01b163..4e16dbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,9 +101,8 @@ dependencies = [ [[package]] name = "cargo-manifest" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7db7ad32d2729eca70d1669bae38b6a29dabc30a16f1892cc00977873f450d0a" +version = "0.15.2" +source = "git+https://github.com/hdoordt/cargo-manifest.git#7fd5e7a28410971d323251fed6399ca701ec4050" dependencies = [ "serde", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index 0979ebf..5de43ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ anyhow = "1.0.80" clap = { version = "4", features = ["derive"] } guppy = "0.17.5" fs-err = "2.11.0" -cargo-manifest = "0.14.0" +cargo-manifest = "0.15.2" toml = "0.8.10" semver = "1.0.22" toml_edit = "0.22.6" @@ -37,3 +37,7 @@ installers = [] targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"] # Publish jobs to run in CI pr-run-mode = "plan" + +[patch.crates-io] +# TODO remove me once https://github.com/LukeMathWalker/cargo-manifest/pull/57 is released +cargo-manifest = { git = "https://github.com/hdoordt/cargo-manifest.git" } \ No newline at end of file diff --git a/README.md b/README.md index 5016a9b..fb52e74 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,33 @@ It collects all the dependencies in your workspace, determines which ones can be the `[workspace.dependencies]` section of the root `Cargo.toml`. It also takes care of updating the members' `Cargo.toml` files, setting the correct `features` field for each package. +To exclude workspace members from the autoinherit process, you can either pass their packgage names as an +option like so: + +```bash +cargo autoinherit -e cargo-inherit-test-web +``` + +or you can define the exclusion in the workspace metadata: + +```toml +# Cargo.toml +[workspace] +members = [ + "cli", + "config", + "db", + "web", + "macros" +] + +[workspace.metadata.cargo-autoinherit] +# Skip cargo-autoinherit for these packages +exclude-members = [ + "cargo-autoinherit-test-web" # <= This member will be excluded +] +``` + ## Installation You can find prebuilt binaries on the [Releases page](https://github.com/mainmatter/cargo-autoinherit/releases). diff --git a/src/lib.rs b/src/lib.rs index ca253b8..5325c1f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ use crate::dedup::MinimalVersionSet; -use anyhow::Context; -use cargo_manifest::{Dependency, DependencyDetail, DepsSet, Manifest}; +use anyhow::{anyhow, Context}; +use cargo_manifest::{Dependency, DependencyDetail, DepsSet, Manifest, Workspace}; use guppy::VersionReq; use std::collections::{BTreeMap, BTreeSet}; use std::fmt::Formatter; @@ -20,6 +20,41 @@ pub struct AutoInheritConf { exclude: Vec, } +#[derive(Debug, Default)] +struct AutoInheritMetadata { + exclude: Vec, +} + +impl AutoInheritMetadata { + fn from_workspace(workspace: &Workspace) -> Result { + fn error() -> anyhow::Error { + anyhow!("Excpected value of `exclude` in `workspace.metadata.cargo-autoinherit` to be an array of strings") + } + + let Some(exclude) = workspace + .metadata + .as_ref() + .and_then(|m| m.get("cargo-autoinherit")) + .and_then(|v| v.as_table()) + .and_then(|t| t.get("exclude-members").or(t.get("exclude_members"))) + else { + return Ok(Self::default()); + }; + + let exclude: Vec = match exclude { + toml::Value::Array(excluded) => excluded + .iter() + .map(|v| v.as_str().ok_or_else(error).map(|s| s.to_string())) + .try_fold(Vec::with_capacity(excluded.len()), |mut res, item| { + res.push(item?); + Ok::<_, anyhow::Error>(res) + })?, + _ => return Err(error()), + }; + Ok(Self { exclude }) + } +} + /// Rewrites a `path` dependency as being absolute, based on a given path fn rewrite_dep_paths_as_absolute<'a, P: AsRef>( deps: impl Iterator, @@ -87,7 +122,7 @@ pub fn auto_inherit(conf: AutoInheritConf) -> Result<(), anyhow::Error> { .build_graph() .context("Failed to build package graph")?; let workspace_root = graph.workspace().root(); - let mut root_manifest: Manifest = { + let mut root_manifest: Manifest = { let contents = fs_err::read_to_string(workspace_root.join("Cargo.toml").as_std_path()) .context("Failed to read root manifest")?; toml::from_str(&contents).context("Failed to parse root manifest")? @@ -99,7 +134,10 @@ pub fn auto_inherit(conf: AutoInheritConf) -> Result<(), anyhow::Error> { workspace_root ) }; - let excluded = BTreeSet::from_iter(conf.exclude); + + let autoinherit_metadata = AutoInheritMetadata::from_workspace(workspace)?; + let excluded = + BTreeSet::from_iter(conf.exclude.into_iter().chain(autoinherit_metadata.exclude)); let mut package_name2specs: BTreeMap = BTreeMap::new(); if let Some(deps) = &mut workspace.dependencies { @@ -113,7 +151,7 @@ pub fn auto_inherit(conf: AutoInheritConf) -> Result<(), anyhow::Error> { let mut manifest: Manifest = { if excluded.contains(package.name()) { - println!("Excluded package `{}`", package.name()); + println!("Excluded workspace member `{}`", package.name()); continue; } let contents = fs_err::read_to_string(package.manifest_path().as_std_path()) From 6579efe52ea696981137349fb19d60026fb7b8f5 Mon Sep 17 00:00:00 2001 From: Henk Oordt Date: Wed, 30 Oct 2024 12:31:19 +0100 Subject: [PATCH 3/4] rename exclude to exclude_members to clarify what exactly is being excluded --- src/lib.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5325c1f..aa9faef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,12 +17,12 @@ pub struct AutoInheritConf { pub prefer_simple_dotted: bool, /// Package name(s) of workspace member(s) to exclude. #[arg(short, long)] - exclude: Vec, + exclude_members: Vec, } #[derive(Debug, Default)] struct AutoInheritMetadata { - exclude: Vec, + exclude_members: Vec, } impl AutoInheritMetadata { @@ -51,7 +51,9 @@ impl AutoInheritMetadata { })?, _ => return Err(error()), }; - Ok(Self { exclude }) + Ok(Self { + exclude_members: exclude, + }) } } @@ -136,8 +138,11 @@ pub fn auto_inherit(conf: AutoInheritConf) -> Result<(), anyhow::Error> { }; let autoinherit_metadata = AutoInheritMetadata::from_workspace(workspace)?; - let excluded = - BTreeSet::from_iter(conf.exclude.into_iter().chain(autoinherit_metadata.exclude)); + let excluded_members = BTreeSet::from_iter( + conf.exclude_members + .into_iter() + .chain(autoinherit_metadata.exclude_members), + ); let mut package_name2specs: BTreeMap = BTreeMap::new(); if let Some(deps) = &mut workspace.dependencies { @@ -150,7 +155,7 @@ pub fn auto_inherit(conf: AutoInheritConf) -> Result<(), anyhow::Error> { assert!(package.in_workspace()); let mut manifest: Manifest = { - if excluded.contains(package.name()) { + if excluded_members.contains(package.name()) { println!("Excluded workspace member `{}`", package.name()); continue; } @@ -240,7 +245,7 @@ pub fn auto_inherit(conf: AutoInheritConf) -> Result<(), anyhow::Error> { // Inherit new "shared" dependencies in each member's manifest for member_id in graph.workspace().member_ids() { let package = graph.metadata(member_id)?; - if excluded.contains(package.name()) { + if excluded_members.contains(package.name()) { continue; } From 3589f3811389aed2cf964f2cd8315f08dee7105f Mon Sep 17 00:00:00 2001 From: Henk Oordt Date: Fri, 1 Nov 2024 10:29:46 +0100 Subject: [PATCH 4/4] use v0.16.0 of cargo-manifest instead of patch --- Cargo.lock | 5 +++-- Cargo.toml | 6 +----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4e16dbb..790eda1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,8 +101,9 @@ dependencies = [ [[package]] name = "cargo-manifest" -version = "0.15.2" -source = "git+https://github.com/hdoordt/cargo-manifest.git#7fd5e7a28410971d323251fed6399ca701ec4050" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecbe6b6f6285b7016d41a045742e0ffda96b6e8e6fe7a479a7e5bc2bcffddad" dependencies = [ "serde", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index 5de43ca..e71a735 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ anyhow = "1.0.80" clap = { version = "4", features = ["derive"] } guppy = "0.17.5" fs-err = "2.11.0" -cargo-manifest = "0.15.2" +cargo-manifest = "0.16.0" toml = "0.8.10" semver = "1.0.22" toml_edit = "0.22.6" @@ -37,7 +37,3 @@ installers = [] targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"] # Publish jobs to run in CI pr-run-mode = "plan" - -[patch.crates-io] -# TODO remove me once https://github.com/LukeMathWalker/cargo-manifest/pull/57 is released -cargo-manifest = { git = "https://github.com/hdoordt/cargo-manifest.git" } \ No newline at end of file