1 use crate::core
::compiler
::{CompileKind, CompileTarget, RustcTargetData}
;
2 use crate::core
::resolver
::{Resolve, ResolveOpts}
;
4 use crate::core
::dependency
::DepKind
;
5 use crate::core
::{Dependency, InternedString, Package, PackageId, Workspace}
;
6 use crate::ops
::{self, Packages}
;
7 use crate::util
::CargoResult
;
8 use cargo_platform
::Platform
;
10 use std
::collections
::HashMap
;
11 use std
::path
::PathBuf
;
13 const VERSION
: u32 = 1;
15 pub struct OutputMetadataOptions
{
16 pub features
: Vec
<String
>,
17 pub no_default_features
: bool
,
18 pub all_features
: bool
,
21 pub filter_platform
: Option
<String
>,
24 /// Loads the manifest, resolves the dependencies of the package to the concrete
25 /// used versions - considering overrides - and writes all dependencies in a JSON
27 pub fn output_metadata(ws
: &Workspace
<'_
>, opt
: &OutputMetadataOptions
) -> CargoResult
<ExportInfo
> {
28 if opt
.version
!= VERSION
{
30 "metadata version {} not supported, only {} is currently supported",
35 let (packages
, resolve
) = if opt
.no_deps
{
36 let packages
= ws
.members().cloned().collect();
39 let (packages
, resolve
) = build_resolve_graph(ws
, opt
)?
;
40 (packages
, Some(resolve
))
45 workspace_members
: ws
.members().map(|pkg
| pkg
.package_id()).collect(),
47 target_directory
: ws
.target_dir().into_path_unlocked(),
49 workspace_root
: ws
.root().to_path_buf(),
53 /// This is the structure that is serialized and displayed to the user.
55 /// See cargo-metadata.adoc for detailed documentation of the format.
57 pub struct ExportInfo
{
58 packages
: Vec
<Package
>,
59 workspace_members
: Vec
<PackageId
>,
60 resolve
: Option
<MetadataResolve
>,
61 target_directory
: PathBuf
,
63 workspace_root
: PathBuf
,
67 struct MetadataResolve
{
68 nodes
: Vec
<MetadataResolveNode
>,
69 root
: Option
<PackageId
>,
73 struct MetadataResolveNode
{
75 dependencies
: Vec
<PackageId
>,
77 features
: Vec
<InternedString
>,
84 dep_kinds
: Vec
<DepKindInfo
>,
87 #[derive(Serialize, PartialEq, Eq, PartialOrd, Ord)]
90 target
: Option
<Platform
>,
93 impl From
<&Dependency
> for DepKindInfo
{
94 fn from(dep
: &Dependency
) -> DepKindInfo
{
97 target
: dep
.platform().cloned(),
102 /// Builds the resolve graph as it will be displayed to the user.
103 fn build_resolve_graph(
105 metadata_opts
: &OutputMetadataOptions
,
106 ) -> CargoResult
<(Vec
<Package
>, MetadataResolve
)> {
107 // TODO: Without --filter-platform, features are being resolved for `host` only.
108 // How should this work?
109 let requested_kind
= match &metadata_opts
.filter_platform
{
110 Some(t
) => CompileKind
::Target(CompileTarget
::new(t
)?
),
111 None
=> CompileKind
::Host
,
113 let target_data
= RustcTargetData
::new(ws
, requested_kind
)?
;
114 // Resolve entire workspace.
115 let specs
= Packages
::All
.to_package_id_specs(ws
)?
;
116 let resolve_opts
= ResolveOpts
::new(
118 &metadata_opts
.features
,
119 metadata_opts
.all_features
,
120 !metadata_opts
.no_default_features
,
122 let ws_resolve
= ops
::resolve_ws_with_opts(
130 // Download all Packages. This is needed to serialize the information
131 // for every package. In theory this could honor target filtering,
132 // but that would be somewhat complex.
133 let mut package_map
: HashMap
<PackageId
, Package
> = ws_resolve
135 .get_many(ws_resolve
.pkg_set
.package_ids())?
137 .map(|pkg
| (pkg
.package_id(), pkg
.clone()))
140 // Start from the workspace roots, and recurse through filling out the
141 // map, filtering targets as necessary.
142 let mut node_map
= HashMap
::new();
143 for member_pkg
in ws
.members() {
144 build_resolve_graph_r(
146 member_pkg
.package_id(),
147 &ws_resolve
.targeted_resolve
,
153 // Get a Vec of Packages.
154 let actual_packages
= package_map
156 .filter_map(|(pkg_id
, pkg
)| node_map
.get(&pkg_id
).map(|_
| pkg
))
158 let mr
= MetadataResolve
{
159 nodes
: node_map
.drain().map(|(_pkg_id
, node
)| node
).collect(),
160 root
: ws
.current_opt().map(|pkg
| pkg
.package_id()),
162 Ok((actual_packages
, mr
))
165 fn build_resolve_graph_r(
166 node_map
: &mut HashMap
<PackageId
, MetadataResolveNode
>,
169 package_map
: &HashMap
<PackageId
, Package
>,
170 target_data
: &RustcTargetData
,
171 requested_kind
: CompileKind
,
173 if node_map
.contains_key(&pkg_id
) {
176 let features
= resolve
.features(pkg_id
).iter().cloned().collect();
178 let deps
: Vec
<Dep
> = resolve
180 .filter(|(_dep_id
, deps
)| match requested_kind
{
181 CompileKind
::Target(_
) => deps
183 .any(|dep
| target_data
.dep_platform_activated(dep
, requested_kind
)),
184 // No --filter-platform is interpreted as "all platforms".
185 CompileKind
::Host
=> true,
187 .filter_map(|(dep_id
, deps
)| {
188 let mut dep_kinds
: Vec
<_
> = deps
.iter().map(DepKindInfo
::from
).collect();
189 // Duplicates may appear if the same package is used by different
190 // members of a workspace with different features selected.
191 dep_kinds
.sort_unstable();
195 .and_then(|pkg
| pkg
.targets().iter().find(|t
| t
.is_lib()))
196 .and_then(|lib_target
| resolve
.extern_crate_name(pkg_id
, dep_id
, lib_target
).ok())
204 let dumb_deps
: Vec
<PackageId
> = deps
.iter().map(|dep
| dep
.pkg
).collect();
205 let to_visit
= dumb_deps
.clone();
206 let node
= MetadataResolveNode
{
208 dependencies
: dumb_deps
,
212 node_map
.insert(pkg_id
, node
);
213 for dep_id
in to_visit
{
214 build_resolve_graph_r(