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