From fe7291ad7b6003ab0d6669eeb55a603eb163d636 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 21 Dec 2015 18:05:26 +0300 Subject: [PATCH] metadata: serialize Resolve --- src/cargo/ops/cargo_output_metadata.rs | 47 +++------ tests/test_cargo_metadata.rs | 126 +++++++++++++++++++++++-- 2 files changed, 132 insertions(+), 41 deletions(-) diff --git a/src/cargo/ops/cargo_output_metadata.rs b/src/cargo/ops/cargo_output_metadata.rs index 38f084af2..6d4415e51 100644 --- a/src/cargo/ops/cargo_output_metadata.rs +++ b/src/cargo/ops/cargo_output_metadata.rs @@ -1,5 +1,4 @@ use std::ascii::AsciiExt; -use std::collections::HashMap; use std::path::{Path, PathBuf}; use core::resolver::Resolve; @@ -53,40 +52,13 @@ pub fn output_metadata(opt: OutputMetadataOptions, config: &Config) -> CargoResu config, opt.features, opt.no_default_features)); - let (resolved_deps, packages) = deps; + let (packages, resolve) = deps; - #[derive(RustcEncodable)] - struct RootPackageInfo<'a> { - name: &'a str, - version: String, - features: Option<&'a HashMap>>, - } - - #[derive(RustcEncodable)] - struct ExportInfo<'a> { - root: RootPackageInfo<'a>, - packages: Vec<&'a Package>, - } - - let mut output = ExportInfo { - root: RootPackageInfo { - name: resolved_deps.root().name(), - version: format!("{}", resolved_deps.root().version()), - features: None, - }, - packages: Vec::new(), + let output = ExportInfo { + packages: &packages, + resolve: &resolve, }; - for package in packages.iter() { - output.packages.push(&package); - if package.package_id() == resolved_deps.root() { - let features = package.manifest().summary().features(); - if !features.is_empty() { - output.root.features = Some(features); - } - } - } - let serialized_str = match &opt.output_format.to_ascii_uppercase()[..] { "TOML" => toml::encode_str(&output), "JSON" => try!(json::encode(&output)), @@ -102,13 +74,20 @@ pub fn output_metadata(opt: OutputMetadataOptions, config: &Config) -> CargoResu Ok(()) } +#[derive(RustcEncodable)] +struct ExportInfo<'a> { + packages: &'a [Package], + resolve: &'a Resolve, +} + + /// Loads the manifest and resolves the dependencies of the project to the /// concrete used versions. Afterwards available overrides of dependencies are applied. fn resolve_dependencies(manifest: &Path, config: &Config, features: Vec, no_default_features: bool) - -> CargoResult<(Resolve, Vec)> { + -> CargoResult<(Vec, Resolve)> { let mut source = try!(PathSource::for_path(manifest.parent().unwrap(), config)); try!(source.update()); @@ -122,5 +101,5 @@ fn resolve_dependencies(manifest: &Path, let (packages, resolve_with_overrides, _) = deps; - Ok((resolve_with_overrides, packages)) + Ok((packages, resolve_with_overrides)) } diff --git a/tests/test_cargo_metadata.rs b/tests/test_cargo_metadata.rs index f491737f8..7ae3df669 100644 --- a/tests/test_cargo_metadata.rs +++ b/tests/test_cargo_metadata.rs @@ -2,6 +2,8 @@ use std::fs::File; use std::io::prelude::*; use hamcrest::{assert_that, existing_file, is, equal_to}; +use rustc_serialize::json::Json; +use support::registry::Package; use support::{project, execs, basic_bin_manifest}; @@ -27,7 +29,11 @@ kind = ["bin"] name = "foo" src_path = "src[..]foo.rs" -[root] +[resolve] +package = [] + +[resolve.root] +dependencies = [] name = "foo" version = "0.5.0" @@ -41,11 +47,6 @@ test!(cargo_metadata_simple_json { assert_that(p.cargo_process("metadata").arg("-f").arg("json"), execs().with_stdout(r#" { - "root": { - "name": "foo", - "version": "0.5.0", - "features": null - }, "packages": [ { "name": "foo", @@ -65,10 +66,121 @@ test!(cargo_metadata_simple_json { "features": {}, "manifest_path": "[..]Cargo.toml" } - ] + ], + "resolve": { + "package": [], + "root": { + "name": "foo", + "version": "0.5.0", + "source": null, + "dependencies" : [] + }, + "metadata": null + } }"#.split_whitespace().collect::())); }); + +test!(cargo_metadata_with_deps { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.5.0" + authors = [] + license = "MIT" + description = "foo" + + [[bin]] + name = "foo" + + [dependencies] + bar = "*" + "#); + Package::new("baz", "0.0.1").publish(); + Package::new("bar", "0.0.1").dep("baz", "0.0.1").publish(); + + assert_that(p.cargo_process("metadata") + .arg("--output-path").arg("metadata.json") + .arg("-f").arg("json"), + + execs().with_status(0)); + + let outputfile = p.root().join("metadata.json"); + assert_that(&outputfile, existing_file()); + + let mut output = String::new(); + File::open(&outputfile).unwrap().read_to_string(&mut output).unwrap(); + let result = Json::from_str(&output).unwrap(); + println!("{}", result.pretty()); + + let packages = result.find("packages") + .and_then(|o| o.as_array()) + .expect("no packages"); + + assert_that(packages.len(), is(equal_to(3))); + + let root = result.find_path(&["resolve", "root"]) + .and_then(|o| o.as_object()) + .expect("no root"); + + // source is null because foo is root + let foo_id_start = format!("{} {}", root["name"].as_string().unwrap(), + root["version"].as_string().unwrap()); + let foo_name = packages.iter().find(|o| { + o.find("id").and_then(|i| i.as_string()).unwrap() + .starts_with(&foo_id_start) + }).and_then(|p| p.find("name")) + .and_then(|n| n.as_string()) + .expect("no root package"); + assert_that(foo_name, is(equal_to("foo"))); + + let foo_deps = root["dependencies"].as_array().expect("no root deps"); + assert_that(foo_deps.len(), is(equal_to(1))); + + let bar = &foo_deps[0].as_string().expect("bad root dep"); + + let check_name_for_id = |id: &str, expected_name: &str| { + let name = packages.iter().find(|o| { + id == o.find("id").and_then(|i| i.as_string()).unwrap() + }).and_then(|p| p.find("name")) + .and_then(|n| n.as_string()) + .expect(&format!("no {} in packages", expected_name)); + + assert_that(name, is(equal_to(expected_name))); + }; + + let find_deps = |id: &str| -> Vec<_> { + result.find_path(&["resolve", "package"]) + .and_then(|o| o.as_array()).expect("resolve.package is not an array") + .iter() + .find(|o| { + let o = o.as_object().expect("package is not an object"); + let o_id = format!("{} {} ({})", + o["name"].as_string().unwrap(), + o["version"].as_string().unwrap(), + o["source"].as_string().unwrap()); + id == o_id + }) + .and_then(|o| o.find("dependencies")) + .and_then(|o| o.as_array()) + .and_then(|a| a.iter() + .map(|o| o.as_string()) + .collect()) + .expect(&format!("no deps for {}", id)) + }; + + + check_name_for_id(bar, "bar"); + let bar_deps = find_deps(&bar); + assert_that(bar_deps.len(), is(equal_to(1))); + + let baz = &bar_deps[0]; + check_name_for_id(baz, "baz"); + let baz_deps = find_deps(&baz); + assert_that(baz_deps.len(), is(equal_to(0))); +}); + test!(cargo_metadata_with_invalid_manifest { let p = project("foo") .file("Cargo.toml", ""); -- 2.39.5