1- use std:: collections:: HashMap ;
2- use std:: path:: PathBuf ;
3-
4- use serde:: ser;
5- use serde:: Serialize ;
6-
1+ use crate :: core:: compiler:: { CompileKind , CompileTarget , TargetInfo } ;
72use crate :: core:: resolver:: { Resolve , ResolveOpts } ;
83use crate :: core:: { Package , PackageId , Workspace } ;
94use crate :: ops:: { self , Packages } ;
105use crate :: util:: CargoResult ;
116
7+ use serde:: Serialize ;
8+ use std:: collections:: HashMap ;
9+ use std:: path:: PathBuf ;
10+
1211const VERSION : u32 = 1 ;
1312
1413pub struct OutputMetadataOptions {
@@ -17,6 +16,7 @@ pub struct OutputMetadataOptions {
1716 pub all_features : bool ,
1817 pub no_deps : bool ,
1918 pub version : u32 ,
19+ pub filter_platform : Option < String > ,
2020}
2121
2222/// Loads the manifest, resolves the dependencies of the package to the concrete
@@ -30,54 +30,33 @@ pub fn output_metadata(ws: &Workspace<'_>, opt: &OutputMetadataOptions) -> Cargo
3030 VERSION
3131 ) ;
3232 }
33- if opt. no_deps {
34- metadata_no_deps ( ws, opt)
33+ let ( packages, resolve) = if opt. no_deps {
34+ let packages = ws. members ( ) . cloned ( ) . collect ( ) ;
35+ ( packages, None )
3536 } else {
36- metadata_full ( ws, opt)
37- }
38- }
39-
40- fn metadata_no_deps ( ws : & Workspace < ' _ > , _opt : & OutputMetadataOptions ) -> CargoResult < ExportInfo > {
41- Ok ( ExportInfo {
42- packages : ws. members ( ) . cloned ( ) . collect ( ) ,
43- workspace_members : ws. members ( ) . map ( |pkg| pkg. package_id ( ) ) . collect ( ) ,
44- resolve : None ,
45- target_directory : ws. target_dir ( ) . into_path_unlocked ( ) ,
46- version : VERSION ,
47- workspace_root : ws. root ( ) . to_path_buf ( ) ,
48- } )
49- }
50-
51- fn metadata_full ( ws : & Workspace < ' _ > , opt : & OutputMetadataOptions ) -> CargoResult < ExportInfo > {
52- let specs = Packages :: All . to_package_id_specs ( ws) ?;
53- let opts = ResolveOpts :: new (
54- /*dev_deps*/ true ,
55- & opt. features ,
56- opt. all_features ,
57- !opt. no_default_features ,
58- ) ;
59- let ws_resolve = ops:: resolve_ws_with_opts ( ws, opts, & specs) ?;
60- let mut packages = HashMap :: new ( ) ;
61- for pkg in ws_resolve
62- . pkg_set
63- . get_many ( ws_resolve. pkg_set . package_ids ( ) ) ?
64- {
65- packages. insert ( pkg. package_id ( ) , pkg. clone ( ) ) ;
66- }
37+ let resolve_opts = ResolveOpts :: new (
38+ /*dev_deps*/ true ,
39+ & opt. features ,
40+ opt. all_features ,
41+ !opt. no_default_features ,
42+ ) ;
43+ let ( packages, resolve) = build_resolve_graph ( ws, resolve_opts, & opt. filter_platform ) ?;
44+ ( packages, Some ( resolve) )
45+ } ;
6746
6847 Ok ( ExportInfo {
69- packages : packages . values ( ) . map ( |p| ( * p ) . clone ( ) ) . collect ( ) ,
48+ packages,
7049 workspace_members : ws. members ( ) . map ( |pkg| pkg. package_id ( ) ) . collect ( ) ,
71- resolve : Some ( MetadataResolve {
72- resolve : ( packages, ws_resolve. targeted_resolve ) ,
73- root : ws. current_opt ( ) . map ( |pkg| pkg. package_id ( ) ) ,
74- } ) ,
50+ resolve,
7551 target_directory : ws. target_dir ( ) . into_path_unlocked ( ) ,
7652 version : VERSION ,
7753 workspace_root : ws. root ( ) . to_path_buf ( ) ,
7854 } )
7955}
8056
57+ /// This is the structure that is serialized and displayed to the user.
58+ ///
59+ /// See cargo-metadata.adoc for detailed documentation of the format.
8160#[ derive( Serialize ) ]
8261pub struct ExportInfo {
8362 packages : Vec < Package > ,
@@ -88,52 +67,124 @@ pub struct ExportInfo {
8867 workspace_root : PathBuf ,
8968}
9069
91- /// Newtype wrapper to provide a custom `Serialize` implementation.
92- /// The one from lock file does not fit because it uses a non-standard
93- /// format for `PackageId`s
9470#[ derive( Serialize ) ]
9571struct MetadataResolve {
96- #[ serde( rename = "nodes" , serialize_with = "serialize_resolve" ) ]
97- resolve : ( HashMap < PackageId , Package > , Resolve ) ,
72+ nodes : Vec < MetadataResolveNode > ,
9873 root : Option < PackageId > ,
9974}
10075
101- fn serialize_resolve < S > (
102- ( packages, resolve) : & ( HashMap < PackageId , Package > , Resolve ) ,
103- s : S ,
104- ) -> Result < S :: Ok , S :: Error >
105- where
106- S : ser:: Serializer ,
107- {
108- #[ derive( Serialize ) ]
109- struct Dep {
110- name : String ,
111- pkg : PackageId ,
112- }
76+ #[ derive( Serialize ) ]
77+ struct MetadataResolveNode {
78+ id : PackageId ,
79+ dependencies : Vec < PackageId > ,
80+ deps : Vec < Dep > ,
81+ features : Vec < String > ,
82+ }
11383
114- #[ derive( Serialize ) ]
115- struct Node < ' a > {
116- id : PackageId ,
117- dependencies : Vec < PackageId > ,
118- deps : Vec < Dep > ,
119- features : Vec < & ' a str > ,
120- }
84+ #[ derive( Serialize ) ]
85+ struct Dep {
86+ name : String ,
87+ pkg : PackageId ,
88+ }
12189
122- s. collect_seq ( resolve. iter ( ) . map ( |id| {
123- Node {
124- id,
125- dependencies : resolve. deps ( id) . map ( |( pkg, _deps) | pkg) . collect ( ) ,
126- deps : resolve
127- . deps ( id)
128- . filter_map ( |( pkg, _deps) | {
129- packages
130- . get ( & pkg)
131- . and_then ( |pkg| pkg. targets ( ) . iter ( ) . find ( |t| t. is_lib ( ) ) )
132- . and_then ( |lib_target| resolve. extern_crate_name ( id, pkg, lib_target) . ok ( ) )
133- . map ( |name| Dep { name, pkg } )
134- } )
135- . collect ( ) ,
136- features : resolve. features_sorted ( id) ,
90+ /// Builds the resolve graph as it will be displayed to the user.
91+ fn build_resolve_graph (
92+ ws : & Workspace < ' _ > ,
93+ resolve_opts : ResolveOpts ,
94+ target : & Option < String > ,
95+ ) -> CargoResult < ( Vec < Package > , MetadataResolve ) > {
96+ let target_info = match target {
97+ Some ( target) => {
98+ let config = ws. config ( ) ;
99+ let ct = CompileTarget :: new ( target) ?;
100+ let short_name = ct. short_name ( ) . to_string ( ) ;
101+ let kind = CompileKind :: Target ( ct) ;
102+ let rustc = config. load_global_rustc ( Some ( ws) ) ?;
103+ Some ( ( short_name, TargetInfo :: new ( config, kind, & rustc, kind) ?) )
137104 }
138- } ) )
105+ None => None ,
106+ } ;
107+ // Resolve entire workspace.
108+ let specs = Packages :: All . to_package_id_specs ( ws) ?;
109+ let ws_resolve = ops:: resolve_ws_with_opts ( ws, resolve_opts, & specs) ?;
110+ // Download all Packages. This is needed to serialize the information
111+ // for every package. In theory this could honor target filtering,
112+ // but that would be somewhat complex.
113+ let mut package_map: HashMap < PackageId , Package > = ws_resolve
114+ . pkg_set
115+ . get_many ( ws_resolve. pkg_set . package_ids ( ) ) ?
116+ . into_iter ( )
117+ . map ( |pkg| ( pkg. package_id ( ) , pkg. clone ( ) ) )
118+ . collect ( ) ;
119+ // Start from the workspace roots, and recurse through filling out the
120+ // map, filtering targets as necessary.
121+ let mut node_map = HashMap :: new ( ) ;
122+ for member_pkg in ws. members ( ) {
123+ build_resolve_graph_r (
124+ & mut node_map,
125+ member_pkg. package_id ( ) ,
126+ & ws_resolve. targeted_resolve ,
127+ & package_map,
128+ target_info. as_ref ( ) ,
129+ ) ;
130+ }
131+ // Get a Vec of Packages.
132+ let actual_packages = package_map
133+ . drain ( )
134+ . filter_map ( |( pkg_id, pkg) | node_map. get ( & pkg_id) . map ( |_| pkg) )
135+ . collect ( ) ;
136+ let mr = MetadataResolve {
137+ nodes : node_map. drain ( ) . map ( |( _pkg_id, node) | node) . collect ( ) ,
138+ root : ws. current_opt ( ) . map ( |pkg| pkg. package_id ( ) ) ,
139+ } ;
140+ Ok ( ( actual_packages, mr) )
141+ }
142+
143+ fn build_resolve_graph_r (
144+ node_map : & mut HashMap < PackageId , MetadataResolveNode > ,
145+ pkg_id : PackageId ,
146+ resolve : & Resolve ,
147+ package_map : & HashMap < PackageId , Package > ,
148+ target : Option < & ( String , TargetInfo ) > ,
149+ ) {
150+ if node_map. contains_key ( & pkg_id) {
151+ return ;
152+ }
153+ let features = resolve
154+ . features_sorted ( pkg_id)
155+ . into_iter ( )
156+ . map ( |s| s. to_string ( ) )
157+ . collect ( ) ;
158+ let deps: Vec < Dep > = resolve
159+ . deps ( pkg_id)
160+ . filter ( |( _dep_id, deps) | match target {
161+ Some ( ( short_name, info) ) => deps. iter ( ) . any ( |dep| {
162+ let platform = match dep. platform ( ) {
163+ Some ( p) => p,
164+ None => return true ,
165+ } ;
166+ platform. matches ( short_name, info. cfg ( ) )
167+ } ) ,
168+ None => true ,
169+ } )
170+ . filter_map ( |( dep_id, _deps) | {
171+ package_map
172+ . get ( & dep_id)
173+ . and_then ( |pkg| pkg. targets ( ) . iter ( ) . find ( |t| t. is_lib ( ) ) )
174+ . and_then ( |lib_target| resolve. extern_crate_name ( pkg_id, dep_id, lib_target) . ok ( ) )
175+ . map ( |name| Dep { name, pkg : dep_id } )
176+ } )
177+ . collect ( ) ;
178+ let dumb_deps: Vec < PackageId > = deps. iter ( ) . map ( |dep| dep. pkg ) . collect ( ) ;
179+ let to_visit = dumb_deps. clone ( ) ;
180+ let node = MetadataResolveNode {
181+ id : pkg_id,
182+ dependencies : dumb_deps,
183+ deps,
184+ features,
185+ } ;
186+ node_map. insert ( pkg_id, node) ;
187+ for dep_id in to_visit {
188+ build_resolve_graph_r ( node_map, dep_id, resolve, package_map, target) ;
189+ }
139190}
0 commit comments