@@ -17,7 +17,7 @@ use std::fs;
17
17
use std:: path:: { Path , PathBuf } ;
18
18
use std:: sync:: Arc ;
19
19
20
- use failure:: Error ;
20
+ use failure:: { bail , Error } ;
21
21
use log:: debug;
22
22
use same_file:: is_same_file;
23
23
use serde:: Serialize ;
@@ -217,6 +217,43 @@ fn rustc<'a, 'cfg>(
217
217
let fingerprint_dir = cx. files ( ) . fingerprint_dir ( unit) ;
218
218
let rmeta_produced = cx. rmeta_required ( unit) ;
219
219
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
+
220
257
return Ok ( Work :: new ( move |state| {
221
258
// Only at runtime have we discovered what the extra -L and -l
222
259
// arguments are for native libraries, so we process those here. We
@@ -277,7 +314,9 @@ fn rustc<'a, 'cfg>(
277
314
& target,
278
315
mode,
279
316
& 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
+ } ,
281
320
)
282
321
. map_err ( internal_if_simple_exit_code)
283
322
. chain_err ( || format ! ( "Could not compile `{}`." , name) ) ?;
@@ -659,7 +698,7 @@ fn rustdoc<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult
659
698
rustdoc
660
699
. exec_with_streaming (
661
700
& 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 ) ,
663
702
false ,
664
703
)
665
704
. chain_err ( || format ! ( "Could not document `{}`." , name) ) ?;
@@ -1054,22 +1093,49 @@ fn on_stderr_line(
1054
1093
line : & str ,
1055
1094
package_id : PackageId ,
1056
1095
target : & Target ,
1096
+ extract_rendered_errors : bool ,
1057
1097
) -> 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
+
1058
1136
// Switch json lines from rustc/rustdoc that appear on stderr to instead.
1059
1137
// We want the stdout of Cargo to always be machine parseable as stderr has
1060
1138
// 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) ;
1074
1140
Ok ( ( ) )
1075
1141
}
0 commit comments