From 42f7462b4b228590967dfca720183de0b23df2bc Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 2 May 2018 20:26:52 +0300 Subject: [PATCH 1/2] Extract Package::lib_target method --- src/cargo/core/compiler/context/unit_dependencies.rs | 4 ++-- src/cargo/core/package.rs | 4 ++++ src/cargo/ops/cargo_compile.rs | 9 ++++++--- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/cargo/core/compiler/context/unit_dependencies.rs b/src/cargo/core/compiler/context/unit_dependencies.rs index e53c3da374d..ba742456a43 100644 --- a/src/cargo/core/compiler/context/unit_dependencies.rs +++ b/src/cargo/core/compiler/context/unit_dependencies.rs @@ -122,7 +122,7 @@ fn compute_deps<'a, 'b, 'cfg>( true }) }).filter_map(|(id, _)| match bcx.get_package(id) { - Ok(pkg) => pkg.targets().iter().find(|t| t.is_lib()).map(|t| { + Ok(pkg) => pkg.lib_target().map(|t| { let mode = check_or_build_mode(&unit.mode, t); let unit = new_unit(bcx, pkg, t, profile_for, unit.kind.for_target(t), mode); Ok((unit, profile_for)) @@ -222,7 +222,7 @@ fn compute_deps_doc<'a, 'cfg>( let mut ret = Vec::new(); for dep in deps { let dep = dep?; - let lib = match dep.targets().iter().find(|t| t.is_lib()) { + let lib = match dep.lib_target() { Some(lib) => lib, None => continue, }; diff --git a/src/cargo/core/package.rs b/src/cargo/core/package.rs index 37ad259f10b..475130506c7 100644 --- a/src/cargo/core/package.rs +++ b/src/cargo/core/package.rs @@ -129,6 +129,10 @@ impl Package { pub fn targets(&self) -> &[Target] { self.manifest.targets() } + /// Get the library target for the package + pub fn lib_target(&self) -> Option<&Target> { + self.targets().iter().find(|t| t.is_lib()) + } /// Get the current package version pub fn version(&self) -> &Version { self.package_id().version() diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index 36eac5b3b41..1d93885958f 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -546,8 +546,11 @@ fn generate_targets<'a>( proposals.extend(default_units); if build_config.mode == CompileMode::Test { // Include the lib as it will be required for doctests. - if let Some(t) = pkg.targets().iter().find(|t| t.is_lib() && t.doctested()) { - proposals.push((new_unit(pkg, t, CompileMode::Build), false)); + match pkg.lib_target() { + Some(t) if t.doctested() => { + proposals.push((new_unit(pkg, t, CompileMode::Build), false)); + } + _ => {} } } } @@ -560,7 +563,7 @@ fn generate_targets<'a>( ref benches, } => { if lib { - if let Some(target) = pkg.targets().iter().find(|t| t.is_lib()) { + if let Some(target) = pkg.lib_target() { proposals.push((new_unit(pkg, target, build_config.mode), false)); } else if !all_targets { bail!("no library targets found") From a312c55dcbf5bd814ee2002c92270aaa3dc25c45 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 5 May 2018 18:07:18 +0300 Subject: [PATCH 2/2] Expand dependency info of cargo --metadata 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 | 113 +++++++++---- tests/testsuite/metadata.rs | 225 ++++++++++++++++++++++++- 2 files changed, 304 insertions(+), 34 deletions(-) diff --git a/src/cargo/ops/cargo_output_metadata.rs b/src/cargo/ops/cargo_output_metadata.rs index 5cba2d482de..c7d3c3ce851 100644 --- a/src/cargo/ops/cargo_output_metadata.rs +++ b/src/cargo/ops/cargo_output_metadata.rs @@ -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>>()?; - 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, + /// Packages which are direct members of the current project. workspace_members: Vec, + /// A graph of the dependencies between packages. resolve: Option, + /// 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, + /// Deprecated, use `ExportInfo::workspace_members`. root: Option, } -fn serialize_resolve(resolve: &Resolve, s: S) -> Result -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, + /// Dependencies of this package. + deps: Vec, + /// Features, enabled for this package. + features: Vec, +} - resolve - .iter() - .map(|id| Node { - id, - dependencies: resolve.deps(id).map(|p| p.0).collect(), - features: resolve.features_sorted(id), - }) - .collect::>() - .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, + ) -> 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::>().into_iter() + }) + .collect(), + features: resolve + .features_sorted(pkg) + .into_iter() + .map(|s| s.to_string()) + .collect(), + } + }) + .collect(); + MetadataResolve { nodes, root } + } } diff --git a/tests/testsuite/metadata.rs b/tests/testsuite/metadata.rs index f0b5e104be0..de0c0afb41d 100644 --- a/tests/testsuite/metadata.rs +++ b/tests/testsuite/metadata.rs @@ -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)" }