Skip to content

Commit

Permalink
In JSON output, emit a directive after metadata is generated.
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
nnethercote committed Apr 26, 2019
1 parent 91d5b76 commit 8515fb6
Show file tree
Hide file tree
Showing 10 changed files with 79 additions and 25 deletions.
2 changes: 2 additions & 0 deletions src/librustc/session/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1468,6 +1468,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
the same values as the target option of the same name"),
allow_features: Option<Vec<String>> = (None, parse_opt_comma_list, [TRACKED],
"only allow the listed language features to be enabled in code (space separated)"),
emit_directives: bool = (false, parse_bool, [UNTRACKED],
"emit build directives if producing JSON output"),
}

pub fn default_lib_output() -> CrateType {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_codegen_llvm/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ impl CodegenBackend for LlvmCodegenBackend {
sess: &Session,
dep_graph: &DepGraph,
outputs: &OutputFilenames,
) -> Result<(), ErrorReported>{
) -> Result<(), ErrorReported> {
use rustc::util::common::time;
let (codegen_results, work_products) =
ongoing_codegen.downcast::
Expand Down
10 changes: 8 additions & 2 deletions src/librustc_codegen_ssa/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,14 @@ fn link_binary_output<'a, B: ArchiveBuilder<'a>>(sess: &'a Session,
.tempdir_in(out_filename.parent().unwrap())
.unwrap_or_else(|err| sess.fatal(&format!("couldn't create a temp dir: {}", err)));
let metadata = emit_metadata(sess, codegen_results, &metadata_tmpdir);
if let Err(e) = fs::rename(metadata, &out_filename) {
sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e));
match fs::rename(&metadata, &out_filename) {
Ok(_) => {
if sess.opts.debugging_opts.emit_directives {
sess.parse_sess.span_diagnostic.maybe_emit_json_directive(
format!("metadata file written: {}", out_filename.display()));
}
}
Err(e) => sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)),
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/librustc_codegen_ssa/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1726,7 +1726,7 @@ impl SharedEmitter {
}

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

// These are generally cheap and won't through off scheduling.
// These are generally cheap and won't throw off scheduling.
let cost = 0;
submit_codegened_module_to_llvm(&self.backend, tcx, module, cost);
}
Expand Down
8 changes: 6 additions & 2 deletions src/librustc_errors/emitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ const ANONYMIZED_LINE_NUM: &str = "LL";
/// Emitter trait for emitting errors.
pub trait Emitter {
/// Emit a structured diagnostic.
fn emit(&mut self, db: &DiagnosticBuilder<'_>);
fn emit_diagnostic(&mut self, db: &DiagnosticBuilder<'_>);

/// Emit a JSON directive. The default is to do nothing; this should only
/// be emitted with --error-format=json.
fn maybe_emit_json_directive(&mut self, _directive: String) {}

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

impl Emitter for EmitterWriter {
fn emit(&mut self, db: &DiagnosticBuilder<'_>) {
fn emit_diagnostic(&mut self, db: &DiagnosticBuilder<'_>) {
let mut primary_span = db.span.clone();
let mut children = db.children.clone();
let mut suggestions: &[_] = &[];
Expand Down
22 changes: 16 additions & 6 deletions src/librustc_errors/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,9 +294,16 @@ impl error::Error for ExplicitBug {
pub use diagnostic::{Diagnostic, SubDiagnostic, DiagnosticStyledString, DiagnosticId};
pub use diagnostic_builder::DiagnosticBuilder;

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

Expand Down Expand Up @@ -736,7 +743,7 @@ impl Handler {
}

pub fn force_print_db(&self, mut db: DiagnosticBuilder<'_>) {
self.emitter.borrow_mut().emit(&db);
self.emitter.borrow_mut().emit_diagnostic(&db);
db.cancel();
}

Expand All @@ -761,14 +768,17 @@ impl Handler {
// Only emit the diagnostic if we haven't already emitted an equivalent
// one:
if self.emitted_diagnostics.borrow_mut().insert(diagnostic_hash) {
self.emitter.borrow_mut().emit(db);
self.emitter.borrow_mut().emit_diagnostic(db);
if db.is_error() {
self.bump_err_count();
}
}
}
}

pub fn maybe_emit_json_directive(&self, directive: String) {
self.emitter.borrow_mut().maybe_emit_json_directive(directive);
}
}

#[derive(Copy, PartialEq, Clone, Hash, Debug, RustcEncodable, RustcDecodable)]
pub enum Level {
Expand Down
22 changes: 20 additions & 2 deletions src/libsyntax/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ impl JsonEmitter {
}

impl Emitter for JsonEmitter {
fn emit(&mut self, db: &DiagnosticBuilder<'_>) {
fn emit_diagnostic(&mut self, db: &DiagnosticBuilder<'_>) {
let data = Diagnostic::from_diagnostic_builder(db, self);
let result = if self.pretty {
writeln!(&mut self.dst, "{}", as_pretty_json(&data))
Expand All @@ -90,6 +90,18 @@ impl Emitter for JsonEmitter {
panic!("failed to print diagnostics: {:?}", e);
}
}

fn maybe_emit_json_directive(&mut self, directive: String) {
let data = Directive { directive };
let result = if self.pretty {
writeln!(&mut self.dst, "{}", as_pretty_json(&data))
} else {
writeln!(&mut self.dst, "{}", as_json(&data))
};
if let Err(e) = result {
panic!("failed to print message: {:?}", e);
}
}
}

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

#[derive(RustcEncodable)]
struct Directive {
/// The directive itself.
directive: String,
}

impl Diagnostic {
fn from_diagnostic_builder(db: &DiagnosticBuilder<'_>,
je: &JsonEmitter)
Expand Down Expand Up @@ -200,7 +218,7 @@ impl Diagnostic {
let buf = BufWriter::default();
let output = buf.clone();
je.json_rendered.new_emitter(Box::new(buf), Some(je.sm.clone()), false)
.ui_testing(je.ui_testing).emit(db);
.ui_testing(je.ui_testing).emit_diagnostic(db);
let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap();
let output = String::from_utf8(output).unwrap();

Expand Down
6 changes: 6 additions & 0 deletions src/test/ui/emit-directives.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// A very basic test for the emission of build directives in JSON output.

// compile-flags:--emit=metadata --error-format=json -Z emit-directives
// compile-pass

fn main() {}
1 change: 1 addition & 0 deletions src/test/ui/emit-directives.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"directive":"metadata file written: $TEST_BUILD_DIR/emit-directives/a"}
27 changes: 17 additions & 10 deletions src/tools/compiletest/src/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ struct Diagnostic {
rendered: Option<String>,
}

#[derive(Deserialize)]
struct Directive {
#[allow(dead_code)]
directive: String,
}

#[derive(Deserialize, Clone)]
struct DiagnosticSpan {
file_name: String,
Expand Down Expand Up @@ -67,16 +73,17 @@ pub fn extract_rendered(output: &str) -> String {
.lines()
.filter_map(|line| {
if line.starts_with('{') {
match serde_json::from_str::<Diagnostic>(line) {
Ok(diagnostic) => diagnostic.rendered,
Err(error) => {
print!(
"failed to decode compiler output as json: \
`{}`\nline: {}\noutput: {}",
error, line, output
);
panic!()
}
if let Ok(diagnostic) = serde_json::from_str::<Diagnostic>(line) {
diagnostic.rendered
} else if let Ok(_directive) = serde_json::from_str::<Directive>(line) {
// Swallow the directive.
None
} else {
print!(
"failed to decode compiler output as json: line: {}\noutput: {}",
line, output
);
panic!()
}
} else {
// preserve non-JSON lines, such as ICEs
Expand Down

0 comments on commit 8515fb6

Please sign in to comment.