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 {
sess: &Session,
dep_graph: &DepGraph,
outputs: &OutputFilenames,
- ) -> Result<(), ErrorReported>{
+ ) -> Result<(), ErrorReported> {
use rustc::util::common::time;
let (codegen_results, work_products) =
ongoing_codegen.downcast::
.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)),
}
}
}
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(),
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);
}
/// 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 {
}
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: &[_] = &[];
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,
}
pub fn force_print_db(&self, mut db: DiagnosticBuilder<'_>) {
- self.emitter.borrow_mut().emit(&db);
+ self.emitter.borrow_mut().emit_diagnostic(&db);
db.cancel();
}
// 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 {
}
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))
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.
explanation: Option<&'static str>,
}
+#[derive(RustcEncodable)]
+struct Directive {
+ /// The directive itself.
+ directive: String,
+}
+
impl Diagnostic {
fn from_diagnostic_builder(db: &DiagnosticBuilder<'_>,
je: &JsonEmitter)
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();
--- /dev/null
+// 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() {}
--- /dev/null
+{"directive":"metadata file written: .../emit-directives/a"}
rendered: Option<String>,
}
+#[derive(Deserialize)]
+struct Directive {
+ #[allow(dead_code)]
+ directive: String,
+}
+
#[derive(Deserialize, Clone)]
struct DiagnosticSpan {
file_name: 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