1use crate::core::compiler::artifact::match_artifacts_kind_with_targets;
2use crate::core::compiler::{CompileKind, 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 =
136 CompileKind::from_requested_targets(ws.gctx(), &metadata_opts.filter_platforms)?;
137 let mut target_data = RustcTargetData::new(ws, &requested_kinds)?;
138 let specs = Packages::All(Vec::new()).to_package_id_specs(ws)?;
140 let force_all = if metadata_opts.filter_platforms.is_empty() {
141 crate::core::resolver::features::ForceAllTargets::Yes
142 } else {
143 crate::core::resolver::features::ForceAllTargets::No
144 };
145
146 let dry_run = false;
149 let ws_resolve = ops::resolve_ws_with_opts(
150 ws,
151 &mut target_data,
152 &requested_kinds,
153 &metadata_opts.cli_features,
154 &specs,
155 HasDevUnits::Yes,
156 force_all,
157 dry_run,
158 )?;
159
160 let package_map: BTreeMap<PackageId, Package> = ws_resolve
161 .pkg_set
162 .packages()
163 .map(|pkg| (pkg.package_id(), Package::clone(pkg)))
165 .collect();
166
167 let mut node_map = BTreeMap::new();
170 for member_pkg in ws.members() {
171 build_resolve_graph_r(
172 &mut node_map,
173 member_pkg.package_id(),
174 &ws_resolve.targeted_resolve,
175 &package_map,
176 &target_data,
177 &requested_kinds,
178 )?;
179 }
180 let actual_packages = package_map
182 .into_iter()
183 .filter_map(|(pkg_id, pkg)| node_map.get(&pkg_id).map(|_| pkg))
184 .map(|pkg| pkg.serialized(ws.gctx().cli_unstable(), ws.unstable_features()))
185 .collect();
186
187 let mr = MetadataResolve {
188 nodes: node_map.into_iter().map(|(_pkg_id, node)| node).collect(),
189 root: ws.current_opt().map(|pkg| pkg.package_id().to_spec()),
190 };
191 Ok((actual_packages, mr))
192}
193
194fn build_resolve_graph_r(
195 node_map: &mut BTreeMap<PackageId, MetadataResolveNode>,
196 pkg_id: PackageId,
197 resolve: &Resolve,
198 package_map: &BTreeMap<PackageId, Package>,
199 target_data: &RustcTargetData<'_>,
200 requested_kinds: &[CompileKind],
201) -> CargoResult<()> {
202 if node_map.contains_key(&pkg_id) {
203 return Ok(());
204 }
205 let normalize_id = |id| -> PackageId { *package_map.get_key_value(&id).unwrap().0 };
220 let features = resolve.features(pkg_id).to_vec();
221
222 let deps = {
223 let mut dep_metadatas = Vec::new();
224 let iter = resolve.deps(pkg_id).filter(|(_dep_id, deps)| {
225 if requested_kinds == [CompileKind::Host] {
226 true
227 } else {
228 requested_kinds.iter().any(|kind| {
229 deps.iter()
230 .any(|dep| target_data.dep_platform_activated(dep, *kind))
231 })
232 }
233 });
234 for (dep_id, deps) in iter {
235 let mut dep_kinds = Vec::new();
236
237 let targets = package_map[&dep_id].targets();
238
239 let extern_name = |target| {
241 resolve
242 .extern_crate_name_and_dep_name(pkg_id, dep_id, target)
243 .map(|(ext_crate_name, _)| ext_crate_name)
244 };
245
246 let lib_target = targets.iter().find(|t| t.is_lib());
247
248 for dep in deps.iter() {
249 if let Some(target) = lib_target {
250 let included = match dep.artifact() {
252 None => true,
254 Some(a) if a.is_lib() => true,
256 _ => false,
257 };
258 let extern_name = if dep.artifact().is_some() {
262 Some(extern_name(target)?)
263 } else {
264 None
265 };
266 if included {
267 dep_kinds.push(DepKindInfo {
268 kind: dep.kind(),
269 target: dep.platform().cloned(),
270 extern_name,
271 artifact: None,
272 compile_target: None,
273 bin_name: None,
274 });
275 }
276 }
277
278 let Some(artifact_requirements) = dep.artifact() else {
280 continue;
281 };
282
283 let compile_target = match artifact_requirements.target() {
284 Some(t) => t
285 .to_compile_target()
286 .map(|t| t.rustc_target())
287 .or_else(|| Some(InternedString::new("<target>"))),
291 None => None,
292 };
293
294 let target_set =
295 match_artifacts_kind_with_targets(dep, targets, pkg_id.name().as_str())?;
296 dep_kinds.reserve(target_set.len());
297 for (kind, target) in target_set.into_iter() {
298 dep_kinds.push(DepKindInfo {
299 kind: dep.kind(),
300 target: dep.platform().cloned(),
301 extern_name: extern_name(target).ok(),
302 artifact: Some(kind.crate_type()),
303 compile_target,
304 bin_name: target.is_bin().then(|| target.name().to_string()),
305 })
306 }
307 }
308
309 dep_kinds.sort();
310
311 let pkg_id = normalize_id(dep_id);
312
313 let dep = match (lib_target, dep_kinds.len()) {
314 (Some(target), _) => Dep {
315 name: extern_name(target)?,
316 pkg: pkg_id.to_spec(),
317 pkg_id,
318 dep_kinds,
319 },
320 (None, 1..) => Dep {
322 name: InternedString::new(""),
323 pkg: pkg_id.to_spec(),
324 pkg_id,
325 dep_kinds,
326 },
327 (None, _) => continue,
330 };
331
332 dep_metadatas.push(dep)
333 }
334 dep_metadatas
335 };
336
337 let to_visit: Vec<PackageId> = deps.iter().map(|dep| dep.pkg_id).collect();
338 let node = MetadataResolveNode {
339 id: normalize_id(pkg_id).to_spec(),
340 dependencies: to_visit.iter().map(|id| id.to_spec()).collect(),
341 deps,
342 features,
343 };
344 node_map.insert(pkg_id, node);
345 for dep_id in to_visit {
346 build_resolve_graph_r(
347 node_map,
348 dep_id,
349 resolve,
350 package_map,
351 target_data,
352 requested_kinds,
353 )?;
354 }
355
356 Ok(())
357}