blob: ca796806cc6106c90d690c791f3d37fc6863ba15 [file] [log] [blame]
// Copyright 2020 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::convert::{TryFrom, TryInto};
use tinyjson::JsonValue;
use crate::output::LineOutput;
#[derive(Debug, Copy, Clone)]
pub(crate) enum ErrorFormat {
Json,
Rendered,
}
impl Default for ErrorFormat {
fn default() -> Self {
Self::Rendered
}
}
fn get_key(value: &JsonValue, key: &str) -> Option<String> {
if let JsonValue::Object(map) = value {
if let JsonValue::String(s) = map.get(key)? {
Some(s.clone())
} else {
None
}
} else {
None
}
}
#[derive(Debug)]
enum RustcMessage {
Emit(String),
Message(String),
}
impl TryFrom<JsonValue> for RustcMessage {
type Error = ();
fn try_from(val: JsonValue) -> Result<Self, Self::Error> {
if let Some(emit) = get_key(&val, "emit") {
return Ok(Self::Emit(emit));
}
if let Some(rendered) = get_key(&val, "rendered") {
return Ok(Self::Message(rendered));
}
Err(())
}
}
/// process_rustc_json takes an output line from rustc configured with
/// --error-format=json, parses the json and returns the appropriate output
/// according to the original --error-format supplied.
/// Only messages are returned, emits are ignored.
pub(crate) fn process_json(line: String, error_format: ErrorFormat) -> LineOutput {
let parsed: JsonValue = line
.parse()
.expect("process wrapper error: expected json messages in pipeline mode");
match parsed.try_into() {
Ok(RustcMessage::Message(msg)) => match error_format {
// If the output should be json, we just forward the messages as-is
// using `line`.
ErrorFormat::Json => LineOutput::Message(line),
// Otherwise we return the rendered field.
_ => LineOutput::Message(msg),
},
_ => LineOutput::Skip,
}
}
/// stop_on_rmeta_completion parses the json output of rustc in the same way process_rustc_json does.
/// In addition, it will signal to stop when metadata is emitted
/// so the compiler can be terminated.
/// This is used to implement pipelining in rules_rust, please see
/// https://internals.rust-lang.org/t/evaluating-pipelined-rustc-compilation/10199
pub(crate) fn stop_on_rmeta_completion(
line: String,
error_format: ErrorFormat,
kill: &mut bool,
) -> LineOutput {
let parsed: JsonValue = line
.parse()
.expect("process wrapper error: expected json messages in pipeline mode");
match parsed.try_into() {
Ok(RustcMessage::Emit(emit)) if emit == "metadata" => {
*kill = true;
LineOutput::Terminate
}
Ok(RustcMessage::Message(msg)) => match error_format {
// If the output should be json, we just forward the messages as-is
// using `line`.
ErrorFormat::Json => LineOutput::Message(line),
// Otherwise we return the rendered field.
_ => LineOutput::Message(msg),
},
_ => LineOutput::Skip,
}
}