Skip to content

Commit d6b5a6b

Browse files
committed
Rewrite target filtering to filter the entire graph.
This also removes the special `host` target.
1 parent 5b521f6 commit d6b5a6b

File tree

2 files changed

+272
-184
lines changed

2 files changed

+272
-184
lines changed
Lines changed: 112 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
use crate::core::compiler::{CompileKind, CompileTarget, TargetInfo};
22
use crate::core::resolver::{Resolve, ResolveOpts};
3-
use crate::core::{Dependency, Package, PackageId, Workspace};
3+
use crate::core::{Package, PackageId, Workspace};
44
use crate::ops::{self, Packages};
55
use crate::util::CargoResult;
6-
use cargo_platform::Cfg;
7-
use serde::ser;
6+
87
use serde::Serialize;
98
use std::collections::HashMap;
109
use std::path::PathBuf;
@@ -35,53 +34,14 @@ pub fn output_metadata(ws: &Workspace<'_>, opt: &OutputMetadataOptions) -> Cargo
3534
let packages = ws.members().cloned().collect();
3635
(packages, None)
3736
} else {
38-
let specs = Packages::All.to_package_id_specs(ws)?;
39-
let opts = ResolveOpts::new(
37+
let resolve_opts = ResolveOpts::new(
4038
/*dev_deps*/ true,
4139
&opt.features,
4240
opt.all_features,
4341
!opt.no_default_features,
4442
);
45-
let ws_resolve = ops::resolve_ws_with_opts(ws, opts, &specs)?;
46-
let mut package_map = HashMap::new();
47-
for pkg in ws_resolve
48-
.pkg_set
49-
.get_many(ws_resolve.pkg_set.package_ids())?
50-
{
51-
package_map.insert(pkg.package_id(), pkg.clone());
52-
}
53-
let packages = package_map.values().map(|p| (*p).clone()).collect();
54-
let rustc = ws.config().load_global_rustc(Some(ws))?;
55-
let (target, cfg) = match &opt.filter_platform {
56-
Some(platform) => {
57-
if platform == "host" {
58-
let ti =
59-
TargetInfo::new(ws.config(), CompileKind::Host, &rustc, CompileKind::Host)?;
60-
(
61-
Some(rustc.host.as_str().to_string()),
62-
Some(ti.cfg().iter().cloned().collect()),
63-
)
64-
} else {
65-
let kind = CompileKind::Target(CompileTarget::new(platform)?);
66-
let ti = TargetInfo::new(ws.config(), kind, &rustc, kind)?;
67-
(
68-
Some(platform.clone()),
69-
Some(ti.cfg().iter().cloned().collect()),
70-
)
71-
}
72-
}
73-
None => (None, None),
74-
};
75-
let resolve = Some(MetadataResolve {
76-
helper: ResolveHelper {
77-
packages: package_map,
78-
resolve: ws_resolve.targeted_resolve,
79-
target,
80-
cfg,
81-
},
82-
root: ws.current_opt().map(|pkg| pkg.package_id()),
83-
});
84-
(packages, resolve)
43+
let (packages, resolve) = build_resolve_graph(ws, resolve_opts, &opt.filter_platform)?;
44+
(packages, Some(resolve))
8545
};
8646

8747
Ok(ExportInfo {
@@ -104,81 +64,123 @@ pub struct ExportInfo {
10464
workspace_root: PathBuf,
10565
}
10666

107-
/// Newtype wrapper to provide a custom `Serialize` implementation.
108-
/// The one from lock file does not fit because it uses a non-standard
109-
/// format for `PackageId`s
11067
#[derive(Serialize)]
11168
struct MetadataResolve {
112-
#[serde(rename = "nodes", serialize_with = "serialize_resolve")]
113-
helper: ResolveHelper,
69+
nodes: Vec<MetadataResolveNode>,
11470
root: Option<PackageId>,
11571
}
11672

117-
struct ResolveHelper {
118-
packages: HashMap<PackageId, Package>,
119-
resolve: Resolve,
120-
target: Option<String>,
121-
cfg: Option<Vec<Cfg>>,
73+
#[derive(Serialize)]
74+
struct MetadataResolveNode {
75+
id: PackageId,
76+
dependencies: Vec<PackageId>,
77+
deps: Vec<Dep>,
78+
features: Vec<String>,
12279
}
12380

124-
fn serialize_resolve<S>(helper: &ResolveHelper, s: S) -> Result<S::Ok, S::Error>
125-
where
126-
S: ser::Serializer,
127-
{
128-
let ResolveHelper {
129-
packages,
130-
resolve,
131-
target,
132-
cfg,
133-
} = helper;
81+
#[derive(Serialize)]
82+
struct Dep {
83+
name: String,
84+
pkg: PackageId,
85+
}
13486

135-
#[derive(Serialize)]
136-
struct Dep {
137-
name: String,
138-
pkg: PackageId,
87+
fn build_resolve_graph(
88+
ws: &Workspace<'_>,
89+
resolve_opts: ResolveOpts,
90+
target: &Option<String>,
91+
) -> CargoResult<(Vec<Package>, MetadataResolve)> {
92+
let target_info = match target {
93+
Some(target) => {
94+
let config = ws.config();
95+
let ct = CompileTarget::new(target)?;
96+
let short_name = ct.short_name().to_string();
97+
let kind = CompileKind::Target(ct);
98+
let rustc = config.load_global_rustc(Some(ws))?;
99+
Some((short_name, TargetInfo::new(config, kind, &rustc, kind)?))
100+
}
101+
None => None,
102+
};
103+
// Resolve entire workspace.
104+
let specs = Packages::All.to_package_id_specs(ws)?;
105+
let ws_resolve = ops::resolve_ws_with_opts(ws, resolve_opts, &specs)?;
106+
// Download all Packages. This is needed to serialize the information
107+
// for every package. In theory this could honor target filtering,
108+
// but that would be somewhat complex.
109+
let mut package_map: HashMap<PackageId, Package> = ws_resolve
110+
.pkg_set
111+
.get_many(ws_resolve.pkg_set.package_ids())?
112+
.into_iter()
113+
.map(|pkg| (pkg.package_id(), pkg.clone()))
114+
.collect();
115+
// Start from the workspace roots, and recurse through filling out the
116+
// map, filtering targets as necessary.
117+
let mut node_map = HashMap::new();
118+
for member_pkg in ws.members() {
119+
build_resolve_graph_r(
120+
&mut node_map,
121+
member_pkg.package_id(),
122+
&ws_resolve.targeted_resolve,
123+
&package_map,
124+
target_info.as_ref(),
125+
);
139126
}
127+
// Get a Vec of Packages.
128+
let actual_packages = package_map
129+
.drain()
130+
.filter_map(|(pkg_id, pkg)| node_map.get(&pkg_id).map(|_| pkg))
131+
.collect();
132+
let mr = MetadataResolve {
133+
nodes: node_map.drain().map(|(_pkg_id, node)| node).collect(),
134+
root: ws.current_opt().map(|pkg| pkg.package_id()),
135+
};
136+
Ok((actual_packages, mr))
137+
}
140138

141-
#[derive(Serialize)]
142-
struct Node<'a> {
143-
id: PackageId,
144-
dependencies: Vec<PackageId>,
145-
deps: Vec<Dep>,
146-
features: Vec<&'a str>,
139+
fn build_resolve_graph_r(
140+
node_map: &mut HashMap<PackageId, MetadataResolveNode>,
141+
pkg_id: PackageId,
142+
resolve: &Resolve,
143+
package_map: &HashMap<PackageId, Package>,
144+
target: Option<&(String, TargetInfo)>,
145+
) {
146+
if node_map.contains_key(&pkg_id) {
147+
return;
147148
}
148-
149-
// A filter for removing platform dependencies.
150-
let dep_filter = |(_pkg, deps): &(PackageId, &[Dependency])| match (target, cfg) {
151-
(Some(target), Some(cfg)) => deps.iter().any(|dep| {
152-
let platform = match dep.platform() {
153-
Some(p) => p,
154-
None => return true,
155-
};
156-
platform.matches(target, cfg)
157-
}),
158-
(None, None) => true,
159-
_ => unreachable!(),
149+
let features = resolve
150+
.features_sorted(pkg_id)
151+
.into_iter()
152+
.map(|s| s.to_string())
153+
.collect();
154+
let deps: Vec<Dep> = resolve
155+
.deps(pkg_id)
156+
.filter(|(_dep_id, deps)| match target {
157+
Some((short_name, info)) => deps.iter().any(|dep| {
158+
let platform = match dep.platform() {
159+
Some(p) => p,
160+
None => return true,
161+
};
162+
platform.matches(short_name, info.cfg())
163+
}),
164+
None => true,
165+
})
166+
.filter_map(|(dep_id, _deps)| {
167+
package_map
168+
.get(&dep_id)
169+
.and_then(|pkg| pkg.targets().iter().find(|t| t.is_lib()))
170+
.and_then(|lib_target| resolve.extern_crate_name(pkg_id, dep_id, lib_target).ok())
171+
.map(|name| Dep { name, pkg: dep_id })
172+
})
173+
.collect();
174+
let dumb_deps: Vec<PackageId> = deps.iter().map(|dep| dep.pkg).collect();
175+
let to_visit = dumb_deps.clone();
176+
let node = MetadataResolveNode {
177+
id: pkg_id,
178+
dependencies: dumb_deps,
179+
deps,
180+
features,
160181
};
161-
162-
s.collect_seq(resolve.iter().map(|id| {
163-
Node {
164-
id,
165-
dependencies: resolve
166-
.deps(id)
167-
.filter(dep_filter)
168-
.map(|(pkg, _deps)| pkg)
169-
.collect(),
170-
deps: resolve
171-
.deps(id)
172-
.filter(dep_filter)
173-
.filter_map(|(pkg, _deps)| {
174-
packages
175-
.get(&pkg)
176-
.and_then(|pkg| pkg.targets().iter().find(|t| t.is_lib()))
177-
.and_then(|lib_target| resolve.extern_crate_name(id, pkg, lib_target).ok())
178-
.map(|name| Dep { name, pkg })
179-
})
180-
.collect(),
181-
features: resolve.features_sorted(id),
182-
}
183-
}))
182+
node_map.insert(pkg_id, node);
183+
for dep_id in to_visit {
184+
build_resolve_graph_r(node_map, dep_id, resolve, package_map, target);
185+
}
184186
}

0 commit comments

Comments
 (0)