Expand dependency info of cargo --metadata
authorAleksey Kladov <aleksey.kladov@gmail.com>
Sat, 5 May 2018 15:07:18 +0000 (18:07 +0300)
committerAleksey Kladov <aleksey.kladov@gmail.com>
Sat, 5 May 2018 21:14:02 +0000 (00:14 +0300)
The old `dependencies` key in `resolve` is deprecated.

Instead, the new `deps` key introduced which lists dependencies kinds
(dev/build/normal) as well as `extern crate` name for the dep.

src/cargo/ops/cargo_output_metadata.rs
tests/testsuite/metadata.rs

index 5cba2d482de7cd7eaba3a047fea0018fe04a7166..c7d3c3ce85122aa9be3962c3829a8ff1b29cc25a 100644 (file)
@@ -1,7 +1,6 @@
-use serde::ser::{self, Serialize};
-
 use core::resolver::Resolve;
-use core::{Package, PackageId, Workspace};
+use core::{Package, PackageId, PackageSet, Workspace};
+use core::dependency;
 use ops::{self, Packages};
 use util::CargoResult;
 
@@ -56,18 +55,19 @@ fn metadata_full(ws: &Workspace, opt: &OutputMetadataOptions) -> CargoResult<Exp
     )?;
     let (packages, resolve) = deps;
 
+    let resolve = MetadataResolve::new(
+        &packages,
+        &resolve,
+        ws.current_opt().map(|pkg| pkg.package_id().clone()),
+    );
     let packages = packages
         .package_ids()
         .map(|i| packages.get(i).map(|p| p.clone()))
         .collect::<CargoResult<Vec<_>>>()?;
