11//! Handles build script specific information
22
33use std:: {
4- io:: BufReader ,
54 path:: PathBuf ,
65 process:: { Command , Stdio } ,
76 sync:: Arc ,
@@ -13,7 +12,8 @@ use cargo_metadata::{BuildScript, Message};
1312use itertools:: Itertools ;
1413use paths:: { AbsPath , AbsPathBuf } ;
1514use rustc_hash:: FxHashMap ;
16- use stdx:: { format_to, JodChild } ;
15+ use serde:: Deserialize ;
16+ use stdx:: format_to;
1717
1818use crate :: { cfg_flag:: CfgFlag , CargoConfig } ;
1919
@@ -171,67 +171,86 @@ impl WorkspaceBuildData {
171171
172172 cmd. stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: piped ( ) ) . stdin ( Stdio :: null ( ) ) ;
173173
174- let mut child = cmd. spawn ( ) . map ( JodChild ) ?;
175- let child_stdout = child. stdout . take ( ) . unwrap ( ) ;
176- let stdout = BufReader :: new ( child_stdout) ;
177-
178174 let mut res = WorkspaceBuildData :: default ( ) ;
179- for message in cargo_metadata:: Message :: parse_stream ( stdout) . flatten ( ) {
180- match message {
181- Message :: BuildScriptExecuted ( BuildScript {
182- package_id,
183- out_dir,
184- cfgs,
185- env,
186- ..
187- } ) => {
188- let cfgs = {
189- let mut acc = Vec :: new ( ) ;
190- for cfg in cfgs {
191- match cfg. parse :: < CfgFlag > ( ) {
192- Ok ( it) => acc. push ( it) ,
193- Err ( err) => {
194- anyhow:: bail!( "invalid cfg from cargo-metadata: {}" , err)
195- }
196- } ;
197- }
198- acc
199- } ;
200- let package_build_data =
201- res. per_package . entry ( package_id. repr . clone ( ) ) . or_default ( ) ;
202- // cargo_metadata crate returns default (empty) path for
203- // older cargos, which is not absolute, so work around that.
204- if !out_dir. as_str ( ) . is_empty ( ) {
205- let out_dir = AbsPathBuf :: assert ( PathBuf :: from ( out_dir. into_os_string ( ) ) ) ;
206- package_build_data. out_dir = Some ( out_dir) ;
207- package_build_data. cfgs = cfgs;
208- }
209175
210- package_build_data. envs = env;
176+ let mut callback_err = None ;
177+ let output = stdx:: process:: streaming_output (
178+ cmd,
179+ & mut |line| {
180+ if callback_err. is_some ( ) {
181+ return ;
211182 }
212- Message :: CompilerArtifact ( message) => {
213- progress ( format ! ( "metadata {}" , message. target. name) ) ;
214-
215- if message. target . kind . contains ( & "proc-macro" . to_string ( ) ) {
216- let package_id = message. package_id ;
217- // Skip rmeta file
218- if let Some ( filename) = message. filenames . iter ( ) . find ( |name| is_dylib ( name) )
219- {
220- let filename = AbsPathBuf :: assert ( PathBuf :: from ( & filename) ) ;
221- let package_build_data =
222- res. per_package . entry ( package_id. repr . clone ( ) ) . or_default ( ) ;
223- package_build_data. proc_macro_dylib_path = Some ( filename) ;
183+
184+ // Copy-pasted from existing cargo_metadata. It seems like we
185+ // should be using sered_stacker here?
186+ let mut deserializer = serde_json:: Deserializer :: from_str ( & line) ;
187+ deserializer. disable_recursion_limit ( ) ;
188+ let message = Message :: deserialize ( & mut deserializer)
189+ . unwrap_or ( Message :: TextLine ( line. to_string ( ) ) ) ;
190+
191+ match message {
192+ Message :: BuildScriptExecuted ( BuildScript {
193+ package_id,
194+ out_dir,
195+ cfgs,
196+ env,
197+ ..
198+ } ) => {
199+ let cfgs = {
200+ let mut acc = Vec :: new ( ) ;
201+ for cfg in cfgs {
202+ match cfg. parse :: < CfgFlag > ( ) {
203+ Ok ( it) => acc. push ( it) ,
204+ Err ( err) => {
205+ callback_err = Some ( anyhow:: format_err!(
206+ "invalid cfg from cargo-metadata: {}" ,
207+ err
208+ ) ) ;
209+ return ;
210+ }
211+ } ;
212+ }
213+ acc
214+ } ;
215+ let package_build_data =
216+ res. per_package . entry ( package_id. repr . clone ( ) ) . or_default ( ) ;
217+ // cargo_metadata crate returns default (empty) path for
218+ // older cargos, which is not absolute, so work around that.
219+ if !out_dir. as_str ( ) . is_empty ( ) {
220+ let out_dir =
221+ AbsPathBuf :: assert ( PathBuf :: from ( out_dir. into_os_string ( ) ) ) ;
222+ package_build_data. out_dir = Some ( out_dir) ;
223+ package_build_data. cfgs = cfgs;
224224 }
225+
226+ package_build_data. envs = env;
225227 }
228+ Message :: CompilerArtifact ( message) => {
229+ progress ( format ! ( "metadata {}" , message. target. name) ) ;
230+
231+ if message. target . kind . contains ( & "proc-macro" . to_string ( ) ) {
232+ let package_id = message. package_id ;
233+ // Skip rmeta file
234+ if let Some ( filename) =
235+ message. filenames . iter ( ) . find ( |name| is_dylib ( name) )
236+ {
237+ let filename = AbsPathBuf :: assert ( PathBuf :: from ( & filename) ) ;
238+ let package_build_data =
239+ res. per_package . entry ( package_id. repr . clone ( ) ) . or_default ( ) ;
240+ package_build_data. proc_macro_dylib_path = Some ( filename) ;
241+ }
242+ }
243+ }
244+ Message :: CompilerMessage ( message) => {
245+ progress ( message. target . name . clone ( ) ) ;
246+ }
247+ Message :: BuildFinished ( _) => { }
248+ Message :: TextLine ( _) => { }
249+ _ => { }
226250 }
227- Message :: CompilerMessage ( message) => {
228- progress ( message. target . name . clone ( ) ) ;
229- }
230- Message :: BuildFinished ( _) => { }
231- Message :: TextLine ( _) => { }
232- _ => { }
233- }
234- }
251+ } ,
252+ & mut |_| ( ) ,
253+ ) ?;
235254
236255 for package in packages {
237256 let package_build_data = res. per_package . entry ( package. id . repr . clone ( ) ) . or_default ( ) ;
@@ -244,7 +263,6 @@ impl WorkspaceBuildData {
244263 }
245264 }
246265
247- let output = child. into_inner ( ) . wait_with_output ( ) ?;
248266 if !output. status . success ( ) {
249267 let mut stderr = String :: from_utf8 ( output. stderr ) . unwrap_or_default ( ) ;
250268 if stderr. is_empty ( ) {
0 commit comments