mirror of
https://github.com/rust-lang/cargo.git
synced 2025-09-28 11:20:36 +00:00
feat(tree): Add --depth public
behind -Zunstable-options
(#15243)
### What does this PR try to resolve? I was investigating some issues around public dependency lints and wanted to see the structure of the public dependencies and had the idea to add this with us having added `--depth workspace`. See https://github.com/rust-lang/rust/issues/119428#issuecomment-2686384070 for some example output (comparing `cargo tree` with `cargo tree --depth public`) ### How should we test and review this PR? ### Additional information
This commit is contained in:
commit
73b3092fd5
@ -78,6 +78,7 @@ impl Node {
|
||||
pub struct Edge {
|
||||
kind: EdgeKind,
|
||||
node: NodeId,
|
||||
public: bool,
|
||||
}
|
||||
|
||||
impl Edge {
|
||||
@ -88,6 +89,10 @@ impl Edge {
|
||||
pub fn node(&self) -> NodeId {
|
||||
self.node
|
||||
}
|
||||
|
||||
pub fn public(&self) -> bool {
|
||||
self.public
|
||||
}
|
||||
}
|
||||
|
||||
/// The kind of edge, for separating dependencies into different sections.
|
||||
@ -105,7 +110,7 @@ pub enum EdgeKind {
|
||||
///
|
||||
/// The value is a `Vec` because each edge kind can have multiple outgoing
|
||||
/// edges. For example, package "foo" can have multiple normal dependencies.
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
struct Edges(HashMap<EdgeKind, Vec<Edge>>);
|
||||
|
||||
impl Edges {
|
||||
@ -135,6 +140,7 @@ impl Edges {
|
||||
}
|
||||
|
||||
/// A graph of dependencies.
|
||||
#[derive(Debug)]
|
||||
pub struct Graph<'a> {
|
||||
nodes: Vec<Node>,
|
||||
/// The indexes of `edges` correspond to the `nodes`. That is, `edges[0]`
|
||||
@ -268,6 +274,7 @@ impl<'a> Graph<'a> {
|
||||
let new_edge = Edge {
|
||||
kind: edge.kind(),
|
||||
node: new_to_index,
|
||||
public: edge.public(),
|
||||
};
|
||||
new_graph.edges_mut(new_from).add_edge(new_edge);
|
||||
}
|
||||
@ -290,6 +297,7 @@ impl<'a> Graph<'a> {
|
||||
let new_edge = Edge {
|
||||
kind: edge.kind(),
|
||||
node: NodeId::new(from_idx, self.nodes[from_idx].name()),
|
||||
public: edge.public(),
|
||||
};
|
||||
new_edges[edge.node().index].add_edge(new_edge);
|
||||
}
|
||||
@ -514,6 +522,7 @@ fn add_pkg(
|
||||
let new_edge = Edge {
|
||||
kind: EdgeKind::Dep(dep.kind()),
|
||||
node: dep_index,
|
||||
public: dep.is_public(),
|
||||
};
|
||||
if opts.graph_features {
|
||||
// Add the dependency node with feature nodes in-between.
|
||||
@ -577,12 +586,14 @@ fn add_feature(
|
||||
let from_edge = Edge {
|
||||
kind: to.kind(),
|
||||
node: node_index,
|
||||
public: to.public(),
|
||||
};
|
||||
graph.edges_mut(from).add_edge(from_edge);
|
||||
}
|
||||
let to_edge = Edge {
|
||||
kind: EdgeKind::Feature,
|
||||
node: to.node(),
|
||||
public: true,
|
||||
};
|
||||
graph.edges_mut(node_index).add_edge(to_edge);
|
||||
(missing, node_index)
|
||||
@ -620,6 +631,7 @@ fn add_cli_features(
|
||||
let feature_edge = Edge {
|
||||
kind: EdgeKind::Feature,
|
||||
node: package_index,
|
||||
public: true,
|
||||
};
|
||||
let index = add_feature(graph, feature, None, feature_edge).1;
|
||||
graph.cli_features.insert(index);
|
||||
@ -654,6 +666,7 @@ fn add_cli_features(
|
||||
let feature_edge = Edge {
|
||||
kind: EdgeKind::Feature,
|
||||
node: package_index,
|
||||
public: true,
|
||||
};
|
||||
let index = add_feature(graph, dep_name, None, feature_edge).1;
|
||||
graph.cli_features.insert(index);
|
||||
@ -661,6 +674,7 @@ fn add_cli_features(
|
||||
let dep_edge = Edge {
|
||||
kind: EdgeKind::Feature,
|
||||
node: dep_index,
|
||||
public: true,
|
||||
};
|
||||
let index = add_feature(graph, dep_feature, None, dep_edge).1;
|
||||
graph.cli_features.insert(index);
|
||||
@ -721,6 +735,7 @@ fn add_feature_rec(
|
||||
let feature_edge = Edge {
|
||||
kind: EdgeKind::Feature,
|
||||
node: package_index,
|
||||
public: true,
|
||||
};
|
||||
let (missing, feat_index) = add_feature(graph, *dep_name, Some(from), feature_edge);
|
||||
// Don't recursive if the edge already exists to deal with cycles.
|
||||
@ -771,12 +786,14 @@ fn add_feature_rec(
|
||||
let feature_edge = Edge {
|
||||
kind: EdgeKind::Feature,
|
||||
node: package_index,
|
||||
public: true,
|
||||
};
|
||||
add_feature(graph, *dep_name, Some(from), feature_edge);
|
||||
}
|
||||
let dep_edge = Edge {
|
||||
kind: EdgeKind::Feature,
|
||||
node: dep_index,
|
||||
public: true,
|
||||
};
|
||||
let (missing, feat_index) =
|
||||
add_feature(graph, *dep_feature, Some(from), dep_edge);
|
||||
|
@ -91,6 +91,7 @@ impl FromStr for Prefix {
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum DisplayDepth {
|
||||
MaxDisplayDepth(u32),
|
||||
Public,
|
||||
Workspace,
|
||||
}
|
||||
|
||||
@ -100,6 +101,7 @@ impl FromStr for DisplayDepth {
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"workspace" => Ok(Self::Workspace),
|
||||
"public" => Ok(Self::Public),
|
||||
s => s.parse().map(Self::MaxDisplayDepth).map_err(|_| {
|
||||
clap::Error::raw(
|
||||
clap::error::ErrorKind::ValueValidation,
|
||||
@ -282,7 +284,7 @@ fn print(
|
||||
&mut visited_deps,
|
||||
&mut levels_continue,
|
||||
&mut print_stack,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -302,7 +304,7 @@ fn print_node<'a>(
|
||||
visited_deps: &mut HashSet<NodeId>,
|
||||
levels_continue: &mut Vec<(anstyle::Style, bool)>,
|
||||
print_stack: &mut Vec<NodeId>,
|
||||
) {
|
||||
) -> CargoResult<()> {
|
||||
let new = no_dedupe || visited_deps.insert(node_index);
|
||||
|
||||
match prefix {
|
||||
@ -343,7 +345,7 @@ fn print_node<'a>(
|
||||
drop_println!(ws.gctx(), "{}{}", format.display(graph, node_index), star);
|
||||
|
||||
if !new || in_cycle {
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
print_stack.push(node_index);
|
||||
|
||||
@ -367,9 +369,11 @@ fn print_node<'a>(
|
||||
levels_continue,
|
||||
print_stack,
|
||||
kind,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
print_stack.pop();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Prints all the dependencies of a package for the given dependency kind.
|
||||
@ -387,10 +391,10 @@ fn print_dependencies<'a>(
|
||||
levels_continue: &mut Vec<(anstyle::Style, bool)>,
|
||||
print_stack: &mut Vec<NodeId>,
|
||||
kind: &EdgeKind,
|
||||
) {
|
||||
) -> CargoResult<()> {
|
||||
let deps = graph.edges_of_kind(node_index, kind);
|
||||
if deps.is_empty() {
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let name = match kind {
|
||||
@ -415,14 +419,20 @@ fn print_dependencies<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
let (max_display_depth, filter_non_workspace_member) = match display_depth {
|
||||
DisplayDepth::MaxDisplayDepth(max) => (max, false),
|
||||
DisplayDepth::Workspace => (u32::MAX, true),
|
||||
let (max_display_depth, filter_non_workspace_member, filter_private) = match display_depth {
|
||||
DisplayDepth::MaxDisplayDepth(max) => (max, false, false),
|
||||
DisplayDepth::Workspace => (u32::MAX, true, false),
|
||||
DisplayDepth::Public => {
|
||||
if !ws.gctx().cli_unstable().unstable_options {
|
||||
anyhow::bail!("`--depth public` requires `-Zunstable-options`")
|
||||
}
|
||||
(u32::MAX, false, true)
|
||||
}
|
||||
};
|
||||
|
||||
// Current level exceeds maximum display depth. Skip.
|
||||
if levels_continue.len() + 1 > max_display_depth as usize {
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut it = deps
|
||||
@ -434,9 +444,17 @@ fn print_dependencies<'a>(
|
||||
if filter_non_workspace_member && !ws.is_member_id(*package_id) {
|
||||
return false;
|
||||
}
|
||||
if filter_private && !dep.public() {
|
||||
return false;
|
||||
}
|
||||
!pkgs_to_prune.iter().any(|spec| spec.matches(*package_id))
|
||||
}
|
||||
_ => true,
|
||||
Node::Feature { .. } => {
|
||||
if filter_private && !dep.public() {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
})
|
||||
.peekable();
|
||||
@ -457,9 +475,11 @@ fn print_dependencies<'a>(
|
||||
visited_deps,
|
||||
levels_continue,
|
||||
print_stack,
|
||||
);
|
||||
)?;
|
||||
levels_continue.pop();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn edge_line_color(kind: EdgeKind) -> anstyle::Style {
|
||||
|
@ -1901,6 +1901,138 @@ c v0.1.0 ([ROOT]/foo/c) (*)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")]
|
||||
fn depth_public() {
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[workspace]
|
||||
members = ["diamond", "left-pub", "right-priv", "dep"]
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"diamond/Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["public-dependency"]
|
||||
|
||||
[package]
|
||||
name = "diamond"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
left-pub = { path = "../left-pub", public = true }
|
||||
right-priv = { path = "../right-priv", public = true }
|
||||
"#,
|
||||
)
|
||||
.file("diamond/src/lib.rs", "")
|
||||
.file(
|
||||
"left-pub/Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["public-dependency"]
|
||||
|
||||
[package]
|
||||
name = "left-pub"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
dep = { path = "../dep", public = true }
|
||||
"#,
|
||||
)
|
||||
.file("left-pub/src/lib.rs", "")
|
||||
.file(
|
||||
"right-priv/Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "right-priv"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
dep = { path = "../dep" }
|
||||
"#,
|
||||
)
|
||||
.file("right-priv/src/lib.rs", "")
|
||||
.file(
|
||||
"dep/Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "dep"
|
||||
version = "0.1.0"
|
||||
"#,
|
||||
)
|
||||
.file("dep/src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("tree --depth public")
|
||||
.masquerade_as_nightly_cargo(&["public-dependency", "depth-public"])
|
||||
.with_status(101)
|
||||
.with_stderr_data(str![[r#"
|
||||
[ERROR] `--depth public` requires `-Zunstable-options`
|
||||
|
||||
"#]])
|
||||
.run();
|
||||
|
||||
p.cargo("tree --depth public -p left-pub")
|
||||
.arg("-Zunstable-options")
|
||||
.masquerade_as_nightly_cargo(&["public-dependency", "depth-public"])
|
||||
.with_stdout_data(str![[r#"
|
||||
left-pub v0.1.0 ([ROOT]/foo/left-pub)
|
||||
└── dep v0.1.0 ([ROOT]/foo/dep)
|
||||
|
||||
"#]])
|
||||
.run();
|
||||
|
||||
p.cargo("tree --depth public -p right-priv")
|
||||
.arg("-Zunstable-options")
|
||||
.masquerade_as_nightly_cargo(&["public-dependency", "depth-public"])
|
||||
.with_stdout_data(str![[r#"
|
||||
right-priv v0.1.0 ([ROOT]/foo/right-priv)
|
||||
|
||||
"#]])
|
||||
.run();
|
||||
|
||||
p.cargo("tree --depth public -p diamond")
|
||||
.arg("-Zunstable-options")
|
||||
.masquerade_as_nightly_cargo(&["public-dependency", "depth-public"])
|
||||
.with_stdout_data(str![[r#"
|
||||
diamond v0.1.0 ([ROOT]/foo/diamond)
|
||||
├── left-pub v0.1.0 ([ROOT]/foo/left-pub)
|
||||
│ └── dep v0.1.0 ([ROOT]/foo/dep)
|
||||
└── right-priv v0.1.0 ([ROOT]/foo/right-priv)
|
||||
|
||||
"#]])
|
||||
.run();
|
||||
|
||||
p.cargo("tree --depth public")
|
||||
.arg("-Zunstable-options")
|
||||
.masquerade_as_nightly_cargo(&["public-dependency", "depth-public"])
|
||||
.with_stdout_data(str![[r#"
|
||||
dep v0.1.0 ([ROOT]/foo/dep)
|
||||
|
||||
diamond v0.1.0 ([ROOT]/foo/diamond)
|
||||
├── left-pub v0.1.0 ([ROOT]/foo/left-pub)
|
||||
│ └── dep v0.1.0 ([ROOT]/foo/dep)
|
||||
└── right-priv v0.1.0 ([ROOT]/foo/right-priv)
|
||||
|
||||
left-pub v0.1.0 ([ROOT]/foo/left-pub) (*)
|
||||
|
||||
right-priv v0.1.0 ([ROOT]/foo/right-priv) (*)
|
||||
|
||||
"#]])
|
||||
.run();
|
||||
|
||||
p.cargo("tree --depth public --invert dep")
|
||||
.arg("-Zunstable-options")
|
||||
.masquerade_as_nightly_cargo(&["public-dependency", "depth-public"])
|
||||
.with_stdout_data(str![[r#"
|
||||
dep v0.1.0 ([ROOT]/foo/dep)
|
||||
└── left-pub v0.1.0 ([ROOT]/foo/left-pub)
|
||||
└── diamond v0.1.0 ([ROOT]/foo/diamond)
|
||||
|
||||
"#]])
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn prune() {
|
||||
let p = make_simple_proj();
|
||||
|
@ -350,3 +350,164 @@ foo v0.1.0 ([ROOT]/foo)
|
||||
"#]])
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")]
|
||||
fn depth_public_no_features() {
|
||||
Package::new("pub-defaultdep", "1.0.0").publish();
|
||||
Package::new("priv-defaultdep", "1.0.0").publish();
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["public-dependency"]
|
||||
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
pub-defaultdep = { version = "1.0.0", public = true }
|
||||
priv-defaultdep = "1.0.0"
|
||||
"#,
|
||||
)
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("tree -e features --depth public")
|
||||
.arg("-Zunstable-options")
|
||||
.masquerade_as_nightly_cargo(&["public-dependency", "depth-public"])
|
||||
.with_stdout_data(str![[r#"
|
||||
foo v0.1.0 ([ROOT]/foo)
|
||||
└── pub-defaultdep feature "default"
|
||||
└── pub-defaultdep v1.0.0
|
||||
|
||||
"#]])
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")]
|
||||
fn depth_public_transitive_features() {
|
||||
Package::new("pub-defaultdep", "1.0.0")
|
||||
.feature("default", &["f1"])
|
||||
.feature("f1", &["f2"])
|
||||
.feature("f2", &["optdep"])
|
||||
.add_dep(Dependency::new("optdep", "1.0").optional(true).public(true))
|
||||
.publish();
|
||||
Package::new("priv-defaultdep", "1.0.0")
|
||||
.feature("default", &["f1"])
|
||||
.feature("f1", &["f2"])
|
||||
.feature("f2", &["optdep"])
|
||||
.add_dep(Dependency::new("optdep", "1.0").optional(true))
|
||||
.publish();
|
||||
Package::new("optdep", "1.0.0")
|
||||
.feature("default", &["f"])
|
||||
.feature("f", &[])
|
||||
.publish();
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["public-dependency"]
|
||||
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
pub-defaultdep = { version = "1.0.0", public = true }
|
||||
priv-defaultdep = { version = "1.0.0", public = true }
|
||||
"#,
|
||||
)
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("tree -e features --depth public")
|
||||
.arg("-Zunstable-options")
|
||||
.masquerade_as_nightly_cargo(&["public-dependency", "depth-public"])
|
||||
.with_stdout_data(str![[r#"
|
||||
foo v0.1.0 ([ROOT]/foo)
|
||||
├── priv-defaultdep feature "default"
|
||||
│ ├── priv-defaultdep v1.0.0
|
||||
│ └── priv-defaultdep feature "f1"
|
||||
│ ├── priv-defaultdep v1.0.0 (*)
|
||||
│ └── priv-defaultdep feature "f2"
|
||||
│ ├── priv-defaultdep v1.0.0 (*)
|
||||
│ └── priv-defaultdep feature "optdep"
|
||||
│ └── priv-defaultdep v1.0.0 (*)
|
||||
└── pub-defaultdep feature "default"
|
||||
├── pub-defaultdep v1.0.0
|
||||
│ └── optdep feature "default"
|
||||
│ ├── optdep v1.0.0
|
||||
│ └── optdep feature "f"
|
||||
│ └── optdep v1.0.0
|
||||
└── pub-defaultdep feature "f1"
|
||||
├── pub-defaultdep v1.0.0 (*)
|
||||
└── pub-defaultdep feature "f2"
|
||||
├── pub-defaultdep v1.0.0 (*)
|
||||
└── pub-defaultdep feature "optdep"
|
||||
└── pub-defaultdep v1.0.0 (*)
|
||||
|
||||
"#]])
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")]
|
||||
fn depth_public_cli() {
|
||||
Package::new("priv", "1.0.0").feature("f", &[]).publish();
|
||||
Package::new("pub", "1.0.0").feature("f", &[]).publish();
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["public-dependency"]
|
||||
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
|
||||
[features]
|
||||
priv-indirect = ["priv"]
|
||||
priv = ["dep:priv", "priv?/f"]
|
||||
pub-indirect = ["pub"]
|
||||
pub = ["dep:pub", "priv?/f"]
|
||||
|
||||
[dependencies]
|
||||
priv = { version = "1.0.0", optional = true }
|
||||
pub = { version = "1.0.0", optional = true, public = true }
|
||||
"#,
|
||||
)
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("tree -e features --depth public")
|
||||
.arg("-Zunstable-options")
|
||||
.masquerade_as_nightly_cargo(&["public-dependency", "depth-public"])
|
||||
.with_stdout_data(str![[r#"
|
||||
foo v0.1.0 ([ROOT]/foo)
|
||||
|
||||
"#]])
|
||||
.run();
|
||||
|
||||
p.cargo("tree -e features --depth public --features pub-indirect")
|
||||
.arg("-Zunstable-options")
|
||||
.masquerade_as_nightly_cargo(&["public-dependency", "depth-public"])
|
||||
.with_stdout_data(str![[r#"
|
||||
foo v0.1.0 ([ROOT]/foo)
|
||||
└── pub feature "default"
|
||||
└── pub v1.0.0
|
||||
|
||||
"#]])
|
||||
.run();
|
||||
|
||||
p.cargo("tree -e features --depth public --features priv-indirect")
|
||||
.arg("-Zunstable-options")
|
||||
.masquerade_as_nightly_cargo(&["public-dependency", "depth-public"])
|
||||
.with_stdout_data(str![[r#"
|
||||
foo v0.1.0 ([ROOT]/foo)
|
||||
|
||||
"#]])
|
||||
.run();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user