Skip to content

Commit 63ecc8d

Browse files
authored
feat: Implement --depth workspace for cargo tree command (#14928)
Resolves #14420
2 parents 4412b0a + f744231 commit 63ecc8d

File tree

8 files changed

+139
-26
lines changed

8 files changed

+139
-26
lines changed

src/bin/cargo/commands/tree.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::cli;
22
use crate::command_prelude::*;
33
use anyhow::{bail, format_err};
44
use cargo::core::dependency::DepKind;
5-
use cargo::ops::tree::{self, EdgeKind};
5+
use cargo::ops::tree::{self, DisplayDepth, EdgeKind};
66
use cargo::ops::Packages;
77
use cargo::util::print_available_packages;
88
use cargo::util::CargoResult;
@@ -162,6 +162,12 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
162162

163163
let pkgs_to_prune = args._values_of("prune");
164164

165+
let display_depth = args
166+
._value_of("depth")
167+
.map(|s| s.parse::<DisplayDepth>())
168+
.transpose()?
169+
.unwrap_or(DisplayDepth::MaxDisplayDepth(u32::MAX));
170+
165171
let packages = args.packages_from_flags()?;
166172
let mut invert = args
167173
.get_many::<String>("invert")
@@ -222,7 +228,7 @@ subtree of the package given to -p.\n\
222228
duplicates: args.flag("duplicates"),
223229
format: args.get_one::<String>("format").cloned().unwrap(),
224230
graph_features,
225-
max_display_depth: args.value_of_u32("depth")?.unwrap_or(u32::MAX),
231+
display_depth,
226232
no_proc_macro,
227233
};
228234

src/cargo/core/workspace.rs

+5
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,11 @@ impl<'gctx> Workspace<'gctx> {
591591
self.member_ids.contains(&pkg.package_id())
592592
}
593593

594+
/// Returns true if the given package_id is a member of the workspace.
595+
pub fn is_member_id(&self, package_id: PackageId) -> bool {
596+
self.member_ids.contains(&package_id)
597+
}
598+
594599
pub fn is_ephemeral(&self) -> bool {
595600
self.is_ephemeral
596601
}

src/cargo/ops/tree/mod.rs

+59-23
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::core::dependency::DepKind;
66
use crate::core::resolver::{features::CliFeatures, ForceAllTargets, HasDevUnits};
77
use crate::core::{Package, PackageId, PackageIdSpec, PackageIdSpecQuery, Workspace};
88
use crate::ops::{self, Packages};
9-
use crate::util::{CargoResult, GlobalContext};
9+
use crate::util::CargoResult;
1010
use crate::{drop_print, drop_println};
1111
use anyhow::Context as _;
1212
use graph::Graph;
@@ -43,8 +43,10 @@ pub struct TreeOptions {
4343
pub format: String,
4444
/// Includes features in the tree as separate nodes.
4545
pub graph_features: bool,
46-
/// Maximum display depth of the dependency tree.
47-
pub max_display_depth: u32,
46+
/// Display depth of the dependency tree.
47+
/// If non-negative integer, display dependencies with that amount of max depth.
48+
/// If `workspace`, display dependencies from current workspace only.
49+
pub display_depth: DisplayDepth,
4850
/// Excludes proc-macro dependencies.
4951
pub no_proc_macro: bool,
5052
}
@@ -86,6 +88,32 @@ impl FromStr for Prefix {
8688
}
8789
}
8890

