@@ -17,7 +17,7 @@ use std::fs;
1717use std:: path:: { Path , PathBuf } ;
1818use std:: sync:: Arc ;
1919
20- use failure:: Error ;
20+ use failure:: { bail , Error } ;
2121use log:: debug;
2222use same_file:: is_same_file;
2323use serde:: Serialize ;
@@ -217,6 +217,43 @@ fn rustc<'a, 'cfg>(
217217 let fingerprint_dir = cx. files ( ) . fingerprint_dir ( unit) ;
218218 let rmeta_produced = cx. rmeta_required ( unit) ;
219219
220+ // If this unit is producing a required rmeta file then we need to know
221+ // when the rmeta file is ready so we can signal to the rest of Cargo that
222+ // it can continue dependant compilations. To do this we are currently
223+ // required to switch the compiler into JSON message mode, but we still
224+ // want to present human readable errors as well. (this rabbit hole just
225+ // goes and goes)
226+ //
227+ // All that means is that if we're not already in JSON mode we need to
228+ // switch to JSON mode, ensure that rustc error messages can be rendered
229+ // prettily, and then when parsing JSON messages from rustc we need to
230+ // internally understand that we should extract the `rendered` field and
231+ // present it if we can.
232+ let extract_rendered_errors = if rmeta_produced {
233+ match cx. bcx . build_config . message_format {
234+ MessageFormat :: Json => false ,
235+ MessageFormat :: Human => {
236+ rustc
237+ . arg ( "--error-format=json" )
238+ . arg ( "--json-rendered=termcolor" )
239+ . arg ( "-Zunstable-options" ) ;
240+ true
241+ }
242+
243+ // FIXME(rust-lang/rust#60419): right now we have no way of turning
244+ // on JSON messages from the compiler and also asking the rendered
245+ // field to be in the `short` format.
246+ MessageFormat :: Short => {
247+ bail ! (
248+ "currently `--message-format short` is incompatible with \
249+ pipelined compilation"
250+ ) ;
251+ }
252+ }
253+ } else {
254+ false
255+ } ;
256+
220257 return Ok ( Work :: new ( move |state| {
221258 // Only at runtime have we discovered what the extra -L and -l
222259 // arguments are for native libraries, so we process those here. We
@@ -277,7 +314,9 @@ fn rustc<'a, 'cfg>(
277314 & target,
278315 mode,
279316 & mut |line| on_stdout_line ( state, line, package_id, & target) ,
280- & mut |line| on_stderr_line ( state, line, package_id, & target) ,
317+ & mut |line| {
318+ on_stderr_line ( state, line, package_id, & target, extract_rendered_errors)
319+ } ,
281320 )
282321 . map_err ( internal_if_simple_exit_code)
283322 . chain_err ( || format ! ( "Could not compile `{}`." , name) ) ?;
@@ -659,7 +698,7 @@ fn rustdoc<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult
659698 rustdoc
660699 . exec_with_streaming (
661700 & mut |line| on_stdout_line ( state, line, package_id, & target) ,
662- & mut |line| on_stderr_line ( state, line, package_id, & target) ,
701+ & mut |line| on_stderr_line ( state, line, package_id, & target, false ) ,
663702 false ,
664703 )
665704 . chain_err ( || format ! ( "Could not document `{}`." , name) ) ?;
@@ -1054,22 +1093,49 @@ fn on_stderr_line(
10541093 line : & str ,
10551094 package_id : PackageId ,
10561095 target : & Target ,
1096+ extract_rendered_errors : bool ,
10571097) -> CargoResult < ( ) > {
1098+ // We primarily want to use this function to process JSON messages from
1099+ // rustc. The compiler should always print one JSON message per line, and
1100+ // otherwise it may have other output intermingled (think RUST_LOG or
1101+ // something like that), so skip over everything that doesn't look like a
1102+ // JSON message.
1103+ if !line. starts_with ( '{' ) {
1104+ state. stderr ( line) ;
1105+ return Ok ( ( ) ) ;
1106+ }
1107+
1108+ let compiler_message: Box < serde_json:: value:: RawValue > = serde_json:: from_str ( line)
1109+ . map_err ( |_| internal ( & format ! ( "compiler produced invalid json: `{}`" , line) ) ) ?;
1110+
1111+ // In some modes of compilation Cargo switches the compiler to JSON mode but
1112+ // the user didn't request that so we still want to print pretty rustc
1113+ // colorized errors. In those cases (`extract_rendered_errors`) we take a
1114+ // look at the JSON blob we go, see if it's a relevant diagnostics, and if
1115+ // so forward just that diagnostic for us to print.
1116+ if extract_rendered_errors {
1117+ #[ derive( serde:: Deserialize ) ]
1118+ struct CompilerError {
1119+ rendered : String ,
1120+ }
1121+ if let Ok ( error) = serde_json:: from_str :: < CompilerError > ( compiler_message. get ( ) ) {
1122+ state. stderr ( & error. rendered ) ;
1123+ return Ok ( ( ) ) ;
1124+ }
1125+ }
1126+
1127+ // And failing all that above we should have a legitimate JSON diagnostic
1128+ // from the compiler, so wrap it in an external Cargo JSON message
1129+ // indicating which package it came from and then emit it.
1130+ let msg = machine_message:: emit ( & machine_message:: FromCompiler {
1131+ package_id,
1132+ target,
1133+ message : compiler_message,
1134+ } ) ;
1135+
10581136 // Switch json lines from rustc/rustdoc that appear on stderr to instead.
10591137 // We want the stdout of Cargo to always be machine parseable as stderr has
10601138 // our colorized human-readable messages.
1061- if line. starts_with ( '{' ) {
1062- let compiler_message = serde_json:: from_str ( line)
1063- . map_err ( |_| internal ( & format ! ( "compiler produced invalid json: `{}`" , line) ) ) ?;
1064-
1065- let msg = machine_message:: emit ( & machine_message:: FromCompiler {
1066- package_id,
1067- target,
1068- message : compiler_message,
1069- } ) ;
1070- state. stdout ( & msg) ;
1071- } else {
1072- state. stderr ( line) ;
1073- }
1139+ state. stdout ( & msg) ;
10741140 Ok ( ( ) )
10751141}
0 commit comments