blob: ca796806cc6106c90d690c791f3d37fc6863ba15 [file] [log] [blame]
Brian Silverman5f6f2762022-08-13 19:30:05 -07001// Copyright 2020 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::convert::{TryFrom, TryInto};
16
17use tinyjson::JsonValue;
18
19use crate::output::LineOutput;
20
21#[derive(Debug, Copy, Clone)]
22pub(crate) enum ErrorFormat {
23 Json,
24 Rendered,
25}
26
27impl Default for ErrorFormat {
28 fn default() -> Self {
29 Self::Rendered
30 }
31}
32
33fn get_key(value: &JsonValue, key: &str) -> Option<String> {
34 if let JsonValue::Object(map) = value {
35 if let JsonValue::String(s) = map.get(key)? {
36 Some(s.clone())
37 } else {
38 None
39 }
40 } else {
41 None
42 }
43}
44
45#[derive(Debug)]
46enum RustcMessage {
47 Emit(String),
48 Message(String),
49}
50
51impl TryFrom<JsonValue> for RustcMessage {
52 type Error = ();
53 fn try_from(val: JsonValue) -> Result<Self, Self::Error> {
54 if let Some(emit) = get_key(&val, "emit") {
55 return Ok(Self::Emit(emit));
56 }
57 if let Some(rendered) = get_key(&val, "rendered") {
58 return Ok(Self::Message(rendered));
59 }
60 Err(())
61 }
62}
63
64/// process_rustc_json takes an output line from rustc configured with
65/// --error-format=json, parses the json and returns the appropriate output
66/// according to the original --error-format supplied.
67/// Only messages are returned, emits are ignored.
68pub(crate) fn process_json(line: String, error_format: ErrorFormat) -> LineOutput {
69 let parsed: JsonValue = line
70 .parse()
71 .expect("process wrapper error: expected json messages in pipeline mode");
72 match parsed.try_into() {
73 Ok(RustcMessage::Message(msg)) => match error_format {
74 // If the output should be json, we just forward the messages as-is
75 // using `line`.
76 ErrorFormat::Json => LineOutput::Message(line),
77 // Otherwise we return the rendered field.
78 _ => LineOutput::Message(msg),
79 },
80 _ => LineOutput::Skip,
81 }
82}
83
84/// stop_on_rmeta_completion parses the json output of rustc in the same way process_rustc_json does.
85/// In addition, it will signal to stop when metadata is emitted
86/// so the compiler can be terminated.
87/// This is used to implement pipelining in rules_rust, please see
88/// https://internals.rust-lang.org/t/evaluating-pipelined-rustc-compilation/10199
89pub(crate) fn stop_on_rmeta_completion(
90 line: String,
91 error_format: ErrorFormat,
92 kill: &mut bool,
93) -> LineOutput {
94 let parsed: JsonValue = line
95 .parse()
96 .expect("process wrapper error: expected json messages in pipeline mode");
97
98 match parsed.try_into() {
99 Ok(RustcMessage::Emit(emit)) if emit == "metadata" => {
100 *kill = true;
101 LineOutput::Terminate
102 }
103 Ok(RustcMessage::Message(msg)) => match error_format {
104 // If the output should be json, we just forward the messages as-is
105 // using `line`.
106 ErrorFormat::Json => LineOutput::Message(line),
107 // Otherwise we return the rendered field.
108 _ => LineOutput::Message(msg),
109 },
110 _ => LineOutput::Skip,
111 }
112}