Skip to content

Commit

Permalink
Add --filter-platform to cargo metadata.
Browse files Browse the repository at this point in the history
  • Loading branch information
ehuss committed Oct 28, 2019
1 parent a2f4906 commit 5b521f6
Show file tree
Hide file tree
Showing 3 changed files with 637 additions and 52 deletions.
9 changes: 9 additions & 0 deletions src/bin/cargo/commands/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ pub fn cli() -> App {
)
.arg(opt("quiet", "No output printed to stdout").short("q"))
.arg_features()
.arg(
opt(
"filter-platform",
"Only include resolve dependencies matching the given target-triple \
(\"host\" for current host)",
)
.value_name("TRIPLE"),
)
.arg(opt(
"no-deps",
"Output information only about the root package \
Expand Down Expand Up @@ -44,6 +52,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
all_features: args.is_present("all-features"),
no_default_features: args.is_present("no-default-features"),
no_deps: args.is_present("no-deps"),
filter_platform: args.value_of("filter-platform").map(|s| s.to_string()),
version,
};

Expand Down
147 changes: 96 additions & 51 deletions src/cargo/ops/cargo_output_metadata.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use std::collections::HashMap;
use std::path::PathBuf;

use serde::ser;
use serde::Serialize;

use crate::core::compiler::{CompileKind, CompileTarget, TargetInfo};
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::util::CargoResult;
use cargo_platform::Cfg;
use serde::ser;
use serde::Serialize;
use std::collections::HashMap;
use std::path::PathBuf;

const VERSION: u32 = 1;

Expand All @@ -17,6 +17,7 @@ pub struct OutputMetadataOptions {
pub all_features: bool,
pub no_deps: bool,
pub version: u32,
pub filter_platform: Option<String>,
}

/// Loads the manifest, resolves the dependencies of the package to the concrete
Expand All @@ -30,48 +31,63 @@ pub fn output_metadata(ws: &Workspace<'_>, opt: &OutputMetadataOptions) -> Cargo
VERSION
);
}
if opt.no_deps {
metadata_no_deps(ws, opt)
let (packages, resolve) = if opt.no_deps {
let packages = ws.members().cloned().collect();
(packages, None)
} else {
metadata_full(ws, opt)
}
}

fn metadata_no_deps(ws: &Workspace<'_>, _opt: &OutputMetadataOptions) -> CargoResult<ExportInfo> {
Ok(ExportInfo {
packages: ws.members().cloned().collect(),
workspace_members: ws.members().map(|pkg| pkg.package_id()).collect(),
resolve: None,
target_directory: ws.target_dir().into_path_unlocked(),
version: VERSION,
workspace_root: ws.root().to_path_buf(),
})
}

fn metadata_full(ws: &Workspace<'_>, opt: &OutputMetadataOptions) -> CargoResult<ExportInfo> {
let specs = Packages::All.to_package_id_specs(ws)?;
let opts = ResolveOpts::new(
/*dev_deps*/ true,
&opt.features,
opt.all_features,
!opt.no_default_features,
);
let ws_resolve = ops::resolve_ws_with_opts(ws, opts, &specs)?;
let mut packages = HashMap::new();
for pkg in ws_resolve
.pkg_set
.get_many(ws_resolve.pkg_set.package_ids())?
{
packages.insert(pkg.package_id(), pkg.clone());
}
let specs = Packages::All.to_package_id_specs(ws)?;
let opts = ResolveOpts::new(
/*dev_deps*/ true,
&opt.features,
opt.all_features,
!opt.no_default_features,
);
let ws_resolve = ops::resolve_ws_with_opts(ws, opts, &specs)?;
let mut package_map = HashMap::new();
for pkg in ws_resolve
.pkg_set
.get_many(ws_resolve.pkg_set.package_ids())?
{
package_map.insert(pkg.package_id(), pkg.clone());
}
let packages = package_map.values().map(|p| (*p).clone()).collect();
let rustc = ws.config().load_global_rustc(Some(ws))?;
let (target, cfg) = match &opt.filter_platform {
Some(platform) => {
if platform == "host" {
let ti =
TargetInfo::new(ws.config(), CompileKind::Host, &rustc, CompileKind::Host)?;
(
Some(rustc.host.as_str().to_string()),
Some(ti.cfg().iter().cloned().collect()),
)
} else {
let kind = CompileKind::Target(CompileTarget::new(platform)?);
let ti = TargetInfo::new(ws.config(), kind, &rustc, kind)?;
(
Some(platform.clone()),
Some(ti.cfg().iter().cloned().collect()),
)
}
}
None => (None, None),
};
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()),
});
(packages, resolve)
};

Ok(ExportInfo {
packages: packages.values().map(|p| (*p).clone()).collect(),
packages,
workspace_members: ws.members().map(|pkg| pkg.package_id()).collect(),
resolve: Some(MetadataResolve {
resolve: (packages, ws_resolve.targeted_resolve),
root: ws.current_opt().map(|pkg| pkg.package_id()),
}),
resolve,
target_directory: ws.target_dir().into_path_unlocked(),
version: VERSION,
workspace_root: ws.root().to_path_buf(),
Expand All @@ -94,17 +110,28 @@ pub struct ExportInfo {
#[derive(Serialize)]
struct MetadataResolve {
#[serde(rename = "nodes", serialize_with = "serialize_resolve")]
resolve: (HashMap<PackageId, Package>, Resolve),
helper: ResolveHelper,
root: Option<PackageId>,
}

fn serialize_resolve<S>(
(packages, resolve): &(HashMap<PackageId, Package>, Resolve),
s: S,
) -> Result<S::Ok, S::Error>
struct ResolveHelper {
packages: HashMap<PackageId, Package>,
resolve: Resolve,
target: Option<String>,
cfg: Option<Vec<Cfg>>,
}

fn serialize_resolve<S>(helper: &ResolveHelper, s: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
let ResolveHelper {
packages,
resolve,
target,
cfg,
} = helper;

#[derive(Serialize)]
struct Dep {
name: String,
Expand All @@ -119,12 +146,30 @@ where
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| {
Node {
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(id)
.filter(dep_filter)
.filter_map(|(pkg, _deps)| {
packages
.get(&pkg)
Expand Down
Loading

0 comments on commit 5b521f6

Please sign in to comment.