blob: 6d985b34af7f8e8453742a33822e1477aefc5be3 [file] [log] [blame]
Brian Silvermancc09f182022-03-09 15:40:20 -08001// 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
15mod flags;
16mod options;
Brian Silverman5f6f2762022-08-13 19:30:05 -070017mod output;
18mod rustc;
Brian Silvermancc09f182022-03-09 15:40:20 -080019mod util;
20
21use std::fs::{copy, OpenOptions};
Brian Silverman5f6f2762022-08-13 19:30:05 -070022use std::io;
23use std::process::{exit, Command, ExitStatus, Stdio};
Brian Silvermancc09f182022-03-09 15:40:20 -080024
25use crate::options::options;
Brian Silverman5f6f2762022-08-13 19:30:05 -070026use crate::output::{process_output, LineOutput};
27
28#[cfg(windows)]
29fn status_code(status: ExitStatus, was_killed: bool) -> i32 {
30 // On windows, there's no good way to know if the process was killed by a signal.
31 // If we killed the process, we override the code to signal success.
32 if was_killed {
33 0
34 } else {
35 status.code().unwrap_or(1)
36 }
37}
38
39#[cfg(not(windows))]
40fn status_code(status: ExitStatus, was_killed: bool) -> i32 {
41 // On unix, if code is None it means that the process was killed by a signal.
42 // https://doc.rust-lang.org/std/process/struct.ExitStatus.html#method.success
43 match status.code() {
44 Some(code) => code,
45 // If we killed the process, we expect None here
46 None if was_killed => 0,
47 // Otherwise it's some unexpected signal
48 None => 1,
49 }
50}
Brian Silvermancc09f182022-03-09 15:40:20 -080051
52fn main() {
53 let opts = match options() {
54 Err(err) => panic!("process wrapper error: {}", err),
55 Ok(v) => v,
56 };
Brian Silverman5f6f2762022-08-13 19:30:05 -070057
58 let mut child = Command::new(opts.executable)
Brian Silvermancc09f182022-03-09 15:40:20 -080059 .args(opts.child_arguments)
60 .env_clear()
61 .envs(opts.child_environment)
Brian Silverman5f6f2762022-08-13 19:30:05 -070062 .stdout(if let Some(stdout_file) = opts.stdout_file {
63 OpenOptions::new()
64 .create(true)
65 .truncate(true)
66 .write(true)
67 .open(stdout_file)
68 .expect("process wrapper error: unable to open stdout file")
69 .into()
70 } else {
71 Stdio::inherit()
72 })
73 .stderr(Stdio::piped())
74 .spawn()
Brian Silvermancc09f182022-03-09 15:40:20 -080075 .expect("process wrapper error: failed to spawn child process");
76
Brian Silverman5f6f2762022-08-13 19:30:05 -070077 let mut stderr: Box<dyn io::Write> = if let Some(stderr_file) = opts.stderr_file {
78 Box::new(
79 OpenOptions::new()
80 .create(true)
81 .truncate(true)
82 .write(true)
83 .open(stderr_file)
84 .expect("process wrapper error: unable to open stderr file"),
85 )
86 } else {
87 Box::new(io::stderr())
88 };
89
90 let mut child_stderr = child.stderr.take().unwrap();
91
92 let mut was_killed = false;
93 let result = if let Some(format) = opts.rustc_output_format {
94 let quit_on_rmeta = opts.rustc_quit_on_rmeta;
95 // Process json rustc output and kill the subprocess when we get a signal
96 // that we emitted a metadata file.
97 let mut me = false;
98 let metadata_emitted = &mut me;
99 let result = process_output(&mut child_stderr, stderr.as_mut(), move |line| {
100 if quit_on_rmeta {
101 rustc::stop_on_rmeta_completion(line, format, metadata_emitted)
102 } else {
103 rustc::process_json(line, format)
104 }
105 });
106 if me {
107 // If recv returns Ok(), a signal was sent in this channel so we should terminate the child process.
108 // We can safely ignore the Result from kill() as we don't care if the process already terminated.
109 let _ = child.kill();
110 was_killed = true;
111 }
112 result
113 } else {
114 // Process output normally by forwarding stderr
115 process_output(&mut child_stderr, stderr.as_mut(), LineOutput::Message)
116 };
117 result.expect("process wrapper error: failed to process stderr");
118
119 let status = child
120 .wait()
121 .expect("process wrapper error: failed to wait for child process");
122 // If the child process is rustc and is killed after metadata generation, that's also a success.
123 let code = status_code(status, was_killed);
124 let success = code == 0;
125 if success {
Brian Silvermancc09f182022-03-09 15:40:20 -0800126 if let Some(tf) = opts.touch_file {
127 OpenOptions::new()
128 .create(true)
129 .write(true)
130 .open(tf)
131 .expect("process wrapper error: failed to create touch file");
132 }
133 if let Some((copy_source, copy_dest)) = opts.copy_output {
134 copy(&copy_source, &copy_dest).unwrap_or_else(|_| {
135 panic!(
136 "process wrapper error: failed to copy {} into {}",
137 copy_source, copy_dest
138 )
139 });
140 }
141 }
142
Brian Silverman5f6f2762022-08-13 19:30:05 -0700143 exit(code)
Brian Silvermancc09f182022-03-09 15:40:20 -0800144}