Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 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 | mod flags; |
| 16 | mod options; |
Brian Silverman | 5f6f276 | 2022-08-13 19:30:05 -0700 | [diff] [blame^] | 17 | mod output; |
| 18 | mod rustc; |
Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 19 | mod util; |
| 20 | |
| 21 | use std::fs::{copy, OpenOptions}; |
Brian Silverman | 5f6f276 | 2022-08-13 19:30:05 -0700 | [diff] [blame^] | 22 | use std::io; |
| 23 | use std::process::{exit, Command, ExitStatus, Stdio}; |
Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 24 | |
| 25 | use crate::options::options; |
Brian Silverman | 5f6f276 | 2022-08-13 19:30:05 -0700 | [diff] [blame^] | 26 | use crate::output::{process_output, LineOutput}; |
| 27 | |
| 28 | #[cfg(windows)] |
| 29 | fn 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))] |
| 40 | fn 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 Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 51 | |
| 52 | fn main() { |
| 53 | let opts = match options() { |
| 54 | Err(err) => panic!("process wrapper error: {}", err), |
| 55 | Ok(v) => v, |
| 56 | }; |
Brian Silverman | 5f6f276 | 2022-08-13 19:30:05 -0700 | [diff] [blame^] | 57 | |
| 58 | let mut child = Command::new(opts.executable) |
Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 59 | .args(opts.child_arguments) |
| 60 | .env_clear() |
| 61 | .envs(opts.child_environment) |
Brian Silverman | 5f6f276 | 2022-08-13 19:30:05 -0700 | [diff] [blame^] | 62 | .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 Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 75 | .expect("process wrapper error: failed to spawn child process"); |
| 76 | |
Brian Silverman | 5f6f276 | 2022-08-13 19:30:05 -0700 | [diff] [blame^] | 77 | 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 Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 126 | 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(©_source, ©_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 Silverman | 5f6f276 | 2022-08-13 19:30:05 -0700 | [diff] [blame^] | 143 | exit(code) |
Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame] | 144 | } |