Skip to content

Commit 03122e1

Browse files
committed
Auto merge of #60006 - nnethercote:json-for-pipelining, r=alexcrichton
In JSON output, emit a directive after metadata is generated. To implement pipelining, Cargo needs to know when metadata generation is finished. This is done via a new JSON "directive". 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. r? @alexcrichton
2 parents 00859e3 + 7bcb0cf commit 03122e1

File tree

11 files changed

+101
-53
lines changed

11 files changed

+101
-53
lines changed

src/librustc/session/config.rs

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

14701472
pub fn default_lib_output() -> CrateType {

src/librustc/session/mod.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -1078,8 +1078,7 @@ fn default_emitter(
10781078

10791079
pub enum DiagnosticOutput {
10801080
Default,
1081-
Raw(Box<dyn Write + Send>),
1082-
Emitter(Box<dyn Emitter + Send + sync::Send>)
1081+
Raw(Box<dyn Write + Send>)
10831082
}
10841083

10851084
pub fn build_session_with_source_map(
@@ -1115,7 +1114,6 @@ pub fn build_session_with_source_map(
11151114
DiagnosticOutput::Raw(write) => {
11161115
default_emitter(&sopts, registry, &source_map, Some(write))
11171116
}
1118-
DiagnosticOutput::Emitter(emitter) => emitter,
11191117
};
11201118

11211119
let diagnostic_handler = errors::Handler::with_emitter_and_flags(

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

+23-27
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,10 @@ pub fn remove(sess: &Session, path: &Path) {
4646
/// Performs the linkage portion of the compilation phase. This will generate all
4747
/// of the requested outputs for this compilation session.
4848
pub fn link_binary<'a, B: ArchiveBuilder<'a>>(sess: &'a Session,
49-
codegen_results: &CodegenResults,
50-
outputs: &OutputFilenames,
51-
crate_name: &str,
52-
target_cpu: &str) -> Vec<PathBuf> {
53-
let mut out_filenames = Vec::new();
49+
codegen_results: &CodegenResults,
50+
outputs: &OutputFilenames,
51+
crate_name: &str,
52+
target_cpu: &str) {
5453
for &crate_type in sess.crate_types.borrow().iter() {
5554
// Ignore executable crates if we have -Z no-codegen, as they will error.
5655
let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata);
@@ -64,13 +63,12 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(sess: &'a Session,
6463
bug!("invalid output type `{:?}` for target os `{}`",
6564
crate_type, sess.opts.target_triple);
6665
}
67-
let out_files = link_binary_output::<B>(sess,
68-
codegen_results,
69-
crate_type,
70-
outputs,
71-
crate_name,
72-
target_cpu);
73-
out_filenames.extend(out_files);
66+
link_binary_output::<B>(sess,
67+
codegen_results,
68+
crate_type,
69+
outputs,
70+
crate_name,
71+
target_cpu);
7472
}
7573

7674
// Remove the temporary object file and metadata if we aren't saving temps
@@ -97,22 +95,18 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(sess: &'a Session,
9795
}
9896
}
9997
}
100-
101-
out_filenames
10298
}
10399

104100
fn link_binary_output<'a, B: ArchiveBuilder<'a>>(sess: &'a Session,
105-
codegen_results: &CodegenResults,
106-
crate_type: config::CrateType,
107-
outputs: &OutputFilenames,
108-
crate_name: &str,
109-
target_cpu: &str) -> Vec<PathBuf> {
101+
codegen_results: &CodegenResults,
102+
crate_type: config::CrateType,
103+
outputs: &OutputFilenames,
104+
crate_name: &str,
105+
target_cpu: &str) {
110106
for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) {
111107
check_file_is_writeable(obj, sess);
112108
}
113109

114-
let mut out_filenames = vec![];
115-
116110
if outputs.outputs.contains_key(&OutputType::Metadata) {
117111
let out_filename = filename_for_metadata(sess, crate_name, outputs);
118112
// To avoid races with another rustc process scanning the output directory,
@@ -125,10 +119,15 @@ fn link_binary_output<'a, B: ArchiveBuilder<'a>>(sess: &'a Session,
125119
.tempdir_in(out_filename.parent().unwrap())
126120
.unwrap_or_else(|err| sess.fatal(&format!("couldn't create a temp dir: {}", err)));
127121
let metadata = emit_metadata(sess, codegen_results, &metadata_tmpdir);
128-
if let Err(e) = fs::rename(metadata, &out_filename) {
129-
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)),
130130
}
131-
out_filenames.push(out_filename);
132131
}
133132

134133
let tmpdir = TempFileBuilder::new().prefix("rustc").tempdir().unwrap_or_else(|err|
@@ -158,14 +157,11 @@ fn link_binary_output<'a, B: ArchiveBuilder<'a>>(sess: &'a Session,
158157
);
159158
}
160159
}
161-
out_filenames.push(out_filename);
162160
}
163161

164162
if sess.opts.cg.save_temps {
165163
let _ = tmpdir.into_path();
166164
}
167-
168-
out_filenames
169165
}
170166

171167
// The third parameter is for env vars, used on windows to set up the

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

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// ignore-tidy-linelength
2+
// compile-flags:--emit=metadata --error-format=json -Z emit-directives
3+
// compile-pass
4+
//
5+
// Normalization is required to eliminated minor path and filename differences
6+
// across platforms.
7+
// normalize-stderr-test: "metadata file written: .*/emit-directives" -> "metadata file written: .../emit-directives"
8+
// normalize-stderr-test: "emit-directives(\.\w*)?/a(\.\w*)?" -> "emit-directives/a"
9+
10+
// A very basic test for the emission of build directives in JSON output.
11+
12+
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: .../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)