91+
#[derive(Clone, Copy)]
92+
pub enum DisplayDepth {
93+
MaxDisplayDepth(u32),
94+
Workspace,
95+
}
96+
97+
impl FromStr for DisplayDepth {
98+
type Err = clap::Error;
99+
100+
fn from_str(s: &str) -> Result<Self, Self::Err> {
101+
match s {
102+
"workspace" => Ok(Self::Workspace),
103+
s => s.parse().map(Self::MaxDisplayDepth).map_err(|_| {
104+
clap::Error::raw(
105+
clap::error::ErrorKind::ValueValidation,
106+
format!(
107+
"supported values for --depth are non-negative integers and `workspace`, \
108+
but `{}` is unknown",
109+
s
110+
),
111+
)
112+
}),
113+
}
114+
}
115+
}
116+
89117
struct Symbols {
90118
down: &'static str,
91119
tee: &'static str,
@@ -203,14 +231,14 @@ pub fn build_and_print(ws: &Workspace<'_>, opts: &TreeOptions) -> CargoResult<()
203231
try to use option `--target all` first, and then narrow your search scope accordingly.",
204232
)?;
205233
} else {
206-
print(ws.gctx(), opts, root_indexes, &pkgs_to_prune, &graph)?;
234+
print(ws, opts, root_indexes, &pkgs_to_prune, &graph)?;
207235
}
208236
Ok(())
209237
}
210238

