xref: /aosp_15_r20/external/bazelbuild-rules_rust/util/process_wrapper/main.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 mod flags;
16*d4726bddSHONG Yifan mod options;
17*d4726bddSHONG Yifan mod output;
18*d4726bddSHONG Yifan mod rustc;
19*d4726bddSHONG Yifan mod util;
20*d4726bddSHONG Yifan 
21*d4726bddSHONG Yifan use std::fmt;
22*d4726bddSHONG Yifan use std::fs::{copy, OpenOptions};
23*d4726bddSHONG Yifan use std::io;
24*d4726bddSHONG Yifan use std::process::{exit, Command, ExitStatus, Stdio};
25*d4726bddSHONG Yifan 
26*d4726bddSHONG Yifan use crate::options::options;
27*d4726bddSHONG Yifan use crate::output::{process_output, LineOutput};
28*d4726bddSHONG Yifan 
29*d4726bddSHONG Yifan #[cfg(windows)]
status_code(status: ExitStatus, was_killed: bool) -> i3230*d4726bddSHONG Yifan fn status_code(status: ExitStatus, was_killed: bool) -> i32 {
31*d4726bddSHONG Yifan     // On windows, there's no good way to know if the process was killed by a signal.
32*d4726bddSHONG Yifan     // If we killed the process, we override the code to signal success.
33*d4726bddSHONG Yifan     if was_killed {
34*d4726bddSHONG Yifan         0
35*d4726bddSHONG Yifan     } else {
36*d4726bddSHONG Yifan         status.code().unwrap_or(1)
37*d4726bddSHONG Yifan     }
38*d4726bddSHONG Yifan }
39*d4726bddSHONG Yifan 
40*d4726bddSHONG Yifan #[cfg(not(windows))]
status_code(status: ExitStatus, was_killed: bool) -> i3241*d4726bddSHONG Yifan fn status_code(status: ExitStatus, was_killed: bool) -> i32 {
42*d4726bddSHONG Yifan     // On unix, if code is None it means that the process was killed by a signal.
43*d4726bddSHONG Yifan     // https://doc.rust-lang.org/std/process/struct.ExitStatus.html#method.success
44*d4726bddSHONG Yifan     match status.code() {
45*d4726bddSHONG Yifan         Some(code) => code,
46*d4726bddSHONG Yifan         // If we killed the process, we expect None here
47*d4726bddSHONG Yifan         None if was_killed => 0,
48*d4726bddSHONG Yifan         // Otherwise it's some unexpected signal
49*d4726bddSHONG Yifan         None => 1,
50*d4726bddSHONG Yifan     }
51*d4726bddSHONG Yifan }
52*d4726bddSHONG Yifan 
53*d4726bddSHONG Yifan #[derive(Debug)]
54*d4726bddSHONG Yifan struct ProcessWrapperError(String);
55*d4726bddSHONG Yifan 
56*d4726bddSHONG Yifan impl fmt::Display for ProcessWrapperError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result57*d4726bddSHONG Yifan     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
58*d4726bddSHONG Yifan         write!(f, "process wrapper error: {}", self.0)
59*d4726bddSHONG Yifan     }
60*d4726bddSHONG Yifan }
61*d4726bddSHONG Yifan 
62*d4726bddSHONG Yifan impl std::error::Error for ProcessWrapperError {}
63*d4726bddSHONG Yifan 
main() -> Result<(), ProcessWrapperError>64*d4726bddSHONG Yifan fn main() -> Result<(), ProcessWrapperError> {
65*d4726bddSHONG Yifan     let opts = options().map_err(|e| ProcessWrapperError(e.to_string()))?;
66*d4726bddSHONG Yifan 
67*d4726bddSHONG Yifan     let mut child = Command::new(opts.executable)
68*d4726bddSHONG Yifan         .args(opts.child_arguments)
69*d4726bddSHONG Yifan         .env_clear()
70*d4726bddSHONG Yifan         .envs(opts.child_environment)
71*d4726bddSHONG Yifan         .stdout(if let Some(stdout_file) = opts.stdout_file {
72*d4726bddSHONG Yifan             OpenOptions::new()
73*d4726bddSHONG Yifan                 .create(true)
74*d4726bddSHONG Yifan                 .truncate(true)
75*d4726bddSHONG Yifan                 .write(true)
76*d4726bddSHONG Yifan                 .open(stdout_file)
77*d4726bddSHONG Yifan                 .map_err(|e| ProcessWrapperError(format!("unable to open stdout file: {}", e)))?
78*d4726bddSHONG Yifan                 .into()
79*d4726bddSHONG Yifan         } else {
80*d4726bddSHONG Yifan             Stdio::inherit()
81*d4726bddSHONG Yifan         })
82*d4726bddSHONG Yifan         .stderr(Stdio::piped())
83*d4726bddSHONG Yifan         .spawn()
84*d4726bddSHONG Yifan         .map_err(|e| ProcessWrapperError(format!("failed to spawn child process: {}", e)))?;
85*d4726bddSHONG Yifan 
86*d4726bddSHONG Yifan     let mut stderr: Box<dyn io::Write> = if let Some(stderr_file) = opts.stderr_file {
87*d4726bddSHONG Yifan         Box::new(
88*d4726bddSHONG Yifan             OpenOptions::new()
89*d4726bddSHONG Yifan                 .create(true)
90*d4726bddSHONG Yifan                 .truncate(true)
91*d4726bddSHONG Yifan                 .write(true)
92*d4726bddSHONG Yifan                 .open(stderr_file)
93*d4726bddSHONG Yifan                 .map_err(|e| ProcessWrapperError(format!("unable to open stderr file: {}", e)))?,
94*d4726bddSHONG Yifan         )
95*d4726bddSHONG Yifan     } else {
96*d4726bddSHONG Yifan         Box::new(io::stderr())
97*d4726bddSHONG Yifan     };
98*d4726bddSHONG Yifan 
99*d4726bddSHONG Yifan     let mut child_stderr = child.stderr.take().ok_or(ProcessWrapperError(
100*d4726bddSHONG Yifan         "unable to get child stderr".to_string(),
101*d4726bddSHONG Yifan     ))?;
102*d4726bddSHONG Yifan 
103*d4726bddSHONG Yifan     let mut output_file: Option<std::fs::File> = if let Some(output_file_name) = opts.output_file {
104*d4726bddSHONG Yifan         Some(
105*d4726bddSHONG Yifan             OpenOptions::new()
106*d4726bddSHONG Yifan                 .create(true)
107*d4726bddSHONG Yifan                 .truncate(true)
108*d4726bddSHONG Yifan                 .write(true)
109*d4726bddSHONG Yifan                 .open(output_file_name)
110*d4726bddSHONG Yifan                 .map_err(|e| ProcessWrapperError(format!("Unable to open output_file: {}", e)))?,
111*d4726bddSHONG Yifan         )
112*d4726bddSHONG Yifan     } else {
113*d4726bddSHONG Yifan         None
114*d4726bddSHONG Yifan     };
115*d4726bddSHONG Yifan 
116*d4726bddSHONG Yifan     let mut was_killed = false;
117*d4726bddSHONG Yifan     let result = if let Some(format) = opts.rustc_output_format {
118*d4726bddSHONG Yifan         let quit_on_rmeta = opts.rustc_quit_on_rmeta;
119*d4726bddSHONG Yifan         // Process json rustc output and kill the subprocess when we get a signal
120*d4726bddSHONG Yifan         // that we emitted a metadata file.
121*d4726bddSHONG Yifan         let mut me = false;
122*d4726bddSHONG Yifan         let metadata_emitted = &mut me;
123*d4726bddSHONG Yifan         let result = process_output(
124*d4726bddSHONG Yifan             &mut child_stderr,
125*d4726bddSHONG Yifan             stderr.as_mut(),
126*d4726bddSHONG Yifan             output_file.as_mut(),
127*d4726bddSHONG Yifan             move |line| {
128*d4726bddSHONG Yifan                 if quit_on_rmeta {
129*d4726bddSHONG Yifan                     rustc::stop_on_rmeta_completion(line, format, metadata_emitted)
130*d4726bddSHONG Yifan                 } else {
131*d4726bddSHONG Yifan                     rustc::process_json(line, format)
132*d4726bddSHONG Yifan                 }
133*d4726bddSHONG Yifan             },
134*d4726bddSHONG Yifan         );
135*d4726bddSHONG Yifan         if me {
136*d4726bddSHONG Yifan             // If recv returns Ok(), a signal was sent in this channel so we should terminate the child process.
137*d4726bddSHONG Yifan             // We can safely ignore the Result from kill() as we don't care if the process already terminated.
138*d4726bddSHONG Yifan             let _ = child.kill();
139*d4726bddSHONG Yifan             was_killed = true;
140*d4726bddSHONG Yifan         }
141*d4726bddSHONG Yifan         result
142*d4726bddSHONG Yifan     } else {
143*d4726bddSHONG Yifan         // Process output normally by forwarding stderr
144*d4726bddSHONG Yifan         process_output(
145*d4726bddSHONG Yifan             &mut child_stderr,
146*d4726bddSHONG Yifan             stderr.as_mut(),
147*d4726bddSHONG Yifan             output_file.as_mut(),
148*d4726bddSHONG Yifan             move |line| Ok(LineOutput::Message(line)),
149*d4726bddSHONG Yifan         )
150*d4726bddSHONG Yifan     };
151*d4726bddSHONG Yifan     result.map_err(|e| ProcessWrapperError(format!("failed to process stderr: {}", e)))?;
152*d4726bddSHONG Yifan 
153*d4726bddSHONG Yifan     let status = child
154*d4726bddSHONG Yifan         .wait()
155*d4726bddSHONG Yifan         .map_err(|e| ProcessWrapperError(format!("failed to wait for child process: {}", e)))?;
156*d4726bddSHONG Yifan     // If the child process is rustc and is killed after metadata generation, that's also a success.
157*d4726bddSHONG Yifan     let code = status_code(status, was_killed);
158*d4726bddSHONG Yifan     let success = code == 0;
159*d4726bddSHONG Yifan     if success {
160*d4726bddSHONG Yifan         if let Some(tf) = opts.touch_file {
161*d4726bddSHONG Yifan             OpenOptions::new()
162*d4726bddSHONG Yifan                 .create(true)
163*d4726bddSHONG Yifan                 .truncate(true)
164*d4726bddSHONG Yifan                 .write(true)
165*d4726bddSHONG Yifan                 .open(tf)
166*d4726bddSHONG Yifan                 .map_err(|e| ProcessWrapperError(format!("failed to create touch file: {}", e)))?;
167*d4726bddSHONG Yifan         }
168*d4726bddSHONG Yifan         if let Some((copy_source, copy_dest)) = opts.copy_output {
169*d4726bddSHONG Yifan             copy(&copy_source, &copy_dest).map_err(|e| {
170*d4726bddSHONG Yifan                 ProcessWrapperError(format!(
171*d4726bddSHONG Yifan                     "failed to copy {} into {}: {}",
172*d4726bddSHONG Yifan                     copy_source, copy_dest, e
173*d4726bddSHONG Yifan                 ))
174*d4726bddSHONG Yifan             })?;
175*d4726bddSHONG Yifan         }
176*d4726bddSHONG Yifan     }
177*d4726bddSHONG Yifan 
178*d4726bddSHONG Yifan     exit(code)
179*d4726bddSHONG Yifan }
180