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