Skip to content

Commit e5ec492

Browse files
committed
cargo-metadata: support -Zbindeps info
1 parent ca626c5 commit e5ec492

File tree

4 files changed

+213
-23
lines changed

4 files changed

+213
-23
lines changed

src/cargo/ops/cargo_output_metadata.rs

Lines changed: 113 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::core::compiler::{CompileKind, RustcTargetData};
2-
use crate::core::dependency::DepKind;
2+
use crate::core::dependency::{ArtifactKind, DepKind};
33
use crate::core::package::SerializedPackage;
44
use crate::core::resolver::{features::CliFeatures, HasDevUnits, Resolve};
5-
use crate::core::{Dependency, Package, PackageId, Workspace};
5+
use crate::core::{Package, PackageId, Target, Workspace};
66
use crate::ops::{self, Packages};
77
use crate::util::interning::InternedString;
88
use crate::util::CargoResult;
@@ -90,15 +90,26 @@ struct Dep {
9090
struct DepKindInfo {
9191
kind: DepKind,
9292
target: Option<Platform>,
93-
}
9493

95-
impl From<&Dependency> for DepKindInfo {
96-
fn from(dep: &Dependency) -> DepKindInfo {
97-
DepKindInfo {
98-
kind: dep.kind(),
99-
target: dep.platform().cloned(),
100-
}
101-
}
94+
// vvvvv The fields below are introduced for `-Z bindeps`.
95+
/// Artifact's crate type, e.g. staticlib, cdylib, bin...
96+
#[serde(skip_serializing_if = "Option::is_none")]
97+
artifact: Option<&'static str>,
98+
/// What the manifest calls the crate.
99+
///
100+
/// A renamed dependency will show the rename instead of original name.
101+
#[serde(skip_serializing_if = "Option::is_none")]
102+
extern_name: Option<InternedString>,
103+
/// Equivalent to `{ target = "…" }` in an artifact dependency requirement.
104+
///
105+
/// * If the target points to a custom target JSON file, the path will be absolute.
106+
/// * If the target is a build assumed target `{ target = "target" }`, it will show as `<target>`.
107+
#[serde(skip_serializing_if = "Option::is_none")]
108+
compile_target: Option<InternedString>,
109+
/// Executable name for an artifact binary dependency.
110+
#[serde(skip_serializing_if = "Option::is_none")]
111+
bin_name: Option<String>,
112+
// ^^^^^ The fields above are introduced for `-Z bindeps`.
102113
}
103114

104115
/// Builds the resolve graph as it will be displayed to the user.
@@ -206,22 +217,101 @@ fn build_resolve_graph_r(
206217
}
207218
})
208219
.filter_map(|(dep_id, deps)| {
209-
let mut dep_kinds: Vec<_> = deps.iter().map(DepKindInfo::from).collect();
220+
let mut dep_kinds = Vec::new();
221+
222+
let targets = package_map[&dep_id].targets();
223+
224+
// Try to get the extern name for lib, or crate name for bins.
225+
let extern_name = |target| {
226+
resolve
227+
.extern_crate_name_and_dep_name(pkg_id, dep_id, target)
228+
.map(|(ext_crate_name, _)| ext_crate_name)
229+
.ok()
230+
};
231+
232+
let lib_target_name = targets.iter().find(|t| t.is_lib()).map(extern_name);
233+
234+
for dep in deps.iter() {
235+
let mut include_lib = || {
236+
dep_kinds.push(DepKindInfo {
237+
kind: dep.kind(),
238+
target: dep.platform().cloned(),
239+
artifact: None,
240+
extern_name: lib_target_name,
241+
compile_target: None,
242+
bin_name: None,
243+
});
244+
};
245+
246+
// When we do have a library target, include them in deps if...
247+
match (dep.artifact(), lib_target_name) {
248+
// it is also an artifact dep with `{ …, lib = true }`
249+
(Some(a), Some(_)) if a.is_lib() => include_lib(),
250+
// it is not an artifact dep at all
251+
(None, Some(_)) => include_lib(),
252+
_ => {}
253+
}
254+
255+
// No need to proceed if there is no artifact dependency.
256+
let Some(artifact_requirements) = dep.artifact() else {
257+
continue;
258+
};
259+
260+
let compile_target = match artifact_requirements.target() {
261+
Some(t) => t
262+
.to_compile_target()
263+
.map(|t| t.rustc_target())
264+
// Given that Cargo doesn't know which target it should resolve to,
265+
// when an artifact dep is specified with { target = "target" },
266+
// keep it with a special "<target>" string,
267+
.or_else(|| Some(InternedString::new("<target>"))),
268+
None => None,
269+
};
270+
271+
let mut extend = |kind: &ArtifactKind, filter: &dyn Fn(&&Target) -> bool| {
272+
let iter = targets.iter().filter(filter).map(|target| DepKindInfo {
273+
kind: dep.kind(),
274+
target: dep.platform().cloned(),
275+
artifact: Some(kind.crate_type()),
276+
extern_name: extern_name(target),
277+
compile_target,
278+
bin_name: target.is_bin().then(|| target.name().to_string()),
279+
});
280+
dep_kinds.extend(iter);
281+
};
282+
283+
for kind in artifact_requirements.kinds() {
284+
match kind {
285+
ArtifactKind::Cdylib => extend(kind, &|t| t.is_cdylib()),
286+
ArtifactKind::Staticlib => extend(kind, &|t| t.is_staticlib()),
287+
ArtifactKind::AllBinaries => extend(kind, &|t| t.is_bin()),
288+
ArtifactKind::SelectedBinary(bin_name) => {
289+
extend(kind, &|t| t.is_bin() && t.name() == bin_name.as_str())
290+
}
291+
};
292+
}
293+
}
294+
210295
dep_kinds.sort();
211-
package_map
212-
.get(&dep_id)
213-
.and_then(|pkg| pkg.targets().iter().find(|t| t.is_lib()))
214-
.and_then(|lib_target| {
215-
resolve
216-
.extern_crate_name_and_dep_name(pkg_id, dep_id, lib_target)
217-
.map(|(ext_crate_name, _)| ext_crate_name)
218-
.ok()
219-
})
220-
.map(|name| Dep {
296+
297+
let pkg = normalize_id(dep_id);
298+
299+
match (lib_target_name, dep_kinds.len()) {
300+
(Some(name), _) => Some(Dep {
221301
name,
222-
pkg: normalize_id(dep_id),
302+
pkg,
223303
dep_kinds,
224-
})
304+
}),
305+
// No lib target exists but contains artifact deps.
306+
(None, 1..) => Some(Dep {
307+
name: InternedString::new(""),
308+
pkg,
309+
dep_kinds,
310+
}),
311+
// No lib or artifact dep exists.
312+
// Ususally this mean parent depending on non-lib bin crate.
313+
(None, _) => None,
314+
}
225315
})
226316
.collect();
227317
let dumb_deps: Vec<PackageId> = deps.iter().map(|dep| normalize_id(dep.pkg)).collect();

tests/testsuite/git.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3393,6 +3393,7 @@ fn metadata_master_consistency() {
33933393
"pkg": "bar 1.0.0 (__BAR_SOURCE__#__BAR_HASH__)",
33943394
"dep_kinds": [
33953395
{
3396+
"extern_name": "bar",
33963397
"kind": null,
33973398
"target": null
33983399
}

0 commit comments

Comments
 (0)