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::{features::CliFeatures, HasDevUnits, Resolve};
6use crate::core::{Package, PackageId, PackageIdSpec, Workspace};
7use crate::ops::{self, Packages};
8use crate::util::interning::InternedString;
9use crate::util::CargoResult;
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 version: VERSION,
56 workspace_root: ws.root().to_path_buf(),
57 metadata: ws.custom_metadata().cloned(),
58 })
59}
60
61#[derive(Serialize)]
65pub struct ExportInfo {
66 packages: Vec<SerializedPackage>,
67 workspace_members: Vec<PackageIdSpec>,
68 workspace_default_members: Vec<PackageIdSpec>,
69 resolve: Option<MetadataResolve>,
70 target_directory: PathBuf,
71 version: u32,
72 workspace_root: PathBuf,
73 metadata: Option<toml::Value>,
74}
75
76#[derive(Serialize)]
77struct MetadataResolve {
78 nodes: Vec<MetadataResolveNode>,
79 root: Option<PackageIdSpec>,
80}
81
82#[derive(Serialize)]
83struct MetadataResolveNode {
84 id: PackageIdSpec,
85 dependencies: Vec<PackageIdSpec>,
86 deps: Vec<Dep>,
87 features: Vec<InternedString>,
88}
89
90#[derive(Serialize)]
91struct Dep {
92 name: InternedString,
95 pkg: PackageIdSpec,
96 #[serde(skip)]
97 pkg_id: PackageId,
98 dep_kinds: Vec<DepKindInfo>,
99}
100
101#[derive(Serialize, PartialEq, Eq, PartialOrd, Ord)]
102struct DepKindInfo {
103 kind: DepKind,
104 target: Option<Platform>,
105
106 #[serde(skip_serializing_if = "Option::is_none")]
112 extern_name: Option<InternedString>,
113 #[serde(skip_serializing_if = "Option::is_none")]
115 artifact: Option<&'static str>,
116 #[serde(skip_serializing_if = "Option::is_none")]
121 compile_target: Option<InternedString>,
122 #[serde(skip_serializing_if = "Option::is_none")]
124 bin_name: Option<String>,
125 }
127
128fn build_resolve_graph(
130 ws: &Workspace<'_>,
131 metadata_opts: &OutputMetadataOptions,
132) -> CargoResult<(Vec<SerializedPackage>, MetadataResolve)> {
133 let requested_kinds = CompileKind::from_requested_targets_with_fallback(
141 ws.gctx(),
142 &metadata_opts.filter_platforms,
143 CompileKindFallback::JustHost,
144 )?;
145 let mut target_data = RustcTargetData::new(ws, &requested_kinds)?;
146 let specs = Packages::All(Vec::new()).to_package_id_specs(ws)?;
148 let force_all = if metadata_opts.filter_platforms.is_empty() {
149 crate::core::resolver::features::ForceAllTargets::Yes
150 } else {
151 crate::core::resolver::features::ForceAllTargets::No
152 };
153
154 let dry_run = false;
157 let ws_resolve = ops::resolve_ws_with_opts(
158 ws,
159 &mut target_data,
160 &requested_kinds,
161 &metadata_opts.cli_features,
162 &specs,
163 HasDevUnits::Yes,
164 force_all,
165 dry_run,
166 )?;
167
168 let package_map: BTreeMap<PackageId, Package> = ws_resolve
169 .pkg_set
170 .packages()
171 .map(|pkg| (pkg.package_id(), Package::clone(pkg)))
173 .collect();
174
175 let mut node_map = BTreeMap::new();
178 for member_pkg in ws.members() {
179 build_resolve_graph_r(
180 &mut node_map,
181 member_pkg.package_id(),
182 &ws_resolve.targeted_resolve,
183 &package_map,
184 &target_data,
185 &requested_kinds,
186 )?;
187 }
188 let actual_packages = package_map
190 .into_iter()
191 .filter_map(|(pkg_id, pkg)| node_map.get(&pkg_id).map(|_| pkg))
192 .map(|pkg| pkg.serialized(ws.gctx().cli_unstable(), ws.unstable_features()))
193 .collect();
194
195 let mr = MetadataResolve {
196 nodes: node_map.into_iter().map(|(_pkg_id, node)| node).collect(),
197 root: ws.current_opt().map(|pkg| pkg.package_id().to_spec()),
198 };
199 Ok((actual_packages, mr))
200}
201
202fn build_resolve_graph_r(
203 node_map: &mut BTreeMap<PackageId, MetadataResolveNode>,
204 pkg_id: PackageId,
205 resolve: &Resolve,
206 package_map: &BTreeMap<PackageId, Package>,
207 target_data: &RustcTargetData<'_>,
208 requested_kinds: &[CompileKind],
209) -> CargoResult<()> {
210 if node_map.contains_key(&pkg_id) {
211 return Ok(());
212 }
213 let normalize_id = |id| -> PackageId { *package_map.get_key_value(&id).unwrap().0 };
228 let features = resolve.features(pkg_id).to_vec();
229
230 let deps = {
231 let mut dep_metadatas = Vec::new();
232 let iter = resolve.deps(pkg_id).filter(|(_dep_id, deps)| {
233 if requested_kinds == [CompileKind::Host] {
234 true
235 } else {
236 requested_kinds.iter().any(|kind| {
237 deps.iter()
238 .any(|dep| target_data.dep_platform_activated(dep, *kind))
239 })
240 }
241 });
242 for (dep_id, deps) in iter {
243 let mut dep_kinds = Vec::new();
244
245 let targets = package_map[&dep_id].targets();
246
247 let extern_name = |target| {
249 resolve
250 .extern_crate_name_and_dep_name(pkg_id, dep_id, target)
251 .map(|(ext_crate_name, _)| ext_crate_name)
252 };
253
254 let lib_target = targets.iter().find(|t| t.is_lib());
255
256 for dep in deps.iter() {
257 if let Some(target) = lib_target {
258 let included = match dep.artifact() {
260 None => true,
262 Some(a) if a.is_lib() => true,
264 _ => false,
265 };
266 let extern_name = if dep.artifact().is_some() {
270 Some(extern_name(target)?)
271 } else {
272 None
273 };
274 if included {
275 dep_kinds.push(DepKindInfo {
276 kind: dep.kind(),
277 target: dep.platform().cloned(),
278 extern_name,
279 artifact: None,
280 compile_target: None,
281 bin_name: None,
282 });
283 }
284 }
285
286 let Some(artifact_requirements) = dep.artifact() else {
288 continue;
289 };
290
291 let compile_target = match artifact_requirements.target() {
292 Some(t) => t
293 .to_compile_target()
294 .map(|t| t.rustc_target())
295 .or_else(|| Some(InternedString::new("<target>"))),
299 None => None,
300 };
301
302 let target_set =
303 match_artifacts_kind_with_targets(dep, targets, pkg_id.name().as_str())?;
304 dep_kinds.reserve(target_set.len());
305 for (kind, target) in target_set.into_iter() {
306 dep_kinds.push(DepKindInfo {
307 kind: dep.kind(),
308 target: dep.platform().cloned(),
309 extern_name: extern_name(target).ok(),
310 artifact: Some(kind.crate_type()),
311 compile_target,
312 bin_name: target.is_bin().then(|| target.name().to_string()),
313 })
314 }
315 }
316
317 dep_kinds.sort();
318
319 let pkg_id = normalize_id(dep_id);
320
321 let dep = match (lib_target, dep_kinds.len()) {
322 (Some(target), _) => Dep {
323 name: extern_name(target)?,
324 pkg: pkg_id.to_spec(),
325 pkg_id,
326 dep_kinds,
327 },
328 (None, 1..) => Dep {
330 name: InternedString::new(""),
331 pkg: pkg_id.to_spec(),
332 pkg_id,
333 dep_kinds,
334 },
335 (None, _) => continue,
338 };
339
340 dep_metadatas.push(dep)
341 }
342 dep_metadatas
343 };
344
345 let to_visit: Vec<PackageId> = deps.iter().map(|dep| dep.pkg_id).collect();
346 let node = MetadataResolveNode {
347 id: normalize_id(pkg_id).to_spec(),
348 dependencies: to_visit.iter().map(|id| id.to_spec()).collect(),
349 deps,
350 features,
351 };
352 node_map.insert(pkg_id, node);
353 for dep_id in to_visit {
354 build_resolve_graph_r(
355 node_map,
356 dep_id,
357 resolve,
358 package_map,
359 target_data,
360 requested_kinds,
361 )?;
362 }
363
364 Ok(())
365}