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(©_source, ©_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