xref: /aosp_15_r20/external/bazelbuild-rules_rust/util/process_wrapper/rustc.rs (revision d4726bddaa87cc4778e7472feed243fa4b6c267f)
1 // 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 
15 use std::convert::{TryFrom, TryInto};
16 
17 use tinyjson::JsonValue;
18 
19 use crate::output::{LineOutput, LineResult};
20 
21 #[derive(Debug, Copy, Clone)]
22 pub(crate) enum ErrorFormat {
23     Json,
24     Rendered,
25 }
26 
27 impl Default for ErrorFormat {
default() -> Self28     fn default() -> Self {
29         Self::Rendered
30     }
31 }
32 
get_key(value: &JsonValue, key: &str) -> Option<String>33 fn 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)]
46 enum RustcMessage {
47     Emit(String),
48     Message(String),
49 }
50 
51 impl TryFrom<JsonValue> for RustcMessage {
52     type Error = ();
try_from(val: JsonValue) -> Result<Self, Self::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.
68 /// Retuns an errors if parsing json fails.
process_json(line: String, error_format: ErrorFormat) -> LineResult69 pub(crate) fn process_json(line: String, error_format: ErrorFormat) -> LineResult {
70     let parsed: JsonValue = line
71         .parse()
72         .map_err(|_| "error parsing rustc output as json".to_owned())?;
73     Ok(match parsed.try_into() {
74         Ok(RustcMessage::Message(rendered)) => {
75             output_based_on_error_format(line, rendered, error_format)
76         }
77         _ => LineOutput::Skip,
78     })
79 }
80 
81 /// stop_on_rmeta_completion parses the json output of rustc in the same way
82 /// process_rustc_json does. In addition, it will signal to stop when metadata
83 /// is emitted so the compiler can be terminated.
84 /// This is used to implement pipelining in rules_rust, please see
85 /// https://internals.rust-lang.org/t/evaluating-pipelined-rustc-compilation/10199
86 /// Retuns an error if parsing json fails.
87 /// 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 pub(crate) fn stop_on_rmeta_completion(
89     line: String,
90     error_format: ErrorFormat,
91     kill: &mut bool,
92 ) -> LineResult {
93     let parsed: JsonValue = line
94         .parse()
95         .map_err(|_| "error parsing rustc output as json".to_owned())?;
96     Ok(match parsed.try_into() {
97         Ok(RustcMessage::Emit(emit)) if emit == "metadata" => {
98             *kill = true;
99             LineOutput::Terminate
100         }
101         Ok(RustcMessage::Message(rendered)) => {
102             output_based_on_error_format(line, rendered, error_format)
103         }
104         _ => LineOutput::Skip,
105     })
106 }
107 
output_based_on_error_format( line: String, rendered: String, error_format: ErrorFormat, ) -> LineOutput108 fn output_based_on_error_format(
109     line: String,
110     rendered: String,
111     error_format: ErrorFormat,
112 ) -> LineOutput {
113     match error_format {
114         // If the output should be json, we just forward the messages as-is
115         // using `line`.
116         ErrorFormat::Json => LineOutput::Message(line),
117         // Otherwise we return the rendered field.
118         ErrorFormat::Rendered => LineOutput::Message(rendered),
119     }
120 }
121