Skip to content

Commit 39b1f75

Browse files
committed
Add rename info to Cargo metadata
Previously, `cargo metadata` exposed dependencies info as a graph of package id without any additional information on edges. However, we do want to add some data to edges, including for example, package renames and `cfg` info. Internally, the edges info is represented as a `Vec<Dependnecy>`. We could have exposed that directly, but that risks exposing and ossifying an implementation details. So instead we collapse a `Vec<Dependnecy>` to a single JSON object, which at the moment contains `id` and `rename` info only. It should be possible to add additional fields to that object backwards compatibly. Such representation does not correspond directly to any internal Cargo data structure, but hopefully it shouldn't be to hard to maintain. This representation also does not quite correspond to the "real world", where dependencies are between crate (cargo targets), and not packages. However, because each dep edge is a JSON object, adding a target filter should be possible, which would handle dev-, build-, and potential future bin-specific dependencies backwards-compatibly.
1 parent 2db5b45 commit 39b1f75

File tree

2 files changed

+246
-16
lines changed

2 files changed

+246
-16
lines changed

src/cargo/ops/cargo_output_metadata.rs

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use serde::ser;
22

33
use core::resolver::Resolve;
4-
use core::{Package, PackageId, Workspace};
4+
use core::{Package, PackageId, Workspace, PackageSet};
55
use ops::{self, Packages};
66
use util::CargoResult;
77

@@ -18,7 +18,7 @@ pub struct OutputMetadataOptions {
1818
/// Loads the manifest, resolves the dependencies of the project to the concrete
1919
/// used versions - considering overrides - and writes all dependencies in a JSON
2020
/// format to stdout.
21-
pub fn output_metadata(ws: &Workspace, opt: &OutputMetadataOptions) -> CargoResult<ExportInfo> {
21+
pub fn output_metadata<'a>(ws: &'a Workspace, opt: &OutputMetadataOptions) -> CargoResult<ExportInfo<'a>> {
2222
if opt.version != VERSION {
2323
bail!(
2424
"metadata version {} not supported, only {} is currently supported",
@@ -33,7 +33,7 @@ pub fn output_metadata(ws: &Workspace, opt: &OutputMetadataOptions) -> CargoResu
3333
}
3434
}
3535

36-
fn metadata_no_deps(ws: &Workspace, _opt: &OutputMetadataOptions) -> CargoResult<ExportInfo> {
36+
fn metadata_no_deps<'a>(ws: &'a Workspace, _opt: &OutputMetadataOptions) -> CargoResult<ExportInfo<'a>> {
3737
Ok(ExportInfo {
3838
packages: ws.members().cloned().collect(),
3939
workspace_members: ws.members().map(|pkg| pkg.package_id().clone()).collect(),
@@ -44,7 +44,7 @@ fn metadata_no_deps(ws: &Workspace, _opt: &OutputMetadataOptions) -> CargoResult
4444
})
4545
}
4646

47-
fn metadata_full(ws: &Workspace, opt: &OutputMetadataOptions) -> CargoResult<ExportInfo> {
47+
fn metadata_full<'a>(ws: &'a Workspace, opt: &OutputMetadataOptions) -> CargoResult<ExportInfo<'a>> {
4848
let specs = Packages::All.to_package_id_specs(ws)?;
4949
let deps = ops::resolve_ws_precisely(
5050
ws,
@@ -54,18 +54,18 @@ fn metadata_full(ws: &Workspace, opt: &OutputMetadataOptions) -> CargoResult<Exp
5454
opt.no_default_features,
5555
&specs,
5656
)?;
57-
let (packages, resolve) = deps;
57+
let (package_set, resolve) = deps;
5858

59-
let packages = packages
59+
let packages = package_set
6060
.package_ids()
61-
.map(|i| packages.get(i).map(|p| p.clone()))
61+
.map(|i| package_set.get(i).map(|p| p.clone()))
6262
.collect::<CargoResult<Vec<_>>>()?;
6363

6464
Ok(ExportInfo {
6565
packages,
6666
workspace_members: ws.members().map(|pkg| pkg.package_id().clone()).collect(),
6767
resolve: Some(MetadataResolve {
68-
resolve,
68+
resolve: (package_set, resolve),
6969
root: ws.current_opt().map(|pkg| pkg.package_id().clone()),
7070
}),
7171
target_directory: ws.target_dir().display().to_string(),
@@ -75,10 +75,10 @@ fn metadata_full(ws: &Workspace, opt: &OutputMetadataOptions) -> CargoResult<Exp
7575
}
7676

7777
#[derive(Serialize)]
78-
pub struct ExportInfo {
78+
pub struct ExportInfo<'a> {
7979
packages: Vec<Package>,
8080
workspace_members: Vec<PackageId>,
81-
resolve: Option<MetadataResolve>,
81+
resolve: Option<MetadataResolve<'a>>,
8282
target_directory: String,
8383
version: u32,
8484
workspace_root: String,
@@ -88,28 +88,46 @@ pub struct ExportInfo {
8888
/// The one from lockfile does not fit because it uses a non-standard
8989
/// format for `PackageId`s
9090
#[derive(Serialize)]
91-
struct MetadataResolve {
91+
struct MetadataResolve<'a> {
9292
#[serde(rename = "nodes", serialize_with = "serialize_resolve")]
93-
resolve: Resolve,
93+
resolve: (PackageSet<'a>, Resolve),
9494
root: Option<PackageId>,
9595
}
9696

97-
fn serialize_resolve<S>(resolve: &Resolve, s: S) -> Result<S::Ok, S::Error>
97+
fn serialize_resolve<S>((package_set, resolve): &(PackageSet, Resolve), s: S) -> Result<S::Ok, S::Error>
9898
where
9999
S: ser::Serializer,
100100
{
101+
#[derive(Serialize)]
102+
struct Dep<'a> {
103+
name: Option<String>,
104+
pkg: &'a PackageId
105+
}
106+
101107
#[derive(Serialize)]
102108
struct Node<'a> {
103109
id: &'a PackageId,
104110
dependencies: Vec<&'a PackageId>,
111+
deps: Vec<Dep<'a>>,
105112
features: Vec<&'a str>,
106113
}
107114

108115
s.collect_seq(resolve
109116
.iter()
110117
.map(|id| Node {
111118
id,
112-
dependencies: resolve.deps(id).map(|p| p.0).collect(),
119+
dependencies: resolve.deps(id).map(|(pkg, _deps)| pkg).collect(),
120+
deps: resolve.deps(id)
121+
.map(|(pkg, _deps)| {
122+
let name = package_set.get(pkg).ok()
123+
.and_then(|pkg| pkg.targets().iter().find(|t| t.is_lib()))
124+
.and_then(|lib_target| {
125+
resolve.extern_crate_name(id, pkg, lib_target).ok()
126+
});
127+
128+
Dep { name, pkg }
129+
})
130+
.collect(),
113131
features: resolve.features_sorted(id),
114132
}))
115133
}

0 commit comments

Comments
 (0)