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 } ;
7
2
use crate :: core:: resolver:: { Resolve , ResolveOpts } ;
8
3
use crate :: core:: { Package , PackageId , Workspace } ;
9
4
use crate :: ops:: { self , Packages } ;
10
5
use crate :: util:: CargoResult ;
11
6
7
+ use serde:: Serialize ;
8
+ use std:: collections:: HashMap ;
9
+ use std:: path:: PathBuf ;
10
+
12
11
const VERSION : u32 = 1 ;
13
12
14
13
pub struct OutputMetadataOptions {
@@ -17,6 +16,7 @@ pub struct OutputMetadataOptions {
17
16
pub all_features : bool ,
18
17
pub no_deps : bool ,
19
18
pub version : u32 ,
19
+ pub filter_platform : Option < String > ,
20
20
}
21
21
22
22
/// 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
30
30
VERSION
31
31
) ;
32
32
}
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 )
35
36
} 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
+ } ;
67
46
68
47
Ok ( ExportInfo {
69
- packages : packages . values ( ) . map ( |p| ( * p ) . clone ( ) ) . collect ( ) ,
48
+ packages,
70
49
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,
75
51
target_directory : ws. target_dir ( ) . into_path_unlocked ( ) ,
76
52
version : VERSION ,
77
53
workspace_root : ws. root ( ) . to_path_buf ( ) ,
78
54
} )
79
55
}
80
56
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.
81
60
#[ derive( Serialize ) ]
82
61
pub struct ExportInfo {
83
62
packages : Vec < Package > ,
@@ -88,52 +67,124 @@ pub struct ExportInfo {
88
67
workspace_root : PathBuf ,
89
68
}
90
69
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
94
70
#[ derive( Serialize ) ]
95
71
struct MetadataResolve {
96
- #[ serde( rename = "nodes" , serialize_with = "serialize_resolve" ) ]
97
- resolve : ( HashMap < PackageId , Package > , Resolve ) ,
72
+ nodes : Vec < MetadataResolveNode > ,
98
73
root : Option < PackageId > ,
99
74
}
100
75
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
+ }
113
83
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
+ }
121
89
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) ?) )
137
104
}
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
+ }
139
190
}
0 commit comments