]> git.lizzy.rs Git - rust.git/commitdiff
In JSON output, emit a directive after metadata is generated.
authorNicholas Nethercote <nnethercote@mozilla.com>
Sun, 14 Apr 2019 22:26:08 +0000 (08:26 +1000)
committerNicholas Nethercote <nnethercote@mozilla.com>
Mon, 29 Apr 2019 22:51:57 +0000 (08:51 +1000)
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`.

src/librustc/session/config.rs
src/librustc_codegen_llvm/lib.rs
src/librustc_codegen_ssa/back/link.rs
src/librustc_codegen_ssa/back/write.rs
src/librustc_errors/emitter.rs
src/librustc_errors/lib.rs
src/libsyntax/json.rs
src/test/ui/emit-directives.rs [new file with mode: 0644]
src/test/ui/emit-directives.stderr [new file with mode: 0644]
src/tools/compiletest/src/json.rs

index cb307800fcdc26b8f0a54d4941c2942679d436e4..4fef6cd3dd290ef6af08024ff6a3607bf8f7dc03 100644 (file)
@@ -1468,6 +1468,8 @@ fn parse_merge_functions(slot: &mut Option<MergeFunctions>, v: Option<&str>) ->
          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 {
index cee0d5be6473b1455a6ca780bc03710e3b914bd7..08424e7c3229acd10261b40cc6053a10e7edcdf4 100644 (file)
@@ -300,7 +300,7 @@ fn join_codegen_and_link(
         sess: &Session,
         dep_graph: &DepGraph,
         outputs: &OutputFilenames,
-    ) -> Result<(), ErrorReported>{
+    ) -> Result<(), ErrorReported> {
         use rustc::util::common::time;
         let (codegen_results, work_products) =
             ongoing_codegen.downcast::
index b5e41dd22c9f0cedf19b61745b7e7ed6985e9fb1..4cae20b698a1ca366da63a33eacb05fbda583952 100644 (file)
@@ -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)),
         }
     }
 
index 88a5e5a1aec3b4ccf6a256506fe9cfdc7e51177e..576bcc8f38e656274d2da9d6e7822cf7d0e753c6 100644 (file)
@@ -1726,7 +1726,7 @@ pub fn fatal(&self, msg: &str) {
 }
 
 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(),
@@ -1865,7 +1865,7 @@ pub fn submit_pre_codegened_module_to_llvm(&self,
         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);
     }
index c3d594204f413de24eeeb0ab860a88c37a52170a..bfc9113c2d41ed8d56e6c5bfc44da512a9a4c8e8 100644 (file)
@@ -50,7 +50,11 @@ pub fn new_emitter(
 /// 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 {
@@ -59,7 +63,7 @@ fn should_show_explain(&self) -> bool {
 }
 
 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: &[_] = &[];
index 5d3861d9572caffed03a77b71c0a2f3ac7cecfd5..e173e1060cc1097988a89a93ed98593078c565fd 100644 (file)
@@ -294,9 +294,16 @@ fn description(&self) -> &str {
 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,
 
@@ -736,7 +743,7 @@ pub fn must_teach(&self, code: &DiagnosticId) -> bool {
     }
 
     pub fn force_print_db(&self, mut db: DiagnosticBuilder<'_>) {
-        self.emitter.borrow_mut().emit(&db);
+        self.emitter.borrow_mut().emit_diagnostic(&db);
         db.cancel();
     }
 
@@ -761,14 +768,17 @@ fn emit_db(&self, db: &DiagnosticBuilder<'_>) {
         // 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 {
index c19b408442ad12b16b224dbd3ab4f36201c0d6f9..65f8d0e77d7be430318685fb353fa1944244f521 100644 (file)
@@ -79,7 +79,7 @@ pub fn ui_testing(self, ui_testing: bool) -> Self {
 }
 
 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))
@@ -90,6 +90,18 @@ fn emit(&mut self, db: &DiagnosticBuilder<'_>) {
             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.
@@ -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)
@@ -200,7 +218,7 @@ fn flush(&mut self) -> io::Result<()> {
         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();
 
diff --git a/src/test/ui/emit-directives.rs b/src/test/ui/emit-directives.rs
new file mode 100644 (file)
index 0000000..088280e
--- /dev/null
@@ -0,0 +1,12 @@
+// ignore-tidy-linelength
+// compile-flags:--emit=metadata --error-format=json -Z emit-directives
+// compile-pass
+//
+// Normalization is required to eliminated minor path and filename differences
+// across platforms.
+// normalize-stderr-test: "metadata file written: .*/emit-directives" -> "metadata file written: .../emit-directives"
+// normalize-stderr-test: "emit-directives(\.\w*)?/a(\.\w*)?" -> "emit-directives/a"
+
+// A very basic test for the emission of build directives in JSON output.
+
+fn main() {}
diff --git a/src/test/ui/emit-directives.stderr b/src/test/ui/emit-directives.stderr
new file mode 100644 (file)
index 0000000..b8a4b96
--- /dev/null
@@ -0,0 +1 @@
+{"directive":"metadata file written: .../emit-directives/a"}
index a7615f5f423a3d913219b29c5179298f77ca6d89..26a3c4dee40aa478f3ec112f56187d167b59c34b 100644 (file)
@@ -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,
@@ -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