-
     Ok(ExportInfo {
         packages,
         workspace_members: ws.members().map(|pkg| pkg.package_id().clone()).collect(),
-        resolve: Some(MetadataResolve {
-            resolve,
-            root: ws.current_opt().map(|pkg| pkg.package_id().clone()),
-        }),
+        resolve: Some(resolve),
         target_directory: ws.target_dir().display().to_string(),
         version: VERSION,
         workspace_root: ws.root().display().to_string(),
@@ -76,42 +76,91 @@ fn metadata_full(ws: &Workspace, opt: &OutputMetadataOptions) -> CargoResult<Exp
 
 #[derive(Serialize)]
 pub struct ExportInfo {
+    /// All packages for this project, with dependencies.
     packages: Vec<Package>,
+    /// Packages which are direct members of the current project.
     workspace_members: Vec<PackageId>,
+    /// A graph of the dependencies between packages.
     resolve: Option<MetadataResolve>,
+    /// The directory where intermediate build artifacts will be stored.
     target_directory: String,
+    /// Version of this JSON format
     version: u32,
+    /// Path to the directory with the project.
     workspace_root: String,
 }
 
-/// Newtype wrapper to provide a custom `Serialize` implementation.
-/// The one from lockfile does not fit because it uses a non-standard
-/// format for `PackageId`s
+// The serialization format is different from lockfile, because
+// here we use different format for `PackageId`s, and give more
+// information about dependencies.
 #[derive(Serialize)]
 struct MetadataResolve {
-    #[serde(rename = "nodes", serialize_with = "serialize_resolve")]
-    resolve: Resolve,
+    /// Dependencies for each package from `ExportInfo::package`.
+    nodes: Vec<Node>,
+    /// Deprecated, use `ExportInfo::workspace_members`.
     root: Option<PackageId>,
 }
 
-fn serialize_resolve<S>(resolve: &Resolve, s: S) -> Result<S::Ok, S::Error>
-where
-    S: ser::Serializer,
-{
-    #[derive(Serialize)]
-    struct Node<'a> {
-        id: &'a PackageId,
-        dependencies: Vec<&'a PackageId>,
-        features: Vec<&'a str>,
-    }
+/// Describes dependencies of a single package.
+#[derive(Serialize)]
+struct Node {
+    /// The id of the package.
+    id: PackageId,
+    /// Deprecated, use `deps` field.
+    dependencies: Vec<PackageId>,
+    /// Dependencies of this package.
+    deps: Vec<Dependency>,
+    /// Features, enabled for this package.
+    features: Vec<String>,
+}
 
-    resolve
-        .iter()
-        .map(|id| Node {
-            id,
-            dependencies: resolve.deps(id).map(|p| p.0).collect(),
-            features: resolve.features_sorted(id),
-        })
-        .collect::<Vec<_>>()
-        .serialize(s)
+/// Describes a single dependency.
+#[derive(Serialize)]
+struct Dependency {
+    /// The id of the dependency.
+    id: PackageId,
+    /// The name used for `extern crate` declaration of this dependency.
+    name: String,
+    /// Is this normal, dev or build dependency
+    kind: dependency::Kind,
+}
+
+impl MetadataResolve {
+    pub fn new(
+        packages: &PackageSet,
+        resolve: &Resolve,
+        root: Option<PackageId>,
+    ) -> MetadataResolve {
+        let nodes = resolve
+            .iter()
+            .map(|pkg| {
+                Node {
+                    id: pkg.clone(),
+                    dependencies: resolve.deps(pkg).map(|(dep, _)| dep.clone()).collect(),
+                    deps: resolve
+                        .deps(pkg)
+                        .flat_map(|(id, deps)| {
+                            let dep_name = packages.get(id).unwrap()
+                                .lib_target().unwrap()
+                                .crate_name();
+                            deps.iter().map(|dep| {
+                                Dependency {
+                                    id: id.clone(),
+                                    name: dep.rename().unwrap_or(&dep_name)
+                                        .to_owned(),
+                                    kind: dep.kind(),
+                                }
+                            }).collect::<Vec<_>>().into_iter()
+                        })
+                        .collect(),
+                    features: resolve
+                        .features_sorted(pkg)
+                        .into_iter()
+                        .map(|s| s.to_string())
+                        .collect(),
+                }
+            })
+            .collect();
+        MetadataResolve { nodes, root }
+    }
 }
index f0b5e104be0e9d40bfb5f1a830782cf8683a8788..de0c0afb41dc0e3ebee8b1645ddaba31173c41f1 100644 (file)
@@ -1,5 +1,6 @@
 use cargotest::support::registry::Package;
 use cargotest::support::{basic_bin_manifest, basic_lib_manifest, execs, main_file, project};
+use cargotest::ChannelChanger;
 use hamcrest::assert_that;
 
 #[test]
@@ -53,6 +54,7 @@ fn cargo_metadata_simple() {
             "nodes": [
                 {
                     "dependencies": [],
+                    "deps": [],
                     "features": [],
                     "id": "foo 0.5.0 (path+file:[..]foo)"
                 }
@@ -67,6 +69,204 @@ fn cargo_metadata_simple() {
     );
 }
 
+#[test]
+fn metadata_with_renamed_crates() {
+    Package::new("bar", "0.1.0").publish();
+    Package::new("bar", "0.2.0").publish();
+
+    let p = project("foo")
+        .file(
+            "Cargo.toml",
+            r#"
+            cargo-features = ["rename-dependency"]
+
+            [project]
+            name = "foo"
+            version = "0.5.0"
+            authors = []
+
+            [dependencies]
+            bar = { version = "0.1.0" }
+            baz = { version = "0.2.0", package = "bar" }
+        "#,
+        )
+        .file(
+            "src/lib.rs",
+            "
+            extern crate bar;
+            extern crate baz;
+        ",
+        )
+        .build();
+
+    assert_that(
+        p.cargo("metadata").masquerade_as_nightly_cargo(),
+        execs().with_json(
+            r#"
+    {
+        "packages": "{...}",
+        "workspace_members": [
+            "foo 0.5.0 (path+file:[..]foo)"
+        ],
+        "resolve": {
+            "nodes": [
+                {
+                    "dependencies": [],
+                    "deps": [],
+                    "features": [],
+                    "id": "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)"
+                },
+                {
+                    "dependencies": [
+                        "bar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)"
+                    ],
+                    "deps": [
+                        {
+                            "name": "baz",
+                            "id": "bar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                            "kind": null
+                        },
+                        {
+                            "name": "bar",
+                            "id": "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                            "kind": null
+                        }
+                    ],
+                    "features": [],
+                    "id": "foo 0.5.0 (path+file://[..])"
+                },
+                {
+                    "dependencies": [],
+                    "deps": [],
+                    "features": [],
+                    "id": "bar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)"
+                }
+            ],
+            "root": "foo 0.5.0 (path+file:[..]foo)"
+        },
+        "target_directory": "[..]foo[/]target",
+        "version": 1,
+        "workspace_root": "[..][/]foo"
+    }"#,
+        ),
+    );
+}
+
+#[test]
+fn metadata_dep_kinds() {
+    Package::new("bar", "0.1.0").publish();
+
+    let p = project("foo")
+        .file(
+            "Cargo.toml",
+            r#"
+            [project]
+            name = "foo"
+            version = "0.5.0"
+            authors = []
+
+            [dependencies]
+            bar = { version = "0.1.0", optional = true }
+
+            [build-dependencies]
+            bar = { version = "0.1.0"}
+        "#,
+        )
+        .file("src/lib.rs", "")
+        .build();
+
+    assert_that(
+        p.cargo("metadata"),
+        execs().with_json(
+            r#"
+    {
+        "packages": "{...}",
+        "workspace_members": [
+            "foo 0.5.0 (path+file:[..]foo)"
+        ],
+        "resolve": {
+            "nodes": [
+                {
+                    "dependencies": [],
+                    "deps": [],
+                    "features": [],
+                    "id": "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)"
+                },
+                {
+                    "dependencies": [
+                        "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)"
+                    ],
+                    "deps": [
+                        {
+                            "name": "bar",
+                            "id": "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                            "kind": "build"
+                        }
+                    ],
+                    "features": [],
+                    "id": "foo 0.5.0 (path+file://[..])"
+                }
+            ],
+            "root": "foo 0.5.0 (path+file:[..]foo)"
+        },
+        "target_directory": "[..]foo[/]target",
+        "version": 1,
+        "workspace_root": "[..][/]foo"
+    }"#,
+        ),
+    );
+
+    assert_that(
+        p.cargo("metadata --all-features"),
+        execs().with_json(
+            r#"
+    {
+        "packages": "{...}",
+        "workspace_members": [
+            "foo 0.5.0 (path+file:[..]foo)"
+        ],
+        "resolve": {
+            "nodes": [
+                {
+                    "dependencies": [],
+                    "deps": [],
+                    "features": [],
+                    "id": "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)"
+                },
+                {
+                    "dependencies": [
+                        "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)"
+                    ],
+                    "deps": [
+                        {
+                            "name": "bar",
+                            "id": "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                            "kind": "build"
+                        },
+                        {
+                            "name": "bar",
+                            "id": "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                            "kind": null
+                        }
+                    ],
+                    "features": [ "bar" ],
+                    "id": "foo 0.5.0 (path+file://[..])"
+                }
+            ],
+            "root": "foo 0.5.0 (path+file:[..]foo)"
+        },
+        "target_directory": "[..]foo[/]target",
+        "version": 1,
+        "workspace_root": "[..][/]foo"
+    }"#,
+        ),
+    );
+
+}
+
+
+
 #[test]
 fn cargo_metadata_warns_on_implicit_version() {
     let p = project("foo")
@@ -144,6 +344,7 @@ crate-type = ["lib", "staticlib"]
             "nodes": [
                 {
                     "dependencies": [],
+                    "deps": [],
                     "features": [],
                     "id": "foo 0.5.0 (path+file:[..]foo)"
                 }
@@ -225,6 +426,7 @@ optional_feat = []
             "nodes": [
                 {
                     "dependencies": [],
+                    "deps": [],
                     "features": [
                       "default",
                       "default_feat"
@@ -399,6 +601,13 @@ fn cargo_metadata_with_deps_and_version() {
                     "dependencies": [
                         "bar 0.0.1 (registry+[..])"
                     ],
+                    "deps": [
+                        {
+                            "id": "bar 0.0.1 (registry+[..])",
+                            "name": "bar",
+                            "kind": null
+                        }
+                    ],
                     "features": [],
                     "id": "foo 0.5.0 (path+file:[..]foo)"
                 },
@@ -406,11 +615,19 @@ fn cargo_metadata_with_deps_and_version() {
                     "dependencies": [
                         "baz 0.0.1 (registry+[..])"
                     ],
+                    "deps": [
+                        {
+                            "id": "baz 0.0.1 (registry+[..])",
+                            "name": "baz",
+                            "kind": null
+                        }
+                    ],
                     "features": [],
                     "id": "bar 0.0.1 (registry+[..])"
                 },
                 {
                     "dependencies": [],
+                    "deps": [],
                     "features": [],
                     "id": "baz 0.0.1 (registry+[..])"
                 }
@@ -491,7 +708,8 @@ name = "ex"
                 {
                     "id": "foo 0.1.0 (path+file:[..]foo)",
                     "features": [],
-                    "dependencies": []
+                    "dependencies": [],
+                    "deps": []
                 }
             ]
         },
@@ -570,7 +788,8 @@ crate-type = ["rlib", "dylib"]
                 {
                     "id": "foo 0.1.0 (path+file:[..]foo)",
                     "features": [],
-                    "dependencies": []
+                    "dependencies": [],
+                    "deps": []
                 }
             ]
         },
@@ -666,11 +885,13 @@ fn workspace_metadata() {
             "nodes": [
                 {
                     "dependencies": [],
+                    "deps": [],
                     "features": [],
                     "id": "baz 0.5.0 (path+file:[..]baz)"
                 },
                 {
                     "dependencies": [],
+                    "deps": [],
                     "features": [],
                     "id": "bar 0.5.0 (path+file:[..]bar)"
                 }