Skip to content

Commit 8515fb6

Browse files
committed
In JSON output, emit a directive after metadata is generated.
To implement pipelining, Cargo needs to know when metadata generation is finished. This commit adds code to do that. Unfortunately, metadata file writing currently occurs very late during compilation, so pipelining won't produce a speed-up. Moving metadata file writing earlier will be a follow-up. The change involves splitting the existing `Emitter::emit` method in two: `Emitter::emit_diagnostic` and `Emitter::emit_directive`. The JSON directives look like this: ``` {"directive":"metadata file written: liba.rmeta"} ``` The functionality is behind the `-Z emit-directives` option, and also requires `--error-format=json`.
1 parent 91d5b76 commit 8515fb6

File tree

10 files changed

+79
-25
lines changed

10 files changed

+79
-25
lines changed

src/librustc/session/config.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1468,6 +1468,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
14681468
the same values as the target option of the same name"),
14691469
allow_features: Option<Vec<String>> = (None, parse_opt_comma_list, [TRACKED],
14701470
"only allow the listed language features to be enabled in code (space separated)"),
1471+
emit_directives: bool = (false, parse_bool, [UNTRACKED],
1472+
"emit build directives if producing JSON output"),
14711473
}
14721474

14731475
pub fn default_lib_output() -> CrateType {

src/librustc_codegen_llvm/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ impl CodegenBackend for LlvmCodegenBackend {
300300
sess: &Session,
301301
dep_graph: &DepGraph,
302302
outputs: &OutputFilenames,
303-
) -> Result<(), ErrorReported>{
303+
) -> Result<(), ErrorReported> {
304304
use rustc::util::common::time;
305305
let (codegen_results, work_products) =
306306
ongoing_codegen.downcast::

src/librustc_codegen_ssa/back/link.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,14 @@ fn link_binary_output<'a, B: ArchiveBuilder<'a>>(sess: &'a Session,
119119
.tempdir_in(out_filename.parent().unwrap())
120120
.unwrap_or_else(|err| sess.fatal(&format!("couldn't create a temp dir: {}", err)));
121121
let metadata = emit_metadata(sess, codegen_results, &metadata_tmpdir);
122-
if let Err(e) = fs::rename(metadata, &out_filename) {
123-
sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e));
122+
match fs::rename(&metadata, &out_filename) {
123+
Ok(_) => {
124+
if sess.opts.debugging_opts.emit_directives {
125+
sess.parse_sess.span_diagnostic.maybe_emit_json_directive(
126+
format!("metadata file written: {}", out_filename.display()));
127+
}
128+
}
129+
Err(e) => sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)),
124130
}
125131
}
126132

src/librustc_codegen_ssa/back/write.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1726,7 +1726,7 @@ impl SharedEmitter {
17261726
}
17271727

