diff --git a/src/cargo/ops/tree/graph.rs b/src/cargo/ops/tree/graph.rs index 1b8b1ecbe0c..702c4c1ceb6 100644 --- a/src/cargo/ops/tree/graph.rs +++ b/src/cargo/ops/tree/graph.rs @@ -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}; @@ -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 = }` - 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()`, 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 diff --git a/tests/testsuite/artifact_dep.rs b/tests/testsuite/artifact_dep.rs index db28f921e59..f0a8ce3df96 100644 --- a/tests/testsuite/artifact_dep.rs +++ b/tests/testsuite/artifact_dep.rs @@ -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 = }