diff --git a/src/bin/cargo/commands/tree.rs b/src/bin/cargo/commands/tree.rs index dfbded3463a..6b996d66fa2 100644 --- a/src/bin/cargo/commands/tree.rs +++ b/src/bin/cargo/commands/tree.rs @@ -56,6 +56,11 @@ pub fn cli() -> App { ) .short("i"), ) + .arg(multi_opt( + "prune", + "SPEC", + "Prune the given package from the display of the dependency tree", + )) .arg(opt("depth", "Maximum display depth of the dependency tree").value_name("DEPTH")) // Deprecated, use --prefix=none instead. .arg(Arg::with_name("no-indent").long("no-indent").hidden(true)) @@ -152,6 +157,8 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult { let (edge_kinds, no_proc_macro) = parse_edge_kinds(config, args)?; let graph_features = edge_kinds.contains(&EdgeKind::Feature); + let pkgs_to_prune = args._values_of("prune"); + let packages = args.packages_from_flags()?; let mut invert = args .values_of("invert") @@ -197,6 +204,7 @@ subtree of the package given to -p.\n\ target, edge_kinds, invert, + pkgs_to_prune, prefix, no_dedupe, duplicates: args.is_present("duplicates"), diff --git a/src/cargo/ops/tree/mod.rs b/src/cargo/ops/tree/mod.rs index db5ee4ee202..e9981b78650 100644 --- a/src/cargo/ops/tree/mod.rs +++ b/src/cargo/ops/tree/mod.rs @@ -27,6 +27,8 @@ pub struct TreeOptions { /// The dependency kinds to display. pub edge_kinds: HashSet, pub invert: Vec, + /// The packages to prune from the display of the dependency tree. + pub pkgs_to_prune: Vec, /// The style of prefix for each line. pub prefix: Prefix, /// If `true`, duplicates will be repeated. @@ -199,7 +201,19 @@ pub fn build_and_print(ws: &Workspace<'_>, opts: &TreeOptions) -> CargoResult<() graph.invert(); } - print(ws.config(), opts, root_indexes, &graph)?; + // Packages to prune. + let pkgs_to_prune = opts + .pkgs_to_prune + .iter() + .map(|p| PackageIdSpec::parse(p)) + .map(|r| { + // Provide a error message if pkgid is not within the resolved + // dependencies graph. + r.and_then(|spec| spec.query(ws_resolve.targeted_resolve.iter()).and(Ok(spec))) + }) + .collect::>>()?; + + print(ws.config(), opts, root_indexes, &pkgs_to_prune, &graph)?; Ok(()) } @@ -208,6 +222,7 @@ fn print( config: &Config, opts: &TreeOptions, roots: Vec, + pkgs_to_prune: &[PackageIdSpec], graph: &Graph<'_>, ) -> CargoResult<()> { let format = Pattern::new(&opts.format) @@ -240,6 +255,7 @@ fn print( root_index, &format, symbols, + pkgs_to_prune, opts.prefix, opts.no_dedupe, opts.max_display_depth, @@ -260,6 +276,7 @@ fn print_node<'a>( node_index: usize, format: &Pattern, symbols: &Symbols, + pkgs_to_prune: &[PackageIdSpec], prefix: Prefix, no_dedupe: bool, max_display_depth: u32, @@ -319,6 +336,7 @@ fn print_node<'a>( node_index, format, symbols, + pkgs_to_prune, prefix, no_dedupe, max_display_depth, @@ -339,6 +357,7 @@ fn print_dependencies<'a>( node_index: usize, format: &Pattern, symbols: &Symbols, + pkgs_to_prune: &[PackageIdSpec], prefix: Prefix, no_dedupe: bool, max_display_depth: u32, @@ -371,6 +390,11 @@ fn print_dependencies<'a>( } } + // Current level exceeds maximum display depth. Skip. + if levels_continue.len() + 1 > max_display_depth as usize { + return; + } + let mut it = deps .iter() .filter(|dep| { @@ -386,26 +410,34 @@ fn print_dependencies<'a>( true } }) + .filter(|dep| { + // Filter out packages to prune. + match graph.node(**dep) { + Node::Package { package_id, .. } => { + !pkgs_to_prune.iter().any(|spec| spec.matches(*package_id)) + } + _ => true, + } + }) .peekable(); while let Some(dependency) = it.next() { - if levels_continue.len() + 1 <= max_display_depth as usize { - levels_continue.push(it.peek().is_some()); - print_node( - config, - graph, - *dependency, - format, - symbols, - prefix, - no_dedupe, - max_display_depth, - no_proc_macro, - visited_deps, - levels_continue, - print_stack, - ); - levels_continue.pop(); - } + levels_continue.push(it.peek().is_some()); + print_node( + config, + graph, + *dependency, + format, + symbols, + pkgs_to_prune, + prefix, + no_dedupe, + max_display_depth, + no_proc_macro, + visited_deps, + levels_continue, + print_stack, + ); + levels_continue.pop(); } } diff --git a/src/doc/man/cargo-tree.md b/src/doc/man/cargo-tree.md index e4a1b616a97..e8d4397938b 100644 --- a/src/doc/man/cargo-tree.md +++ b/src/doc/man/cargo-tree.md @@ -71,6 +71,10 @@ flag can be used to display the package's reverse dependencies only with the subtree of the package given to `-p`. {{/option}} +{{#option "`--prune` _spec_" }} +Prune the given package from the display of the dependency tree. +{{/option}} + {{#option "`--depth` _depth_" }} Maximum display depth of the dependency tree. A depth of 1 displays the direct dependencies, for example. diff --git a/src/doc/man/generated_txt/cargo-tree.txt b/src/doc/man/generated_txt/cargo-tree.txt index 345a941d2ef..3928eca014b 100644 --- a/src/doc/man/generated_txt/cargo-tree.txt +++ b/src/doc/man/generated_txt/cargo-tree.txt @@ -59,6 +59,9 @@ OPTIONS package's reverse dependencies only with the subtree of the package given to -p. + --prune spec + Prune the given package from the display of the dependency tree. + --depth depth Maximum display depth of the dependency tree. A depth of 1 displays the direct dependencies, for example. diff --git a/src/doc/src/commands/cargo-tree.md b/src/doc/src/commands/cargo-tree.md index a085d63efaf..16b82f538fb 100644 --- a/src/doc/src/commands/cargo-tree.md +++ b/src/doc/src/commands/cargo-tree.md @@ -71,6 +71,10 @@ flag can be used to display the package's reverse dependencies only with the subtree of the package given to -p. +
--prune spec
+
Prune the given package from the display of the dependency tree.
+ +
--depth depth
Maximum display depth of the dependency tree. A depth of 1 displays the direct dependencies, for example.
diff --git a/src/etc/man/cargo-tree.1 b/src/etc/man/cargo-tree.1 index 48803dedc35..e2967e7c753 100644 --- a/src/etc/man/cargo-tree.1 +++ b/src/etc/man/cargo-tree.1 @@ -69,6 +69,11 @@ flag can be used to display the package's reverse dependencies only with the subtree of the package given to \fB\-p\fR\&. .RE .sp +\fB\-\-prune\fR \fIspec\fR +.RS 4 +Prune the given package from the display of the dependency tree. +.RE +.sp \fB\-\-depth\fR \fIdepth\fR .RS 4 Maximum display depth of the dependency tree. A depth of 1 displays the direct diff --git a/tests/testsuite/tree.rs b/tests/testsuite/tree.rs index c9a5441b627..acb727b64cf 100644 --- a/tests/testsuite/tree.rs +++ b/tests/testsuite/tree.rs @@ -1720,3 +1720,81 @@ c v1.0.0 ) .run(); } + +#[cargo_test] +fn prune() { + let p = make_simple_proj(); + + p.cargo("tree --prune c") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── a v1.0.0 + └── b v1.0.0 +[build-dependencies] +└── bdep v1.0.0 + └── b v1.0.0 (*) +[dev-dependencies] +└── devdep v1.0.0 + └── b v1.0.0 (*) +", + ) + .run(); + + // multiple prune + p.cargo("tree --prune c --prune bdep") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── a v1.0.0 + └── b v1.0.0 +[build-dependencies] +[dev-dependencies] +└── devdep v1.0.0 + └── b v1.0.0 (*) +", + ) + .run(); + + // with edge-kinds + p.cargo("tree --prune c -e normal") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── a v1.0.0 + └── b v1.0.0 +", + ) + .run(); + + // pruning self does not works + p.cargo("tree --prune foo") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── a v1.0.0 +│ └── b v1.0.0 +│ └── c v1.0.0 +└── c v1.0.0 +[build-dependencies] +└── bdep v1.0.0 + └── b v1.0.0 (*) +[dev-dependencies] +└── devdep v1.0.0 + └── b v1.0.0 (*) +", + ) + .run(); + + // dep not exist + p.cargo("tree --prune no-dep") + .with_stderr( + "\ +[ERROR] package ID specification `no-dep` did not match any packages + +Did you mean `bdep`? +", + ) + .with_status(101) + .run(); +}