17281728
impl Emitter for SharedEmitter {
1729-
fn emit(&mut self, db: &DiagnosticBuilder<'_>) {
1729+
fn emit_diagnostic(&mut self, db: &DiagnosticBuilder<'_>) {
17301730
drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
17311731
msg: db.message(),
17321732
code: db.code.clone(),
@@ -1865,7 +1865,7 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
18651865
self.wait_for_signal_to_codegen_item();
18661866
self.check_for_errors(tcx.sess);
18671867

1868-
// These are generally cheap and won't through off scheduling.
1868+
// These are generally cheap and won't throw off scheduling.
18691869
let cost = 0;
18701870
submit_codegened_module_to_llvm(&self.backend, tcx, module, cost);
18711871
}

src/librustc_errors/emitter.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ const ANONYMIZED_LINE_NUM: &str = "LL";
5050
/// Emitter trait for emitting errors.
5151
pub trait Emitter {
5252
/// Emit a structured diagnostic.
53-
fn emit(&mut self, db: &DiagnosticBuilder<'_>);
53+
fn emit_diagnostic(&mut self, db: &DiagnosticBuilder<'_>);
54+
55+
/// Emit a JSON directive. The default is to do nothing; this should only
56+
/// be emitted with --error-format=json.
57+
fn maybe_emit_json_directive(&mut self, _directive: String) {}
5458

5559
/// Checks if should show explanations about "rustc --explain"
5660
fn should_show_explain(&self) -> bool {
@@ -59,7 +63,7 @@ pub trait Emitter {
5963
}
6064

6165
impl Emitter for EmitterWriter {
62-
fn emit(&mut self, db: &DiagnosticBuilder<'_>) {
66+
fn emit_diagnostic(&mut self, db: &DiagnosticBuilder<'_>) {
6367
let mut primary_span = db.span.clone();
6468
let mut children = db.children.clone();
6569
let mut suggestions: &[_] = &[];

src/librustc_errors/lib.rs

+16-6
Original file line numberDiff line numberDiff line change
@@ -294,9 +294,16 @@ impl error::Error for ExplicitBug {
294294
pub use diagnostic::{Diagnostic, SubDiagnostic, DiagnosticStyledString, DiagnosticId};
295295
pub use diagnostic_builder::DiagnosticBuilder;
296296

297-
/// A handler deals with errors; certain errors
298-
/// (fatal, bug, unimpl) may cause immediate exit,
299-
/// others log errors for later reporting.
297+
/// A handler deals with two kinds of compiler output.
298+
/// - Errors: certain errors (fatal, bug, unimpl) may cause immediate exit,
299+
/// others log errors for later reporting.
300+
/// - Directives: with --error-format=json, the compiler produces directives
301+
/// that indicate when certain actions have completed, which are useful for
302+
/// Cargo. They may change at any time and should not be considered a public
303+
/// API.
304+
///
305+
/// This crate's name (rustc_errors) doesn't encompass the directives, because
306+
/// directives were added much later.
300307
pub struct Handler {
301308
pub flags: HandlerFlags,
302309

@@ -736,7 +743,7 @@ impl Handler {
736743
}
737744

738745
pub fn force_print_db(&self, mut db: DiagnosticBuilder<'_>) {
739-
self.emitter.borrow_mut().emit(&db);
746+
self.emitter.borrow_mut().emit_diagnostic(&db);
740747
db.cancel();
741748
}
742749

@@ -761,14 +768,17 @@ impl Handler {
761768
// Only emit the diagnostic if we haven't already emitted an equivalent
762769
// one:
763770
if self.emitted_diagnostics.borrow_mut().insert(diagnostic_hash) {
764-
self.emitter.borrow_mut().emit(db);
771+
self.emitter.borrow_mut().emit_diagnostic(db);
765772
if db.is_error() {
766773
self.bump_err_count();
767774
}
768775
}
769776
}
770-
}
771777

778+
pub fn maybe_emit_json_directive(&self, directive: String) {
779+
self.emitter.borrow_mut().maybe_emit_json_directive(directive);
780+
}
781+
}
772782

773783
#[derive(Copy, PartialEq, Clone, Hash, Debug, RustcEncodable, RustcDecodable)]
774784
pub enum Level {

src/libsyntax/json.rs

+20-2
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ impl JsonEmitter {
7979
}
8080

8181
impl Emitter for JsonEmitter {
82-
fn emit(&mut self, db: &DiagnosticBuilder<'_>) {
82+
fn emit_diagnostic(&mut self, db: &DiagnosticBuilder<'_>) {
8383
let data = Diagnostic::from_diagnostic_builder(db, self);
8484
let result = if self.pretty {
8585
writeln!(&mut self.dst, "{}", as_pretty_json(&data))
@@ -90,6 +90,18 @@ impl Emitter for JsonEmitter {
9090
panic!("failed to print diagnostics: {:?}", e);
9191
}
9292
}
93+
94+
fn maybe_emit_json_directive(&mut self, directive: String) {
95+
let data = Directive { directive };
96+
let result = if self.pretty {
97+
writeln!(&mut self.dst, "{}", as_pretty_json(&data))
98+
} else {
99+
writeln!(&mut self.dst, "{}", as_json(&data))
100+
};
101+
if let Err(e) = result {
102+
panic!("failed to print message: {:?}", e);
103+
}
104+
}
93105
}
94106

95107
// The following data types are provided just for serialisation.
@@ -168,6 +180,12 @@ struct DiagnosticCode {
168180
explanation: Option<&'static str>,
169181
}
170182

183+
#[derive(RustcEncodable)]
184+
struct Directive {
185+
/// The directive itself.
186+
directive: String,
187+
}
188+
171189
impl Diagnostic {
172190
fn from_diagnostic_builder(db: &DiagnosticBuilder<'_>,
173191
je: &JsonEmitter)
@@ -200,7 +218,7 @@ impl Diagnostic {
200218
let buf = BufWriter::default();
201219
let output = buf.clone();
202220
je.json_rendered.new_emitter(Box::new(buf), Some(je.sm.clone()), false)
203-
.ui_testing(je.ui_testing).emit(db);
221+
.ui_testing(je.ui_testing).emit_diagnostic(db);
204222
let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap();
205223
let output = String::from_utf8(output).unwrap();
206224

src/test/ui/emit-directives.rs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// A very basic test for the emission of build directives in JSON output.
2+
3+
// compile-flags:--emit=metadata --error-format=json -Z emit-directives
4+
// compile-pass
5+
6+
fn main() {}

src/test/ui/emit-directives.stderr

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"directive":"metadata file written: $TEST_BUILD_DIR/emit-directives/a"}

src/tools/compiletest/src/json.rs

+17-10
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ struct Diagnostic {
1717
rendered: Option<String>,
1818
}
1919

20+
#[derive(Deserialize)]
21+
struct Directive {
22+
#[allow(dead_code)]
23+
directive: String,
24+
}
25+
2026
#[derive(Deserialize, Clone)]
2127
struct DiagnosticSpan {
2228
file_name: String,
@@ -67,16 +73,17 @@ pub fn extract_rendered(output: &str) -> String {
6773
.lines()
6874
.filter_map(|line| {
6975
if line.starts_with('{') {
70-
match serde_json::from_str::<Diagnostic>(line) {
71-
Ok(diagnostic) => diagnostic.rendered,
72-
Err(error) => {
73-
print!(
74-
"failed to decode compiler output as json: \
75-
`{}`\nline: {}\noutput: {}",
76-
error, line, output
77-
);
78-
panic!()
79-
}
76+
if let Ok(diagnostic) = serde_json::from_str::<Diagnostic>(line) {
77+
diagnostic.rendered
78+
} else if let Ok(_directive) = serde_json::from_str::<Directive>(line) {
79+
// Swallow the directive.
80+
None
81+
} else {
82+
print!(
83+
"failed to decode compiler output as json: line: {}\noutput: {}",
84+
line, output
85+
);
86+
panic!()
8087
}
8188
} else {
8289
// preserve non-JSON lines, such as ICEs

0 commit comments

Comments
 (0)