From 1c0f6cfdcc1b25c3d52020fc2ab5f8917e3e66dd Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 13 Dec 2024 04:25:02 +0900 Subject: [PATCH 1/5] test(tree): new incomplete test for `cargo tree --depth workspace` --- tests/testsuite/tree.rs | 58 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/tests/testsuite/tree.rs b/tests/testsuite/tree.rs index 60374bb59dd..2e81f55ce0e 100644 --- a/tests/testsuite/tree.rs +++ b/tests/testsuite/tree.rs @@ -1846,6 +1846,64 @@ c v1.0.0 .run(); } +#[cargo_test] +fn depth_workspace() { + Package::new("somedep", "1.0.0").publish(); + Package::new("otherdep", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b", "c"] + "#, + ) + .file("a/Cargo.toml", &basic_manifest("a", "1.0.0")) + .file("a/src/lib.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + + [dependencies] + c = { path = "../c" } + somedep = "1" + "#, + ) + .file("b/src/lib.rs", "") + .file( + "c/Cargo.toml", + r#" + [package] + name = "c" + version = "0.1.0" + + [dependencies] + somedep = "1" + otherdep = "1" + "#, + ) + .file("c/src/lib.rs", "") + .build(); + + p.cargo("tree") + .with_stdout_data(str![[r#" +a v1.0.0 ([ROOT]/foo/a) + +b v0.1.0 ([ROOT]/foo/b) +├── c v0.1.0 ([ROOT]/foo/c) +│ ├── otherdep v1.0.0 +│ └── somedep v1.0.0 +└── somedep v1.0.0 + +c v0.1.0 ([ROOT]/foo/c) (*) + +"#]]) + .run(); +} + #[cargo_test] fn prune() { let p = make_simple_proj(); From fc00223fca7bb7e927aeda2ff1810187ac0d7c4c Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 13 Dec 2024 04:26:50 +0900 Subject: [PATCH 2/5] refactor(tree): replace previous `depth: u32` opts with new type `DisplayDepth` refactor(tree): replace previous `depth: u32` opts with new type `DisplayDepth` --- src/bin/cargo/commands/tree.rs | 10 ++++++-- src/cargo/ops/tree/mod.rs | 43 ++++++++++++++++++++++++++++------ 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/bin/cargo/commands/tree.rs b/src/bin/cargo/commands/tree.rs index b0f35370ebc..98b21db6c73 100644 --- a/src/bin/cargo/commands/tree.rs +++ b/src/bin/cargo/commands/tree.rs @@ -2,7 +2,7 @@ use crate::cli; use crate::command_prelude::*; use anyhow::{bail, format_err}; use cargo::core::dependency::DepKind; -use cargo::ops::tree::{self, EdgeKind}; +use cargo::ops::tree::{self, DisplayDepth, EdgeKind}; use cargo::ops::Packages; use cargo::util::print_available_packages; use cargo::util::CargoResult; @@ -162,6 +162,12 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let pkgs_to_prune = args._values_of("prune"); + let display_depth = args + ._value_of("depth") + .map(|s| s.parse::()) + .transpose()? + .unwrap_or(DisplayDepth::MaxDisplayDepth(u32::MAX)); + let packages = args.packages_from_flags()?; let mut invert = args .get_many::("invert") @@ -222,7 +228,7 @@ subtree of the package given to -p.\n\ duplicates: args.flag("duplicates"), format: args.get_one::("format").cloned().unwrap(), graph_features, - max_display_depth: args.value_of_u32("depth")?.unwrap_or(u32::MAX), + display_depth, no_proc_macro, }; diff --git a/src/cargo/ops/tree/mod.rs b/src/cargo/ops/tree/mod.rs index 0940f0ebfad..4ffb235c1d8 100644 --- a/src/cargo/ops/tree/mod.rs +++ b/src/cargo/ops/tree/mod.rs @@ -43,8 +43,9 @@ pub struct TreeOptions { pub format: String, /// Includes features in the tree as separate nodes. pub graph_features: bool, - /// Maximum display depth of the dependency tree. - pub max_display_depth: u32, + /// Display depth of the dependency tree. + /// If non-negative integer, display dependencies with that amount of max depth. + pub display_depth: DisplayDepth, /// Excludes proc-macro dependencies. pub no_proc_macro: bool, } @@ -86,6 +87,30 @@ impl FromStr for Prefix { } } +#[derive(Clone, Copy)] +pub enum DisplayDepth { + MaxDisplayDepth(u32), +} + +impl FromStr for DisplayDepth { + type Err = clap::Error; + + fn from_str(s: &str) -> Result { + match s { + s => s.parse().map(Self::MaxDisplayDepth).map_err(|_| { + clap::Error::raw( + clap::error::ErrorKind::ValueValidation, + format!( + "supported values for --depth are non-negative integers, \ + but `{}` is unknown", + s + ), + ) + }), + } + } +} + struct Symbols { down: &'static str, tee: &'static str, @@ -250,7 +275,7 @@ fn print( pkgs_to_prune, opts.prefix, opts.no_dedupe, - opts.max_display_depth, + opts.display_depth, &mut visited_deps, &mut levels_continue, &mut print_stack, @@ -270,7 +295,7 @@ fn print_node<'a>( pkgs_to_prune: &[PackageIdSpec], prefix: Prefix, no_dedupe: bool, - max_display_depth: u32, + display_depth: DisplayDepth, visited_deps: &mut HashSet, levels_continue: &mut Vec, print_stack: &mut Vec, @@ -329,7 +354,7 @@ fn print_node<'a>( pkgs_to_prune, prefix, no_dedupe, - max_display_depth, + display_depth, visited_deps, levels_continue, print_stack, @@ -349,7 +374,7 @@ fn print_dependencies<'a>( pkgs_to_prune: &[PackageIdSpec], prefix: Prefix, no_dedupe: bool, - max_display_depth: u32, + display_depth: DisplayDepth, visited_deps: &mut HashSet, levels_continue: &mut Vec, print_stack: &mut Vec, @@ -378,6 +403,10 @@ fn print_dependencies<'a>( } } + let max_display_depth = match display_depth { + DisplayDepth::MaxDisplayDepth(max) => max, + }; + // Current level exceeds maximum display depth. Skip. if levels_continue.len() + 1 > max_display_depth as usize { return; @@ -407,7 +436,7 @@ fn print_dependencies<'a>( pkgs_to_prune, prefix, no_dedupe, - max_display_depth, + display_depth, visited_deps, levels_continue, print_stack, From b729060b149c43896924b910b6e55eac8a3c77b9 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 13 Dec 2024 04:33:49 +0900 Subject: [PATCH 3/5] feat(workspace): new function `is_member_id` to check whether given package_id belongs to ws --- src/cargo/core/workspace.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cargo/core/workspace.rs b/src/cargo/core/workspace.rs index a2345cf676b..a81c6b9304e 100644 --- a/src/cargo/core/workspace.rs +++ b/src/cargo/core/workspace.rs @@ -591,6 +591,11 @@ impl<'gctx> Workspace<'gctx> { self.member_ids.contains(&pkg.package_id()) } + /// Returns true if the given package_id is a member of the workspace. + pub fn is_member_id(&self, package_id: PackageId) -> bool { + self.member_ids.contains(&package_id) + } + pub fn is_ephemeral(&self) -> bool { self.is_ephemeral } From 65931bcbf614b212b742e985f217cf94ab1f02ef Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 13 Dec 2024 05:56:20 +0900 Subject: [PATCH 4/5] refactor(tree): change signatures of tree printing functions to receive `&Workspace` --- src/cargo/ops/tree/mod.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/cargo/ops/tree/mod.rs b/src/cargo/ops/tree/mod.rs index 4ffb235c1d8..60d7974c49c 100644 --- a/src/cargo/ops/tree/mod.rs +++ b/src/cargo/ops/tree/mod.rs @@ -6,7 +6,7 @@ use crate::core::dependency::DepKind; use crate::core::resolver::{features::CliFeatures, ForceAllTargets, HasDevUnits}; use crate::core::{Package, PackageId, PackageIdSpec, PackageIdSpecQuery, Workspace}; use crate::ops::{self, Packages}; -use crate::util::{CargoResult, GlobalContext}; +use crate::util::CargoResult; use crate::{drop_print, drop_println}; use anyhow::Context as _; use graph::Graph; @@ -228,14 +228,14 @@ pub fn build_and_print(ws: &Workspace<'_>, opts: &TreeOptions) -> CargoResult<() try to use option `--target all` first, and then narrow your search scope accordingly.", )?; } else { - print(ws.gctx(), opts, root_indexes, &pkgs_to_prune, &graph)?; + print(ws, opts, root_indexes, &pkgs_to_prune, &graph)?; } Ok(()) } /// Prints a tree for each given root. fn print( - gctx: &GlobalContext, + ws: &Workspace<'_>, opts: &TreeOptions, roots: Vec, pkgs_to_prune: &[PackageIdSpec], @@ -244,7 +244,7 @@ fn print( let format = Pattern::new(&opts.format) .with_context(|| format!("tree format `{}` not valid", opts.format))?; - let symbols = if gctx.shell().out_unicode() { + let symbols = if ws.gctx().shell().out_unicode() { &UTF8_SYMBOLS } else { &ASCII_SYMBOLS @@ -256,7 +256,7 @@ fn print( for (i, root_index) in roots.into_iter().enumerate() { if i != 0 { - drop_println!(gctx); + drop_println!(ws.gctx()); } // A stack of bools used to determine where | symbols should appear @@ -267,7 +267,7 @@ fn print( let mut print_stack = vec![]; print_node( - gctx, + ws, graph, root_index, &format, @@ -287,7 +287,7 @@ fn print( /// Prints a package and all of its dependencies. fn print_node<'a>( - gctx: &GlobalContext, + ws: &Workspace<'_>, graph: &'a Graph<'_>, node_index: usize, format: &Pattern, @@ -303,12 +303,12 @@ fn print_node<'a>( let new = no_dedupe || visited_deps.insert(node_index); match prefix { - Prefix::Depth => drop_print!(gctx, "{}", levels_continue.len()), + Prefix::Depth => drop_print!(ws.gctx(), "{}", levels_continue.len()), Prefix::Indent => { if let Some((last_continues, rest)) = levels_continue.split_last() { for continues in rest { let c = if *continues { symbols.down } else { " " }; - drop_print!(gctx, "{} ", c); + drop_print!(ws.gctx(), "{} ", c); } let c = if *last_continues { @@ -316,7 +316,7 @@ fn print_node<'a>( } else { symbols.ell }; - drop_print!(gctx, "{0}{1}{1} ", c, symbols.right); + drop_print!(ws.gctx(), "{0}{1}{1} ", c, symbols.right); } } Prefix::None => {} @@ -332,7 +332,7 @@ fn print_node<'a>( } else { " (*)" }; - drop_println!(gctx, "{}{}", format.display(graph, node_index), star); + drop_println!(ws.gctx(), "{}{}", format.display(graph, node_index), star); if !new || in_cycle { return; @@ -346,7 +346,7 @@ fn print_node<'a>( EdgeKind::Feature, ] { print_dependencies( - gctx, + ws, graph, node_index, format, @@ -366,7 +366,7 @@ fn print_node<'a>( /// Prints all the dependencies of a package for the given dependency kind. fn print_dependencies<'a>( - gctx: &GlobalContext, + ws: &Workspace<'_>, graph: &'a Graph<'_>, node_index: usize, format: &Pattern, @@ -396,10 +396,10 @@ fn print_dependencies<'a>( if let Some(name) = name { for continues in &**levels_continue { let c = if *continues { symbols.down } else { " " }; - drop_print!(gctx, "{} ", c); + drop_print!(ws.gctx(), "{} ", c); } - drop_println!(gctx, "{}", name); + drop_println!(ws.gctx(), "{}", name); } } @@ -428,7 +428,7 @@ fn print_dependencies<'a>( while let Some(dependency) = it.next() { levels_continue.push(it.peek().is_some()); print_node( - gctx, + ws, graph, *dependency, format, From f7442319e7f1ea17b0c98136424cc83bb289c190 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 13 Dec 2024 05:57:12 +0900 Subject: [PATCH 5/5] feat(tree): implement filtering logic for `--depth workspace` flag --- src/cargo/ops/tree/mod.rs | 13 ++++++++++--- src/doc/man/cargo-tree.md | 3 +++ src/doc/man/generated_txt/cargo-tree.txt | 3 +++ src/doc/src/commands/cargo-tree.md | 4 +++- src/etc/man/cargo-tree.1 | 3 +++ tests/testsuite/tree.rs | 7 ++----- 6 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/cargo/ops/tree/mod.rs b/src/cargo/ops/tree/mod.rs index 60d7974c49c..e0579696f61 100644 --- a/src/cargo/ops/tree/mod.rs +++ b/src/cargo/ops/tree/mod.rs @@ -45,6 +45,7 @@ pub struct TreeOptions { pub graph_features: bool, /// Display depth of the dependency tree. /// If non-negative integer, display dependencies with that amount of max depth. + /// If `workspace`, display dependencies from current workspace only. pub display_depth: DisplayDepth, /// Excludes proc-macro dependencies. pub no_proc_macro: bool, @@ -90,6 +91,7 @@ impl FromStr for Prefix { #[derive(Clone, Copy)] pub enum DisplayDepth { MaxDisplayDepth(u32), + Workspace, } impl FromStr for DisplayDepth { @@ -97,11 +99,12 @@ impl FromStr for DisplayDepth { fn from_str(s: &str) -> Result { match s { + "workspace" => Ok(Self::Workspace), s => s.parse().map(Self::MaxDisplayDepth).map_err(|_| { clap::Error::raw( clap::error::ErrorKind::ValueValidation, format!( - "supported values for --depth are non-negative integers, \ + "supported values for --depth are non-negative integers and `workspace`, \ but `{}` is unknown", s ), @@ -403,8 +406,9 @@ fn print_dependencies<'a>( } } - let max_display_depth = match display_depth { - DisplayDepth::MaxDisplayDepth(max) => max, + let (max_display_depth, filter_non_workspace_member) = match display_depth { + DisplayDepth::MaxDisplayDepth(max) => (max, false), + DisplayDepth::Workspace => (u32::MAX, true), }; // Current level exceeds maximum display depth. Skip. @@ -418,6 +422,9 @@ fn print_dependencies<'a>( // Filter out packages to prune. match graph.node(**dep) { Node::Package { package_id, .. } => { + if filter_non_workspace_member && !ws.is_member_id(*package_id) { + return false; + } !pkgs_to_prune.iter().any(|spec| spec.matches(*package_id)) } _ => true, diff --git a/src/doc/man/cargo-tree.md b/src/doc/man/cargo-tree.md index ce99b761e7d..fc3521c9939 100644 --- a/src/doc/man/cargo-tree.md +++ b/src/doc/man/cargo-tree.md @@ -96,6 +96,9 @@ Prune the given package from the display of the dependency tree. {{#option "`--depth` _depth_" }} Maximum display depth of the dependency tree. A depth of 1 displays the direct dependencies, for example. + +If the given value is `workspace`, only shows the dependencies that are member +of the current workspace, instead. {{/option}} {{#option "`--no-dedupe`" }} diff --git a/src/doc/man/generated_txt/cargo-tree.txt b/src/doc/man/generated_txt/cargo-tree.txt index 1af730aff53..0fc542f429f 100644 --- a/src/doc/man/generated_txt/cargo-tree.txt +++ b/src/doc/man/generated_txt/cargo-tree.txt @@ -85,6 +85,9 @@ OPTIONS Maximum display depth of the dependency tree. A depth of 1 displays the direct dependencies, for example. + If the given value is workspace, only shows the dependencies that + are member of the current workspace, instead. + --no-dedupe Do not de-duplicate repeated dependencies. Usually, when a package has already displayed its dependencies, further occurrences will not diff --git a/src/doc/src/commands/cargo-tree.md b/src/doc/src/commands/cargo-tree.md index 154939a28ef..9418eaa9d1c 100644 --- a/src/doc/src/commands/cargo-tree.md +++ b/src/doc/src/commands/cargo-tree.md @@ -91,7 +91,9 @@ subtree of the package given to -p.
--depth depth
Maximum display depth of the dependency tree. A depth of 1 displays the direct -dependencies, for example.
+dependencies, for example.

