Add --filter-platform to cargo metadata.

This commit is contained in:
Eric Huss 2019-09-17 17:17:29 -07:00
parent a2f49062d8
commit 5b521f6530
3 changed files with 639 additions and 54 deletions

View File

@ -12,6 +12,14 @@ pub fn cli() -> App {
) )
.arg(opt("quiet", "No output printed to stdout").short("q")) .arg(opt("quiet", "No output printed to stdout").short("q"))
.arg_features() .arg_features()
.arg(
opt(
"filter-platform",
"Only include resolve dependencies matching the given target-triple \
(\"host\" for current host)",
)
.value_name("TRIPLE"),
)
.arg(opt( .arg(opt(
"no-deps", "no-deps",
"Output information only about the root package \ "Output information only about the root package \
@ -44,6 +52,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
all_features: args.is_present("all-features"), all_features: args.is_present("all-features"),
no_default_features: args.is_present("no-default-features"), no_default_features: args.is_present("no-default-features"),
no_deps: args.is_present("no-deps"), no_deps: args.is_present("no-deps"),
filter_platform: args.value_of("filter-platform").map(|s| s.to_string()),
version, version,
}; };

View File

@ -1,13 +1,13 @@
use std::collections::HashMap; use crate::core::compiler::{CompileKind, CompileTarget, TargetInfo};
use std::path::PathBuf;
use serde::ser;
use serde::Serialize;
use crate::core::resolver::{Resolve, ResolveOpts}; use crate::core::resolver::{Resolve, ResolveOpts};
use crate::core::{Package, PackageId, Workspace}; use crate::core::{Dependency, Package, PackageId, Workspace};
use crate::ops::{self, Packages}; use crate::ops::{self, Packages};
use crate::util::CargoResult; use crate::util::CargoResult;
use cargo_platform::Cfg;
use serde::ser;
use serde::Serialize;
use std::collections::HashMap;
use std::path::PathBuf;
const VERSION: u32 = 1; const VERSION: u32 = 1;
@ -17,6 +17,7 @@ pub struct OutputMetadataOptions {
pub all_features: bool, pub all_features: bool,
pub no_deps: bool, pub no_deps: bool,
pub version: u32, pub version: u32,
pub filter_platform: Option<String>,
} }
/// Loads the manifest, resolves the dependencies of the package to the concrete /// Loads the manifest, resolves the dependencies of the package to the concrete
@ -30,48 +31,63 @@ pub fn output_metadata(ws: &Workspace<'_>, opt: &OutputMetadataOptions) -> Cargo
VERSION VERSION
); );
} }
if opt.no_deps { let (packages, resolve) = if opt.no_deps {
metadata_no_deps(ws, opt) let packages = ws.members().cloned().collect();
(packages, None)
} else { } else {
metadata_full(ws, opt) let specs = Packages::All.to_package_id_specs(ws)?;
} let opts = ResolveOpts::new(
} /*dev_deps*/ true,
&opt.features,
fn metadata_no_deps(ws: &Workspace<'_>, _opt: &OutputMetadataOptions) -> CargoResult<ExportInfo> { opt.all_features,
Ok(ExportInfo { !opt.no_default_features,
packages: ws.members().cloned().collect(), );
workspace_members: ws.members().map(|pkg| pkg.package_id()).collect(), let ws_resolve = ops::resolve_ws_with_opts(ws, opts, &specs)?;
resolve: None, let mut package_map = HashMap::new();
target_directory: ws.target_dir().into_path_unlocked(), for pkg in ws_resolve
version: VERSION, .pkg_set
workspace_root: ws.root().to_path_buf(), .get_many(ws_resolve.pkg_set.package_ids())?
}) {
} package_map.insert(pkg.package_id(), pkg.clone());
}
fn metadata_full(ws: &Workspace<'_>, opt: &OutputMetadataOptions) -> CargoResult<ExportInfo> { let packages = package_map.values().map(|p| (*p).clone()).collect();
let specs = Packages::All.to_package_id_specs(ws)?; let rustc = ws.config().load_global_rustc(Some(ws))?;
let opts = ResolveOpts::new( let (target, cfg) = match &opt.filter_platform {
/*dev_deps*/ true, Some(platform) => {
&opt.features, if platform == "host" {
opt.all_features, let ti =
!opt.no_default_features, TargetInfo::new(ws.config(), CompileKind::Host, &rustc, CompileKind::Host)?;
); (
let ws_resolve = ops::resolve_ws_with_opts(ws, opts, &specs)?; Some(rustc.host.as_str().to_string()),
let mut packages = HashMap::new(); Some(ti.cfg().iter().cloned().collect()),
for pkg in ws_resolve )
.pkg_set } else {
.get_many(ws_resolve.pkg_set.package_ids())? let kind = CompileKind::Target(CompileTarget::new(platform)?);
{ let ti = TargetInfo::new(ws.config(), kind, &rustc, kind)?;
packages.insert(pkg.package_id(), pkg.clone()); (
} Some(platform.clone()),
Some(ti.cfg().iter().cloned().collect()),
Ok(ExportInfo { )
packages: packages.values().map(|p| (*p).clone()).collect(), }
workspace_members: ws.members().map(|pkg| pkg.package_id()).collect(), }
resolve: Some(MetadataResolve { None => (None, None),
resolve: (packages, ws_resolve.targeted_resolve), };
let resolve = Some(MetadataResolve {
helper: ResolveHelper {
packages: package_map,
resolve: ws_resolve.targeted_resolve,
target,
cfg,
},
root: ws.current_opt().map(|pkg| pkg.package_id()), root: ws.current_opt().map(|pkg| pkg.package_id()),
}), });
(packages, resolve)
};
Ok(ExportInfo {
packages,
workspace_members: ws.members().map(|pkg| pkg.package_id()).collect(),
resolve,
target_directory: ws.target_dir().into_path_unlocked(), target_directory: ws.target_dir().into_path_unlocked(),
version: VERSION, version: VERSION,
workspace_root: ws.root().to_path_buf(), workspace_root: ws.root().to_path_buf(),
@ -94,17 +110,28 @@ pub struct ExportInfo {
#[derive(Serialize)] #[derive(Serialize)]
struct MetadataResolve { struct MetadataResolve {
#[serde(rename = "nodes", serialize_with = "serialize_resolve")] #[serde(rename = "nodes", serialize_with = "serialize_resolve")]
resolve: (HashMap<PackageId, Package>, Resolve), helper: ResolveHelper,
root: Option<PackageId>, root: Option<PackageId>,
} }
fn serialize_resolve<S>( struct ResolveHelper {
(packages, resolve): &(HashMap<PackageId, Package>, Resolve), packages: HashMap<PackageId, Package>,
s: S, resolve: Resolve,
) -> Result<S::Ok, S::Error> target: Option<String>,
cfg: Option<Vec<Cfg>>,
}
fn serialize_resolve<S>(helper: &ResolveHelper, s: S) -> Result<S::Ok, S::Error>
where where
S: ser::Serializer, S: ser::Serializer,
{ {
let ResolveHelper {
packages,
resolve,
target,
cfg,
} = helper;
#[derive(Serialize)] #[derive(Serialize)]
struct Dep { struct Dep {
name: String, name: String,
@ -119,12 +146,30 @@ where
features: Vec<&'a str>, features: Vec<&'a str>,
} }
// A filter for removing platform dependencies.
let dep_filter = |(_pkg, deps): &(PackageId, &[Dependency])| match (target, cfg) {
(Some(target), Some(cfg)) => deps.iter().any(|dep| {
let platform = match dep.platform() {
Some(p) => p,
None => return true,
};
platform.matches(target, cfg)
}),
(None, None) => true,
_ => unreachable!(),
};
s.collect_seq(resolve.iter().map(|id| { s.collect_seq(resolve.iter().map(|id| {
Node { Node {
id, id,
dependencies: resolve.deps(id).map(|(pkg, _deps)| pkg).collect(), dependencies: resolve
.deps(id)
.filter(dep_filter)
.map(|(pkg, _deps)| pkg)
.collect(),
deps: resolve deps: resolve
.deps(id) .deps(id)
.filter(dep_filter)
.filter_map(|(pkg, _deps)| { .filter_map(|(pkg, _deps)| {
packages packages
.get(&pkg) .get(&pkg)

View File

@ -1,5 +1,6 @@
use cargo_test_support::cross_compile::alternate;
use cargo_test_support::registry::Package; use cargo_test_support::registry::Package;
use cargo_test_support::{basic_bin_manifest, basic_lib_manifest, main_file, project}; use cargo_test_support::{basic_bin_manifest, basic_lib_manifest, main_file, project, rustc_host};
#[cargo_test] #[cargo_test]
fn cargo_metadata_simple() { fn cargo_metadata_simple() {
@ -1804,3 +1805,533 @@ fn deps_with_bin_only() {
assert!(nodes[0]["deps"].as_array().unwrap().is_empty()); assert!(nodes[0]["deps"].as_array().unwrap().is_empty());
assert!(nodes[1]["deps"].as_array().unwrap().is_empty()); assert!(nodes[1]["deps"].as_array().unwrap().is_empty());
} }
#[cargo_test]
fn filter_platform() {
// Testing the --filter-platform flag.
Package::new("normal-dep", "0.0.1").publish();
Package::new("host-dep", "0.0.1").publish();
Package::new("alt-dep", "0.0.1").publish();
Package::new("cfg-dep", "0.0.1").publish();
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "foo"
version = "0.1.0"
[dependencies]
normal-dep = "0.0.1"
[target.{}.dependencies]
host-dep = "0.0.1"
[target.{}.dependencies]
alt-dep = "0.0.1"
[target.'cfg(foobar)'.dependencies]
cfg-dep = "0.0.1"
"#,
rustc_host(),
alternate()
),
)
.file("src/lib.rs", "")
.build();
p.cargo("metadata")
.with_json(
&r#"
{
"packages": [
{
"name": "alt-dep",
"version": "0.0.1",
"id": "alt-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"license": null,
"license_file": null,
"description": null,
"source": "registry+https://github.com/rust-lang/crates.io-index",
"dependencies": [],
"targets": [
{
"kind": [
"lib"
],
"crate_types": [
"lib"
],
"name": "alt-dep",
"src_path": "[..]/alt-dep-0.0.1/src/lib.rs",
"edition": "2015",
"doctest": true
}
],
"features": {},
"manifest_path": "[..]/alt-dep-0.0.1/Cargo.toml",
"metadata": null,
"publish": null,
"authors": [],
"categories": [],
"keywords": [],
"readme": null,
"repository": null,
"edition": "2015",
"links": null
},
{
"name": "cfg-dep",
"version": "0.0.1",
"id": "cfg-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"license": null,
"license_file": null,
"description": null,
"source": "registry+https://github.com/rust-lang/crates.io-index",
"dependencies": [],
"targets": [
{
"kind": [
"lib"
],
"crate_types": [
"lib"
],
"name": "cfg-dep",
"src_path": "[..]/cfg-dep-0.0.1/src/lib.rs",
"edition": "2015",
"doctest": true
}
],
"features": {},
"manifest_path": "[..]/cfg-dep-0.0.1/Cargo.toml",
"metadata": null,
"publish": null,
"authors": [],
"categories": [],
"keywords": [],
"readme": null,
"repository": null,
"edition": "2015",
"links": null
},
{
"name": "host-dep",
"version": "0.0.1",
"id": "host-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"license": null,
"license_file": null,
"description": null,
"source": "registry+https://github.com/rust-lang/crates.io-index",
"dependencies": [],
"targets": [
{
"kind": [
"lib"
],
"crate_types": [
"lib"
],
"name": "host-dep",
"src_path": "[..]/host-dep-0.0.1/src/lib.rs",
"edition": "2015",
"doctest": true
}
],
"features": {},
"manifest_path": "[..]/host-dep-0.0.1/Cargo.toml",
"metadata": null,
"publish": null,
"authors": [],
"categories": [],
"keywords": [],
"readme": null,
"repository": null,
"edition": "2015",
"links": null
},
{
"name": "normal-dep",
"version": "0.0.1",
"id": "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"license": null,
"license_file": null,
"description": null,
"source": "registry+https://github.com/rust-lang/crates.io-index",
"dependencies": [],
"targets": [
{
"kind": [
"lib"
],
"crate_types": [
"lib"
],
"name": "normal-dep",
"src_path": "[..]/normal-dep-0.0.1/src/lib.rs",
"edition": "2015",
"doctest": true
}
],
"features": {},
"manifest_path": "[..]/normal-dep-0.0.1/Cargo.toml",
"metadata": null,
"publish": null,
"authors": [],
"categories": [],
"keywords": [],
"readme": null,
"repository": null,
"edition": "2015",
"links": null
},
{
"name": "foo",
"version": "0.1.0",
"id": "foo 0.1.0 (path+file:[..]foo)",
"license": null,
"license_file": null,
"description": null,
"source": null,
"dependencies": [
{
"name": "normal-dep",
"source": "registry+https://github.com/rust-lang/crates.io-index",
"req": "^0.0.1",
"kind": null,
"rename": null,
"optional": false,
"uses_default_features": true,
"features": [],
"target": null,
"registry": null
},
{
"name": "cfg-dep",
"source": "registry+https://github.com/rust-lang/crates.io-index",
"req": "^0.0.1",
"kind": null,
"rename": null,
"optional": false,
"uses_default_features": true,
"features": [],
"target": "cfg(foobar)",
"registry": null
},
{
"name": "alt-dep",
"source": "registry+https://github.com/rust-lang/crates.io-index",
"req": "^0.0.1",
"kind": null,
"rename": null,
"optional": false,
"uses_default_features": true,
"features": [],
"target": "$ALT",
"registry": null
},
{
"name": "host-dep",
"source": "registry+https://github.com/rust-lang/crates.io-index",
"req": "^0.0.1",
"kind": null,
"rename": null,
"optional": false,
"uses_default_features": true,
"features": [],
"target": "$HOST",
"registry": null
}
],
"targets": [
{
"kind": [
"lib"
],
"crate_types": [
"lib"
],
"name": "foo",
"src_path": "[..]/foo/src/lib.rs",
"edition": "2015",
"doctest": true
}
],
"features": {},
"manifest_path": "[..]/foo/Cargo.toml",
"metadata": null,
"publish": null,
"authors": [],
"categories": [],
"keywords": [],
"readme": null,
"repository": null,
"edition": "2015",
"links": null
}
],
"workspace_members": [
"foo 0.1.0 (path+file:[..]foo)"
],
"resolve": {
"nodes": [
{
"id": "alt-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dependencies": [],
"deps": [],
"features": []
},
{
"id": "cfg-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dependencies": [],
"deps": [],
"features": []
},
{
"id": "foo 0.1.0 (path+file:[..]foo)",
"dependencies": [
"alt-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"host-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)"
],
"deps": [
{
"name": "alt_dep",
"pkg": "alt-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)"
},
{
"name": "cfg_dep",
"pkg": "cfg-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)"
},
{
"name": "host_dep",
"pkg": "host-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)"
},
{
"name": "normal_dep",
"pkg": "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)"
}
],
"features": []
},
{
"id": "host-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dependencies": [],
"deps": [],
"features": []
},
{
"id": "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dependencies": [],
"deps": [],
"features": []
}
],
"root": "foo 0.1.0 (path+file:[..]foo)"
},
"target_directory": "[..]/foo/target",
"version": 1,
"workspace_root": "[..]/foo"
}
"#
.replace("$ALT", &alternate())
.replace("$HOST", &rustc_host()),
)
.run();
p.cargo("metadata --filter-platform")
.arg(alternate())
.with_json(
r#"
{
"packages": "{...}",
"workspace_members": "{...}",
"resolve": {
"nodes": [
{
"id": "alt-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dependencies": [],
"deps": [],
"features": []
},
{
"id": "cfg-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dependencies": [],
"deps": [],
"features": []
},
{
"id": "foo 0.1.0 (path+file:[..]foo)",
"dependencies": [
"alt-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)"
],
"deps": [
{
"name": "alt_dep",
"pkg": "alt-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)"
},
{
"name": "normal_dep",
"pkg": "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)"
}
],
"features": []
},
{
"id": "host-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dependencies": [],
"deps": [],
"features": []
},
{
"id": "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dependencies": [],
"deps": [],
"features": []
}
],
"root": "foo 0.1.0 (path+file:[..]foo)"
},
"target_directory": "[..]foo/target",
"version": 1,
"workspace_root": "[..]foo"
}
"#,
)
.run();
let host_json = r#"
{
"packages": "{...}",
"workspace_members": "{...}",
"resolve": {
"nodes": [
{
"id": "alt-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dependencies": [],
"deps": [],
"features": []
},
{
"id": "cfg-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dependencies": [],
"deps": [],
"features": []
},
{
"id": "foo 0.1.0 (path+file:[..]foo)",
"dependencies": [
"host-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)"
],
"deps": [
{
"name": "host_dep",
"pkg": "host-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)"
},
{
"name": "normal_dep",
"pkg": "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)"
}
],
"features": []
},
{
"id": "host-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dependencies": [],
"deps": [],
"features": []
},
{
"id": "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dependencies": [],
"deps": [],
"features": []
}
],
"root": "foo 0.1.0 (path+file:[..]foo)"
},
"target_directory": "[..]foo/target",
"version": 1,
"workspace_root": "[..]foo"
}
"#;
p.cargo("metadata --filter-platform=host")
.with_json(host_json)
.run();
p.cargo("metadata --filter-platform")
.arg(rustc_host())
.with_json(host_json)
.run();
p.cargo("metadata --filter-platform=host")
.env("RUSTFLAGS", "--cfg=foobar")
.with_json(
r#"
{
"packages": "{...}",
"workspace_members": "{...}",
"resolve": {
"nodes": [
{
"id": "alt-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dependencies": [],
"deps": [],
"features": []
},
{
"id": "cfg-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dependencies": [],
"deps": [],
"features": []
},
{
"id": "foo 0.1.0 (path+file:[..]/foo)",
"dependencies": [
"cfg-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"host-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)"
],
"deps": [
{
"name": "cfg_dep",
"pkg": "cfg-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)"
},
{
"name": "host_dep",
"pkg": "host-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)"
},
{
"name": "normal_dep",
"pkg": "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)"
}
],
"features": []
},
{
"id": "host-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dependencies": [],
"deps": [],
"features": []
},
{
"id": "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dependencies": [],
"deps": [],
"features": []
}
],
"root": "foo 0.1.0 (path+file:[..]/foo)"
},
"target_directory": "[..]/foo/target",
"version": 1,
"workspace_root": "[..]/foo"
}
"#,
)
.run();
}