1use crate::core::compiler::artifact::match_artifacts_kind_with_targets;
2use crate::core::compiler::{CompileKind, CompileKindFallback, RustcTargetData};
3use crate::core::dependency::DepKind;
4use crate::core::package::SerializedPackage;
5use crate::core::resolver::{HasDevUnits, Resolve, features::CliFeatures};
6use crate::core::{Package, PackageId, PackageIdSpec, Workspace};
7use crate::ops::{self, Packages};
8use crate::util::CargoResult;
9use crate::util::interning::InternedString;
10use cargo_platform::Platform;
11use serde::Serialize;
12use std::collections::BTreeMap;
13use std::path::PathBuf;
14
15const VERSION: u32 = 1;
16
17pub struct OutputMetadataOptions {
18 pub cli_features: CliFeatures,
19 pub no_deps: bool,
20 pub version: u32,
21 pub filter_platforms: Vec<String>,
22}
23
24pub fn output_metadata(ws: &Workspace<'_>, opt: &OutputMetadataOptions) -> CargoResult<ExportInfo> {
28 if opt.version != VERSION {
29 anyhow::bail!(
30 "metadata version {} not supported, only {} is currently supported",
31 opt.version,
32 VERSION
33 );
34 }
35 let (packages, resolve) = if opt.no_deps {
36 let packages = ws
37 .members()
38 .map(|pkg| pkg.serialized(ws.gctx().cli_unstable(), ws.unstable_features()))
39 .collect();
40 (packages, None)
41 } else {
42 let (packages, resolve) = build_resolve_graph(ws, opt)?;
43 (packages, Some(resolve))
44 };
45
46 Ok(ExportInfo {
47 packages,
48 workspace_members: ws.members().map(|pkg| pkg.package_id().to_spec()).collect(),
49 workspace_default_members: ws
50 .default_members()
51 .map(|pkg| pkg.package_id().to_spec())
52 .collect(),
53 resolve,
54 target_directory: ws.target_dir().into_path_unlocked(),
55 build_directory: ws.build_dir().into_path_unlocked(),
56 version: VERSION,
57 workspace_root: ws.root().to_path_buf(),
58 metadata: ws.custom_metadata().cloned(),
59 })
60}
61
62#[derive(Serialize)]
66pub struct ExportInfo {
67 packages: Vec<SerializedPackage>,
68 workspace_members: Vec<PackageIdSpec>,
69 workspace_default_members: Vec<PackageIdSpec>,
70 resolve: Option<MetadataResolve>,
71 target_directory: PathBuf,
72 build_directory: PathBuf,
73 version: u32,
74 workspace_root: PathBuf,
75 metadata: Option<toml::Value>,
76}
77
78#[derive(Serialize)]
79struct MetadataResolve {
80 nodes: Vec<MetadataResolveNode>,
81 root: Option<PackageIdSpec>,
82}
83
84#[derive(Serialize)]
85struct MetadataResolveNode {
86 id: PackageIdSpec,
87 dependencies: Vec<PackageIdSpec>,
88 deps: Vec<Dep>,
89 features: Vec<InternedString>,
90}
91
92#[derive(Serialize)]
93struct Dep {
94 name: InternedString,
97 pkg: PackageIdSpec,
98 #[serde(skip)]
99 pkg_id: PackageId,
100 dep_kinds: Vec<DepKindInfo>,
101}
102
103#[derive(Serialize, PartialEq, Eq, PartialOrd, Ord)]
104struct DepKindInfo {
105 kind: DepKind,
106 target: Option<Platform>,
107
108 #[serde(skip_serializing_if = "Option::is_none")]
114 extern_name: Option<InternedString>,
115 #[serde(skip_serializing_if = "Option::is_none")]
117 artifact: Option<&'static str>,
118 #[serde(skip_serializing_if = "Option::is_none")]
123 compile_target: Option<InternedString>,
124 #[serde(skip_serializing_if = "Option::is_none")]
126 bin_name: Option<String>,
127 }
129
130fn build_resolve_graph(
132 ws: &Workspace<'_>,
133 metadata_opts: &OutputMetadataOptions,
134) -> CargoResult<(Vec<SerializedPackage>, MetadataResolve)> {
135 let requested_kinds = CompileKind::from_requested_targets_with_fallback(
143 ws.gctx(),
144 &metadata_opts.filter_platforms,
145 CompileKindFallback::JustHost,
146 )?;
147 let mut target_data = RustcTargetData::new(ws, &requested_kinds)?;
148 let specs = Packages::All(Vec::new()).to_package_id_specs(ws)?;
150 let force_all = if metadata_opts.filter_platforms.is_empty() {
151 crate::core::resolver::features::ForceAllTargets::Yes
152 } else {
153 crate::core::resolver::features::ForceAllTargets::No
154 };
155
156 let dry_run = false;
159 let ws_resolve = ops::resolve_ws_with_opts(
160 ws,
161 &mut target_data,
162 &requested_kinds,
163 &metadata_opts.cli_features,
164 &specs,
165 HasDevUnits::Yes,
166 force_all,
167 dry_run,
168 )?;
169
170 let package_map: BTreeMap<PackageId, Package> = ws_resolve
171 .pkg_set
172 .packages()
173 .map(|pkg| (pkg.package_id(), Package::clone(pkg)))
175 .collect();
176
177 let mut node_map = BTreeMap::new();
180 for member_pkg in ws.members() {
181 build_resolve_graph_r(
182 &mut node_map,
183 member_pkg.package_id(),
184 &ws_resolve.targeted_resolve,
185 &package_map,
186 &target_data,
187 &requested_kinds,
188 )?;
189 }
190 let actual_packages = package_map
192 .into_iter()
193 .filter_map(|(pkg_id, pkg)| node_map.get(&pkg_id).map(|_| pkg))
194 .map(|pkg| pkg.serialized(ws.gctx().cli_unstable(), ws.unstable_features()))
195 .collect();
196
197 let mr = MetadataResolve {
198 nodes: node_map.into_iter().map(|(_pkg_id, node)| node).collect(),
199 root: ws.current_opt().map(|pkg| pkg.package_id().to_spec()),
200 };
201 Ok((actual_packages, mr))
202}
203
204fn build_resolve_graph_r(
205 node_map: &mut BTreeMap<PackageId, MetadataResolveNode>,
206 pkg_id: PackageId,
207 resolve: &Resolve,
208 package_map: &BTreeMap<PackageId, Package>,
209 target_data: &RustcTargetData<'_>,
210 requested_kinds: &[CompileKind],
211) -> CargoResult<()> {
212 if node_map.contains_key(&pkg_id) {
213 return Ok(());
214 }
215 let normalize_id = |id| -> PackageId { *package_map.get_key_value(&id).unwrap().0 };
230 let features = resolve.features(pkg_id).to_vec();
231
232 let deps = {
233 let mut dep_metadatas = Vec::new();
234 let iter = resolve.deps(pkg_id).filter(|(_dep_id, deps)| {
235 if requested_kinds == [CompileKind::Host] {
236 true
237 } else {
238 requested_kinds.iter().any(|kind| {
239 deps.iter()
240 .any(|dep| target_data.dep_platform_activated(dep, *kind))
241 })
242 }
243 });
244 for (dep_id, deps) in iter {
245 let mut dep_kinds = Vec::new();
246
247 let targets = package_map[&dep_id].targets();
248
249 let extern_name = |target| {
251 resolve
252 .extern_crate_name_and_dep_name(pkg_id, dep_id, target)
253 .map(|(ext_crate_name, _)| ext_crate_name)
254 };
255
256 let lib_target = targets.iter().find(|t| t.is_lib());
257
258 for dep in deps.iter() {
259 if let Some(target) = lib_target {
260 let included = match dep.artifact() {
262 None => true,
264 Some(a) if a.is_lib() => true,
266 _ => false,
267 };
268 let extern_name = if dep.artifact().is_some() {
272 Some(extern_name(target)?)
273 } else {
274 None
275 };
276 if included {
277 dep_kinds.push(DepKindInfo {
278 kind: dep.kind(),
279 target: dep.platform().cloned(),
280 extern_name,
281 artifact: None,
282 compile_target: None,
283 bin_name: None,
284 });
285 }
286 }
287
288 let Some(artifact_requirements) = dep.artifact() else {
290 continue;
291 };
292
293 let compile_target = match artifact_requirements.target() {
294 Some(t) => t
295 .to_compile_target()
296 .map(|t| t.rustc_target())
297 .or_else(|| Some("<target>".into())),
301 None => None,
302 };
303
304 let target_set =
305 match_artifacts_kind_with_targets(dep, targets, pkg_id.name().as_str())?;
306 dep_kinds.reserve(target_set.len());
307 for (kind, target) in target_set.into_iter() {
308 dep_kinds.push(DepKindInfo {
309 kind: dep.kind(),
310 target: dep.platform().cloned(),
311 extern_name: extern_name(target).ok(),
312 artifact: Some(kind.crate_type()),
313 compile_target,
314 bin_name: target.is_bin().then(|| target.name().to_string()),
315 })
316 }
317 }
318
319 dep_kinds.sort();
320
321 let pkg_id = normalize_id(dep_id);
322
323 let dep = match (lib_target, dep_kinds.len()) {
324 (Some(target), _) => Dep {
325 name: extern_name(target)?,
326 pkg: pkg_id.to_spec(),
327 pkg_id,
328 dep_kinds,
329 },
330 (None, 1..) => Dep {
332 name: "".into(),
333 pkg: pkg_id.to_spec(),
334 pkg_id,
335 dep_kinds,
336 },
337 (None, _) => continue,
340 };
341
342 dep_metadatas.push(dep)
343 }
344 dep_metadatas
345 };
346
347 let to_visit: Vec<PackageId> = deps.iter().map(|dep| dep.pkg_id).collect();
348 let node = MetadataResolveNode {
349 id: normalize_id(pkg_id).to_spec(),
350 dependencies: to_visit.iter().map(|id| id.to_spec()).collect(),
351 deps,
352 features,
353 };
354 node_map.insert(pkg_id, node);
355 for dep_id in to_visit {
356 build_resolve_graph_r(
357 node_map,
358 dep_id,
359 resolve,
360 package_map,
361 target_data,
362 requested_kinds,
363 )?;
364 }
365
366 Ok(())
367}