Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 19 additions & 20 deletions src/cargo/ops/tree/graph.rs

@weihanglo weihanglo Jun 20, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution!

Is there any existing issue about this regression? Note that our contributing guide favor issue before PR, so that we can make sure the design direction match. See https://doc.crates.io/contrib/process/working-on-cargo.html.

Also, we also encourage atomic commit pattern, especially for regression. For example, you will commit regression tests capturing the problematic beahvior. The next commit comes with the fix and the snapshot update. The diff of the snapshot will show the behavior change.

See https://epage.github.io/dev/pr-style/#c-test for more about the pattern, and #17123 for a real world example.

(BTW, your agent session may be good enough to understand the those docs and patterns.)

View changes since the review

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. You just opened #17125.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review! I have just rewritten the branch following the atomic commit pattern.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Code for building the graph used by `cargo tree`.

use super::TreeOptions;
use crate::core::compiler::{CompileKind, RustcTargetData};
use crate::core::compiler::{CompileKind, CompileTarget, RustcTargetData};
use crate::core::dependency::DepKind;
use crate::core::resolver::Resolve;
use crate::core::resolver::features::{CliFeatures, FeaturesFor, ResolvedFeatures};
Expand Down Expand Up @@ -486,25 +486,24 @@ fn add_pkg(
let dep_pkg = graph.package_map[&dep_id];

for dep in deps {
let dep_features_for = match dep
.artifact()
.and_then(|artifact| artifact.target())
.and_then(|target| target.to_resolved_compile_target(requested_kind))
{
// Dependency has a `{ …, target = <triple> }`
Some(target) => FeaturesFor::ArtifactDep(target),
// Get the information of the dependent crate from `features_for`.
// If a dependent crate is
//
// * specified as an artifact dep with a `target`, or
// * a host dep,
//
// its transitive deps, including build-deps, need to be built on that target.
None if features_for != FeaturesFor::default() => features_for,
// Dependent crate is a normal dep, then back to old rules:
//
// * normal deps, dev-deps -> inherited target
// * build-deps -> host
// Keep this in sync with `FeatureResolver::deps`: `cargo tree`
// looks up already-resolved features, so it must use the same key.
let dep_features_for = match dep.artifact().and_then(|artifact| artifact.target()) {
// `target = "target"` with a host requested kind is keyed as
// `ArtifactDep(<host triple>)`, not `HostDep`.
Some(artifact_target) => {
match artifact_target.to_resolved_compile_kind(requested_kind) {
CompileKind::Target(target) => FeaturesFor::ArtifactDep(target),
CompileKind::Host => {
let host = CompileTarget::new(
&target_data.rustc.host,
target_data.gctx.cli_unstable().json_target_spec,
)
.expect("host target triple is always valid");
FeaturesFor::ArtifactDep(host)
}
}
}
None => {
if dep.is_build() || dep_pkg.proc_macro() {
FeaturesFor::HostDep
Expand Down
68 changes: 68 additions & 0 deletions tests/testsuite/artifact_dep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1546,6 +1546,74 @@ foo v0.0.0 ([ROOT]/foo)
.run();
}

#[cargo_test]
fn tree_with_build_artifact_dep_inheriting_target() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.0.0"
edition = "2015"
authors = []
resolver = "2"

[build-dependencies]
bar = { path = "bar/", artifact = "cdylib", target = "target" }
"#,
)
.file("src/lib.rs", "")
.file("build.rs", "fn main() {}")
.file(
"bar/Cargo.toml",
r#"
[package]
name = "bar"
version = "0.5.0"
edition = "2015"
authors = []

[lib]
crate-type = ["cdylib"]

[dependencies]
pm = { path = "../pm" }
"#,
)
.file("bar/src/lib.rs", "")
.file(
"pm/Cargo.toml",
r#"
[package]
name = "pm"
version = "0.1.0"
edition = "2015"
authors = []

[lib]
proc-macro = true
"#,
)
.file("pm/src/lib.rs", "")
.build();

p.cargo("check -Z bindeps")
.masquerade_as_nightly_cargo(&["bindeps"])
.run();

p.cargo("tree -Z bindeps")
.masquerade_as_nightly_cargo(&["bindeps"])
.with_stdout_data(str![[r#"
foo v0.0.0 ([ROOT]/foo)
[build-dependencies]
└── bar v0.5.0 ([ROOT]/foo/bar)
└── pm v0.1.0 (proc-macro) ([ROOT]/foo/pm)

"#]])
.run();
}

/// From issue #10593
/// The case where:
/// * artifact dep is { target = <specified> }
Expand Down