@@ -13,12 +13,12 @@ use cargo_metadata::{BuildScript, Message};
13
13
use itertools:: Itertools ;
14
14
use paths:: { AbsPath , AbsPathBuf } ;
15
15
use rustc_hash:: FxHashMap ;
16
- use stdx:: JodChild ;
16
+ use stdx:: { format_to , JodChild } ;
17
17
18
18
use crate :: { cfg_flag:: CfgFlag , CargoConfig } ;
19
19
20
20
#[ derive( Debug , Clone , Default , PartialEq , Eq ) ]
21
- pub ( crate ) struct BuildData {
21
+ pub ( crate ) struct PackageBuildData {
22
22
/// List of config flags defined by this package's build script
23
23
pub ( crate ) cfgs : Vec < CfgFlag > ,
24
24
/// List of cargo-related environment variables with their value
@@ -32,6 +32,17 @@ pub(crate) struct BuildData {
32
32
pub ( crate ) proc_macro_dylib_path : Option < AbsPathBuf > ,
33
33
}
34
34
35
+ #[ derive( Debug , Default , PartialEq , Eq , Clone ) ]
36
+ pub ( crate ) struct WorkspaceBuildData {
37
+ per_package : FxHashMap < String , PackageBuildData > ,
38
+ error : Option < String > ,
39
+ }
40
+
41
+ #[ derive( Debug , Default , PartialEq , Eq , Clone ) ]
42
+ pub struct BuildDataResult {
43
+ per_workspace : FxHashMap < AbsPathBuf , WorkspaceBuildData > ,
44
+ }
45
+
35
46
#[ derive( Clone , Debug ) ]
36
47
pub ( crate ) struct BuildDataConfig {
37
48
cargo_toml : AbsPathBuf ,
@@ -52,13 +63,6 @@ pub struct BuildDataCollector {
52
63
configs : FxHashMap < AbsPathBuf , BuildDataConfig > ,
53
64
}
54
65
55
- #[ derive( Debug , Default , PartialEq , Eq , Clone ) ]
56
- pub struct BuildDataResult {
57
- data : FxHashMap < AbsPathBuf , BuildDataMap > ,
58
- }
59
-
60
- pub ( crate ) type BuildDataMap = FxHashMap < String , BuildData > ;
61
-
62
66
impl BuildDataCollector {
63
67
pub ( crate ) fn add_config ( & mut self , workspace_root : & AbsPath , config : BuildDataConfig ) {
64
68
self . configs . insert ( workspace_root. to_path_buf ( ) , config) ;
@@ -67,7 +71,7 @@ impl BuildDataCollector {
67
71
pub fn collect ( & mut self , progress : & dyn Fn ( String ) ) -> Result < BuildDataResult > {
68
72
let mut res = BuildDataResult :: default ( ) ;
69
73
for ( path, config) in self . configs . iter ( ) {
70
- res. data . insert (
74
+ res. per_workspace . insert (
71
75
path. clone ( ) ,
72
76
collect_from_workspace (
73
77
& config. cargo_toml ,
@@ -81,9 +85,28 @@ impl BuildDataCollector {
81
85
}
82
86
}
83
87
88
+ impl WorkspaceBuildData {
89
+ pub ( crate ) fn get ( & self , package_id : & str ) -> Option < & PackageBuildData > {
90
+ self . per_package . get ( package_id)
91
+ }
92
+ }
93
+
84
94
impl BuildDataResult {
85
- pub ( crate ) fn get ( & self , root : & AbsPath ) -> Option < & BuildDataMap > {
86
- self . data . get ( & root. to_path_buf ( ) )
95
+ pub ( crate ) fn get ( & self , workspace_root : & AbsPath ) -> Option < & WorkspaceBuildData > {
96
+ self . per_workspace . get ( workspace_root)
97
+ }
98
+ pub fn error ( & self ) -> Option < String > {
99
+ let mut buf = String :: new ( ) ;
100
+ for ( _workspace_root, build_data) in & self . per_workspace {
101
+ if let Some ( err) = & build_data. error {
102
+ format_to ! ( buf, "cargo check failed:\n {}" , err) ;
103
+ }
104
+ }
105
+ if buf. is_empty ( ) {
106
+ return None ;
107
+ }
108
+
109
+ Some ( buf)
87
110
}
88
111
}
89
112
@@ -102,7 +125,7 @@ fn collect_from_workspace(
102
125
cargo_features : & CargoConfig ,
103
126
packages : & Vec < cargo_metadata:: Package > ,
104
127
progress : & dyn Fn ( String ) ,
105
- ) -> Result < BuildDataMap > {
128
+ ) -> Result < WorkspaceBuildData > {
106
129
let mut cmd = Command :: new ( toolchain:: cargo ( ) ) ;
107
130
cmd. args ( & [ "check" , "--workspace" , "--message-format=json" , "--manifest-path" ] )
108
131
. arg ( cargo_toml. as_ref ( ) ) ;
@@ -130,13 +153,13 @@ fn collect_from_workspace(
130
153
}
131
154
}
132
155
133
- cmd. stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: null ( ) ) . stdin ( Stdio :: null ( ) ) ;
156
+ cmd. stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: piped ( ) ) . stdin ( Stdio :: null ( ) ) ;
134
157
135
158
let mut child = cmd. spawn ( ) . map ( JodChild ) ?;
136
159
let child_stdout = child. stdout . take ( ) . unwrap ( ) ;
137
160
let stdout = BufReader :: new ( child_stdout) ;
138
161
139
- let mut res = BuildDataMap :: default ( ) ;
162
+ let mut res = WorkspaceBuildData :: default ( ) ;
140
163
for message in cargo_metadata:: Message :: parse_stream ( stdout) . flatten ( ) {
141
164
match message {
142
165
Message :: BuildScriptExecuted ( BuildScript {
@@ -154,16 +177,17 @@ fn collect_from_workspace(
154
177
}
155
178
acc
156
179
} ;
157
- let res = res. entry ( package_id. repr . clone ( ) ) . or_default ( ) ;
180
+ let package_build_data =
181
+ res. per_package . entry ( package_id. repr . clone ( ) ) . or_default ( ) ;
158
182
// cargo_metadata crate returns default (empty) path for
159
183
// older cargos, which is not absolute, so work around that.
160
184
if !out_dir. as_str ( ) . is_empty ( ) {
161
185
let out_dir = AbsPathBuf :: assert ( PathBuf :: from ( out_dir. into_os_string ( ) ) ) ;
162
- res . out_dir = Some ( out_dir) ;
163
- res . cfgs = cfgs;
186
+ package_build_data . out_dir = Some ( out_dir) ;
187
+ package_build_data . cfgs = cfgs;
164
188
}
165
189
166
- res . envs = env;
190
+ package_build_data . envs = env;
167
191
}
168
192
Message :: CompilerArtifact ( message) => {
169
193
progress ( format ! ( "metadata {}" , message. target. name) ) ;
@@ -173,8 +197,9 @@ fn collect_from_workspace(
173
197
// Skip rmeta file
174
198
if let Some ( filename) = message. filenames . iter ( ) . find ( |name| is_dylib ( name) ) {
175
199
let filename = AbsPathBuf :: assert ( PathBuf :: from ( & filename) ) ;
176
- let res = res. entry ( package_id. repr . clone ( ) ) . or_default ( ) ;
177
- res. proc_macro_dylib_path = Some ( filename) ;
200
+ let package_build_data =
201
+ res. per_package . entry ( package_id. repr . clone ( ) ) . or_default ( ) ;
202
+ package_build_data. proc_macro_dylib_path = Some ( filename) ;
178
203
}
179
204
}
180
205
}
@@ -188,16 +213,25 @@ fn collect_from_workspace(
188
213
}
189
214
190
215
for package in packages {
191
- let build_data = res. entry ( package. id . repr . clone ( ) ) . or_default ( ) ;
192
- inject_cargo_env ( package, build_data ) ;
193
- if let Some ( out_dir) = & build_data . out_dir {
216
+ let package_build_data = res. per_package . entry ( package. id . repr . clone ( ) ) . or_default ( ) ;
217
+ inject_cargo_env ( package, package_build_data ) ;
218
+ if let Some ( out_dir) = & package_build_data . out_dir {
194
219
// NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
195
220
if let Some ( out_dir) = out_dir. to_str ( ) . map ( |s| s. to_owned ( ) ) {
196
- build_data . envs . push ( ( "OUT_DIR" . to_string ( ) , out_dir) ) ;
221
+ package_build_data . envs . push ( ( "OUT_DIR" . to_string ( ) , out_dir) ) ;
197
222
}
198
223
}
199
224
}
200
225
226
+ let output = child. into_inner ( ) . wait_with_output ( ) ?;
227
+ if !output. status . success ( ) {
228
+ let mut stderr = String :: from_utf8 ( output. stderr ) . unwrap_or_default ( ) ;
229
+ if stderr. is_empty ( ) {
230
+ stderr = "cargo check failed" . to_string ( ) ;
231
+ }
232
+ res. error = Some ( stderr)
233
+ }
234
+
201
235
Ok ( res)
202
236
}
203
237
@@ -212,7 +246,7 @@ fn is_dylib(path: &Utf8Path) -> bool {
212
246
/// Recreates the compile-time environment variables that Cargo sets.
213
247
///
214
248
/// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
215
- fn inject_cargo_env ( package : & cargo_metadata:: Package , build_data : & mut BuildData ) {
249
+ fn inject_cargo_env ( package : & cargo_metadata:: Package , build_data : & mut PackageBuildData ) {
216
250
let env = & mut build_data. envs ;
217
251
218
252
// FIXME: Missing variables:
0 commit comments