211239
/// Prints a tree for each given root.
212240
fn print(
213-
gctx: &GlobalContext,
241+
ws: &Workspace<'_>,
214242
opts: &TreeOptions,
215243
roots: Vec<usize>,
216244
pkgs_to_prune: &[PackageIdSpec],
@@ -219,7 +247,7 @@ fn print(
219247
let format = Pattern::new(&opts.format)
220248
.with_context(|| format!("tree format `{}` not valid", opts.format))?;
221249

222-
let symbols = if gctx.shell().out_unicode() {
250+
let symbols = if ws.gctx().shell().out_unicode() {
223251
&UTF8_SYMBOLS
224252
} else {
225253
&ASCII_SYMBOLS
@@ -231,7 +259,7 @@ fn print(
231259

232260
for (i, root_index) in roots.into_iter().enumerate() {
233261
if i != 0 {
234-
drop_println!(gctx);
262+
drop_println!(ws.gctx());
235263
}
236264

237265
// A stack of bools used to determine where | symbols should appear
@@ -242,15 +270,15 @@ fn print(
242270
let mut print_stack = vec![];
243271

244272
print_node(
245-
gctx,
273+
ws,
246274
graph,
247275
root_index,
248276
&format,
249277
symbols,
250278
pkgs_to_prune,
251279
opts.prefix,
252280
opts.no_dedupe,
253-
opts.max_display_depth,
281+
opts.display_depth,
254282
&mut visited_deps,
255283
&mut levels_continue,
256284
&mut print_stack,
@@ -262,36 +290,36 @@ fn print(
262290

263291
/// Prints a package and all of its dependencies.
264292
fn print_node<'a>(
265-
gctx: &GlobalContext,
293+
ws: &Workspace<'_>,
266294
graph: &'a Graph<'_>,
267295
node_index: usize,
268296
format: &Pattern,
269297
symbols: &Symbols,
270298
pkgs_to_prune: &[PackageIdSpec],
271299
prefix: Prefix,
272300
no_dedupe: bool,
273-
max_display_depth: u32,
301+
display_depth: DisplayDepth,
274302
visited_deps: &mut HashSet<usize>,
275303
levels_continue: &mut Vec<bool>,
276304
print_stack: &mut Vec<usize>,
277305
) {
278306
let new = no_dedupe || visited_deps.insert(node_index);
279307

280308
match prefix {
281-
Prefix::Depth => drop_print!(gctx, "{}", levels_continue.len()),
309+
Prefix::Depth => drop_print!(ws.gctx(), "{}", levels_continue.len()),
282310
Prefix::Indent => {
283311
if let Some((last_continues, rest)) = levels_continue.split_last() {
284312
for continues in rest {
285313
let c = if *continues { symbols.down } else { " " };
286-
drop_print!(gctx, "{} ", c);
314+
drop_print!(ws.gctx(), "{} ", c);
287315
}
288316

289317
let c = if *last_continues {
290318
symbols.tee
291319
} else {
292320
symbols.ell
293321
};
294-
drop_print!(gctx, "{0}{1}{1} ", c, symbols.right);
322+
drop_print!(ws.gctx(), "{0}{1}{1} ", c, symbols.right);
295323
}
296324
}
297325
Prefix::None => {}
@@ -307,7 +335,7 @@ fn print_node<'a>(
307335
} else {
308336
" (*)"
309337
};
310-
drop_println!(gctx, "{}{}", format.display(graph, node_index), star);
338+
drop_println!(ws.gctx(), "{}{}", format.display(graph, node_index), star);
311339

312340
if !new || in_cycle {
313341
return;
@@ -321,15 +349,15 @@ fn print_node<'a>(
321349
EdgeKind::Feature,
322350
] {
323351
print_dependencies(
324-
gctx,
352+
ws,
325353
graph,
326354
node_index,
327355
format,
328356
symbols,
329357
pkgs_to_prune,
330358
prefix,
331359
no_dedupe,
332-
max_display_depth,
360+
display_depth,
333361
visited_deps,
334362
levels_continue,
335363
print_stack,
@@ -341,15 +369,15 @@ fn print_node<'a>(
341369

342370
/// Prints all the dependencies of a package for the given dependency kind.
343371
fn print_dependencies<'a>(
344-
gctx: &GlobalContext,
372+
ws: &Workspace<'_>,
345373
graph: &'a Graph<'_>,
346374
node_index: usize,
347375
format: &Pattern,
348376
symbols: &Symbols,
349377
pkgs_to_prune: &[PackageIdSpec],
350378
prefix: Prefix,
351379
no_dedupe: bool,
352-
max_display_depth: u32,
380+
display_depth: DisplayDepth,
353381
visited_deps: &mut HashSet<usize>,
354382
levels_continue: &mut Vec<bool>,
355383
print_stack: &mut Vec<usize>,
@@ -371,13 +399,18 @@ fn print_dependencies<'a>(
371399
if let Some(name) = name {
372400
for continues in &**levels_continue {
373401
let c = if *continues { symbols.down } else { " " };
374-
drop_print!(gctx, "{} ", c);
402+
drop_print!(ws.gctx(), "{} ", c);
375403
}
376404

377-
drop_println!(gctx, "{}", name);
405+
drop_println!(ws.gctx(), "{}", name);
378406
}
379407
}
380408

409+
let (max_display_depth, filter_non_workspace_member) = match display_depth {
410+
DisplayDepth::MaxDisplayDepth(max) => (max, false),
411+
DisplayDepth::Workspace => (u32::MAX, true),
412+
};
413+
381414
// Current level exceeds maximum display depth. Skip.
382415
if levels_continue.len() + 1 > max_display_depth as usize {
383416
return;
@@ -389,6 +422,9 @@ fn print_dependencies<'a>(
389422
// Filter out packages to prune.
390423
match graph.node(**dep) {
391424
Node::Package { package_id, .. } => {
425+
if filter_non_workspace_member && !ws.is_member_id(*package_id) {
426+
return false;
427+
}
392428
!pkgs_to_prune.iter().any(|spec| spec.matches(*package_id))
393429
}
394430
_ => true,
@@ -399,15 +435,15 @@ fn print_dependencies<'a>(
399435
while let Some(dependency) = it.next() {
400436
levels_continue.push(it.peek().is_some());
401437
print_node(
402-
gctx,
438+
ws,
403439
graph,
404440
*dependency,
405441
format,
406442
symbols,
407443
pkgs_to_prune,
408444
prefix,
409445
no_dedupe,
410-
max_display_depth,
446+
display_depth,
411447
visited_deps,
412448
levels_continue,
413449
print_stack,

src/doc/man/cargo-tree.md

+3
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ Prune the given package from the display of the dependency tree.
9696
{{#option "`--depth` _depth_" }}
9797
Maximum display depth of the dependency tree. A depth of 1 displays the direct
9898
dependencies, for example.
99+
100+
If the given value is `workspace`, only shows the dependencies that are member
101+
of the current workspace, instead.
99102
{{/option}}
100103

101104
{{#option "`--no-dedupe`" }}

src/doc/man/generated_txt/cargo-tree.txt

+3
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ OPTIONS
8585
Maximum display depth of the dependency tree. A depth of 1 displays
8686
the direct dependencies, for example.
8787

88+
If the given value is workspace, only shows the dependencies that
89+
are member of the current workspace, instead.
90+
8891
--no-dedupe
8992
Do not de-duplicate repeated dependencies. Usually, when a package
9093
has already displayed its dependencies, further occurrences will not

src/doc/src/commands/cargo-tree.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ subtree of the package given to <code>-p</code>.</dd>
9191

9292
<dt class="option-term" id="option-cargo-tree---depth"><a class="option-anchor" href="#option-cargo-tree---depth"></a><code>--depth</code> <em>depth</em></dt>
9393
<dd class="option-desc">Maximum display depth of the dependency tree. A depth of 1 displays the direct
94-
dependencies, for example.</dd>
94+
dependencies, for example.</p>
95+
<p>If the given value is <code>workspace</code>, only shows the dependencies that are member
96+
of the current workspace, instead.</dd>
9597

9698

9799
<dt class="option-term" id="option-cargo-tree---no-dedupe"><a class="option-anchor" href="#option-cargo-tree---no-dedupe"></a><code>--no-dedupe</code></dt>

src/etc/man/cargo-tree.1

+3
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ Prune the given package from the display of the dependency tree.
9393
.RS 4
9494
Maximum display depth of the dependency tree. A depth of 1 displays the direct
9595
dependencies, for example.
96+
.sp
97+
If the given value is \fBworkspace\fR, only shows the dependencies that are member
98+
of the current workspace, instead.
9699
.RE
97100
.sp
98101
\fB\-\-no\-dedupe\fR

tests/testsuite/tree.rs

+55
Original file line numberDiff line numberDiff line change
@@ -1846,6 +1846,61 @@ c v1.0.0
18461846
.run();
18471847
}
18481848

1849+
#[cargo_test]
1850+
fn depth_workspace() {
1851+
Package::new("somedep", "1.0.0").publish();
1852+
Package::new("otherdep", "1.0.0").publish();
1853+
let p = project()
1854+
.file(
1855+
"Cargo.toml",
1856+
r#"
1857+
[workspace]
1858+
members = ["a", "b", "c"]
1859+
"#,
1860+
)
1861+
.file("a/Cargo.toml", &basic_manifest("a", "1.0.0"))
1862+
.file("a/src/lib.rs", "")
1863+
.file(
1864+
"b/Cargo.toml",
1865+
r#"
1866+
[package]
1867+
name = "b"
1868+
version = "0.1.0"
1869+
1870+
[dependencies]
1871+
c = { path = "../c" }
1872+
somedep = "1"
1873+
"#,
1874+
)
1875+
.file("b/src/lib.rs", "")
1876+
.file(
1877+
"c/Cargo.toml",
1878+
r#"
1879+
[package]
1880+
name = "c"
1881+
version = "0.1.0"
1882+
1883+
[dependencies]
1884+
somedep = "1"
1885+
otherdep = "1"
1886+
"#,
1887+
)
1888+
.file("c/src/lib.rs", "")
1889+
.build();
1890+
1891+
p.cargo("tree --depth workspace")
1892+
.with_stdout_data(str![[r#"
1893+
a v1.0.0 ([ROOT]/foo/a)
1894+
1895+
b v0.1.0 ([ROOT]/foo/b)
1896+
└── c v0.1.0 ([ROOT]/foo/c)
1897+
1898+
c v0.1.0 ([ROOT]/foo/c) (*)
1899+
1900+
"#]])
1901+
.run();
1902+
}
1903+
18491904
#[cargo_test]
18501905
fn prune() {
18511906
let p = make_simple_proj();

0 commit comments

Comments
 (0)