-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;
)?;
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(),
#[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 }
+ }
}
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]
"nodes": [
{
"dependencies": [],
+ "deps": [],
"features": [],
"id": "foo 0.5.0 (path+file:[..]foo)"
}
);
}
+#[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")
"nodes": [
{
"dependencies": [],
+ "deps": [],
"features": [],
"id": "foo 0.5.0 (path+file:[..]foo)"
}
"nodes": [
{
"dependencies": [],
+ "deps": [],
"features": [
"default",
"default_feat"
"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)"
},
"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+[..])"
}
{
"id": "foo 0.1.0 (path+file:[..]foo)",
"features": [],
- "dependencies": []
+ "dependencies": [],
+ "deps": []
}
]
},
{
"id": "foo 0.1.0 (path+file:[..]foo)",
"features": [],
- "dependencies": []
+ "dependencies": [],
+ "deps": []
}
]
},
"nodes": [
{
"dependencies": [],
+ "deps": [],
"features": [],
"id": "baz 0.5.0 (path+file:[..]baz)"
},
{
"dependencies": [],
+ "deps": [],
"features": [],
"id": "bar 0.5.0 (path+file:[..]bar)"
}