+

If the given value is workspace, only shows the dependencies that are member +of the current workspace, instead.

--no-dedupe
diff --git a/src/etc/man/cargo-tree.1 b/src/etc/man/cargo-tree.1 index c951460b028..25045b3f92d 100644 --- a/src/etc/man/cargo-tree.1 +++ b/src/etc/man/cargo-tree.1 @@ -93,6 +93,9 @@ Prune the given package from the display of the dependency tree. .RS 4 Maximum display depth of the dependency tree. A depth of 1 displays the direct dependencies, for example. +.sp +If the given value is \fBworkspace\fR, only shows the dependencies that are member +of the current workspace, instead. .RE .sp \fB\-\-no\-dedupe\fR diff --git a/tests/testsuite/tree.rs b/tests/testsuite/tree.rs index 2e81f55ce0e..762cc4d9652 100644 --- a/tests/testsuite/tree.rs +++ b/tests/testsuite/tree.rs @@ -1888,15 +1888,12 @@ fn depth_workspace() { .file("c/src/lib.rs", "") .build(); - p.cargo("tree") + p.cargo("tree --depth workspace") .with_stdout_data(str![[r#" a v1.0.0 ([ROOT]/foo/a) b v0.1.0 ([ROOT]/foo/b) -├── c v0.1.0 ([ROOT]/foo/c) -│ ├── otherdep v1.0.0 -│ └── somedep v1.0.0 -└── somedep v1.0.0 +└── c v0.1.0 ([ROOT]/foo/c) c v0.1.0 ([ROOT]/foo/c) (*)