]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_codegen_ssa/src/back/command.rs
New upstream version 1.59.0+dfsg1
[rustc.git] / compiler / rustc_codegen_ssa / src / back / command.rs
CommitLineData
ea8adc8c
XL
1//! A thin wrapper around `Command` in the standard library which allows us to
2//! read the arguments that are built up.
3
4use std::ffi::{OsStr, OsString};
5use std::fmt;
6use std::io;
ff7c6d11 7use std::mem;
2c00a5a8 8use std::process::{self, Output};
ea8adc8c 9
dfeec247 10use rustc_span::symbol::Symbol;
83c7162d 11use rustc_target::spec::LldFlavor;
0531ce1d 12
ff7c6d11 13#[derive(Clone)]
ea8adc8c 14pub struct Command {
ff7c6d11 15 program: Program,
ea8adc8c
XL
16 args: Vec<OsString>,
17 env: Vec<(OsString, OsString)>,
e1599b0c 18 env_remove: Vec<OsString>,
ea8adc8c
XL
19}
20
ff7c6d11
XL
21#[derive(Clone)]
22enum Program {
23 Normal(OsString),
24 CmdBatScript(OsString),
dfeec247 25 Lld(OsString, LldFlavor),
ff7c6d11
XL
26}
27
ea8adc8c
XL
28impl Command {
29 pub fn new<P: AsRef<OsStr>>(program: P) -> Command {
ff7c6d11
XL
30 Command::_new(Program::Normal(program.as_ref().to_owned()))
31 }
32
33 pub fn bat_script<P: AsRef<OsStr>>(program: P) -> Command {
34 Command::_new(Program::CmdBatScript(program.as_ref().to_owned()))
ea8adc8c
XL
35 }
36
0531ce1d
XL
37 pub fn lld<P: AsRef<OsStr>>(program: P, flavor: LldFlavor) -> Command {
38 Command::_new(Program::Lld(program.as_ref().to_owned(), flavor))
39 }
40
ff7c6d11 41 fn _new(program: Program) -> Command {
dfeec247 42 Command { program, args: Vec::new(), env: Vec::new(), env_remove: Vec::new() }
ea8adc8c
XL
43 }
44
45 pub fn arg<P: AsRef<OsStr>>(&mut self, arg: P) -> &mut Command {
46 self._arg(arg.as_ref());
47 self
48 }
49
e1599b0c 50 pub fn sym_arg(&mut self, arg: Symbol) -> &mut Command {
a2a8927a 51 self.arg(arg.as_str());
e1599b0c
XL
52 self
53 }
54
ea8adc8c 55 pub fn args<I>(&mut self, args: I) -> &mut Command
416331ca
XL
56 where
57 I: IntoIterator<Item: AsRef<OsStr>>,
ea8adc8c
XL
58 {
59 for arg in args {
60 self._arg(arg.as_ref());
61 }
62 self
63 }
64
65 fn _arg(&mut self, arg: &OsStr) {
66 self.args.push(arg.to_owned());
67 }
68
69 pub fn env<K, V>(&mut self, key: K, value: V) -> &mut Command
dfeec247
XL
70 where
71 K: AsRef<OsStr>,
72 V: AsRef<OsStr>,
ea8adc8c
XL
73 {
74 self._env(key.as_ref(), value.as_ref());
75 self
76 }
77
ea8adc8c
XL
78 fn _env(&mut self, key: &OsStr, value: &OsStr) {
79 self.env.push((key.to_owned(), value.to_owned()));
80 }
81
e1599b0c 82 pub fn env_remove<K>(&mut self, key: K) -> &mut Command
dfeec247
XL
83 where
84 K: AsRef<OsStr>,
e1599b0c
XL
85 {
86 self._env_remove(key.as_ref());
87 self
88 }
89
90 fn _env_remove(&mut self, key: &OsStr) {
91 self.env_remove.push(key.to_owned());
92 }
93
ea8adc8c
XL
94 pub fn output(&mut self) -> io::Result<Output> {
95 self.command().output()
96 }
97
ea8adc8c 98 pub fn command(&self) -> process::Command {
ff7c6d11
XL
99 let mut ret = match self.program {
100 Program::Normal(ref p) => process::Command::new(p),
101 Program::CmdBatScript(ref p) => {
102 let mut c = process::Command::new("cmd");
103 c.arg("/c").arg(p);
104 c
105 }
0531ce1d
XL
106 Program::Lld(ref p, flavor) => {
107 let mut c = process::Command::new(p);
108 c.arg("-flavor").arg(match flavor {
109 LldFlavor::Wasm => "wasm",
110 LldFlavor::Ld => "gnu",
111 LldFlavor::Link => "link",
112 LldFlavor::Ld64 => "darwin",
113 });
6c58768f
XL
114 if let LldFlavor::Wasm = flavor {
115 // LLVM expects host-specific formatting for @file
116 // arguments, but we always generate posix formatted files
117 // at this time. Indicate as such.
118 c.arg("--rsp-quoting=posix");
119 }
0531ce1d
XL
120 c
121 }
ff7c6d11 122 };
ea8adc8c
XL
123 ret.args(&self.args);
124 ret.envs(self.env.clone());
e1599b0c
XL
125 for k in &self.env_remove {
126 ret.env_remove(k);
127 }
ba9703b0 128 ret
ea8adc8c
XL
129 }
130
131 // extensions
132
0531ce1d
XL
133 pub fn get_args(&self) -> &[OsString] {
134 &self.args
135 }
136
ff7c6d11 137 pub fn take_args(&mut self) -> Vec<OsString> {
416331ca 138 mem::take(&mut self.args)
ea8adc8c
XL
139 }
140
ff7c6d11
XL
141 /// Returns a `true` if we're pretty sure that this'll blow OS spawn limits,
142 /// or `false` if we should attempt to spawn and see what the OS says.
143 pub fn very_likely_to_exceed_some_spawn_limit(&self) -> bool {
144 // We mostly only care about Windows in this method, on Unix the limits
145 // can be gargantuan anyway so we're pretty unlikely to hit them
146 if cfg!(unix) {
dfeec247 147 return false;
ff7c6d11 148 }
ea8adc8c 149
0531ce1d
XL
150 // Right now LLD doesn't support the `@` syntax of passing an argument
151 // through files, so regardless of the platform we try to go to the OS
152 // on this one.
153 if let Program::Lld(..) = self.program {
dfeec247 154 return false;
0531ce1d
XL
155 }
156
ff7c6d11
XL
157 // Ok so on Windows to spawn a process is 32,768 characters in its
158 // command line [1]. Unfortunately we don't actually have access to that
159 // as it's calculated just before spawning. Instead we perform a
160 // poor-man's guess as to how long our command line will be. We're
161 // assuming here that we don't have to escape every character...
162 //
163 // Turns out though that `cmd.exe` has even smaller limits, 8192
164 // characters [2]. Linkers can often be batch scripts (for example
165 // Emscripten, Gecko's current build system) which means that we're
166 // running through batch scripts. These linkers often just forward
167 // arguments elsewhere (and maybe tack on more), so if we blow 8192
168 // bytes we'll typically cause them to blow as well.
169 //
170 // Basically as a result just perform an inflated estimate of what our
171 // command line will look like and test if it's > 8192 (we actually
172 // test against 6k to artificially inflate our estimate). If all else
173 // fails we'll fall back to the normal unix logic of testing the OS
174 // error code if we fail to spawn and automatically re-spawning the
175 // linker with smaller arguments.
176 //
dfeec247
XL
177 // [1]: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa
178 // [2]: https://devblogs.microsoft.com/oldnewthing/?p=41553
ff7c6d11 179
dfeec247 180 let estimated_command_line_len = self.args.iter().map(|a| a.len()).sum::<usize>();
ff7c6d11 181 estimated_command_line_len > 1024 * 6
ea8adc8c
XL
182 }
183}
184
185impl fmt::Debug for Command {
9fa01778 186 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
ea8adc8c
XL
187 self.command().fmt(f)
188 }
189}