1 //! A thin wrapper around `Command` in the standard library which allows us to
2 //! read the arguments that are built up.
4 use std
::ffi
::{OsStr, OsString}
;
8 use std
::process
::{self, Output}
;
10 use rustc_span
::symbol
::Symbol
;
11 use rustc_target
::spec
::LldFlavor
;
17 env
: Vec
<(OsString
, OsString
)>,
18 env_remove
: Vec
<OsString
>,
24 CmdBatScript(OsString
),
25 Lld(OsString
, LldFlavor
),
29 pub fn new
<P
: AsRef
<OsStr
>>(program
: P
) -> Command
{
30 Command
::_new(Program
::Normal(program
.as_ref().to_owned()))
33 pub fn bat_script
<P
: AsRef
<OsStr
>>(program
: P
) -> Command
{
34 Command
::_new(Program
::CmdBatScript(program
.as_ref().to_owned()))
37 pub fn lld
<P
: AsRef
<OsStr
>>(program
: P
, flavor
: LldFlavor
) -> Command
{
38 Command
::_new(Program
::Lld(program
.as_ref().to_owned(), flavor
))
41 fn _new(program
: Program
) -> Command
{
42 Command { program, args: Vec::new(), env: Vec::new(), env_remove: Vec::new() }
45 pub fn arg
<P
: AsRef
<OsStr
>>(&mut self, arg
: P
) -> &mut Command
{
46 self._arg(arg
.as_ref());
50 pub fn sym_arg(&mut self, arg
: Symbol
) -> &mut Command
{
51 self.arg(&*arg
.as_str());
55 pub fn args
<I
>(&mut self, args
: I
) -> &mut Command
57 I
: IntoIterator
<Item
: AsRef
<OsStr
>>,
60 self._arg(arg
.as_ref());
65 fn _arg(&mut self, arg
: &OsStr
) {
66 self.args
.push(arg
.to_owned());
69 pub fn env
<K
, V
>(&mut self, key
: K
, value
: V
) -> &mut Command
74 self._env(key
.as_ref(), value
.as_ref());
78 fn _env(&mut self, key
: &OsStr
, value
: &OsStr
) {
79 self.env
.push((key
.to_owned(), value
.to_owned()));
82 pub fn env_remove
<K
>(&mut self, key
: K
) -> &mut Command
86 self._env_remove(key
.as_ref());
90 fn _env_remove(&mut self, key
: &OsStr
) {
91 self.env_remove
.push(key
.to_owned());
94 pub fn output(&mut self) -> io
::Result
<Output
> {
95 self.command().output()
98 pub fn command(&self) -> process
::Command
{
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");
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",
117 ret
.args(&self.args
);
118 ret
.envs(self.env
.clone());
119 for k
in &self.env_remove
{
127 pub fn get_args(&self) -> &[OsString
] {
131 pub fn take_args(&mut self) -> Vec
<OsString
> {
132 mem
::take(&mut self.args
)
135 /// Returns a `true` if we're pretty sure that this'll blow OS spawn limits,
136 /// or `false` if we should attempt to spawn and see what the OS says.
137 pub fn very_likely_to_exceed_some_spawn_limit(&self) -> bool
{
138 // We mostly only care about Windows in this method, on Unix the limits
139 // can be gargantuan anyway so we're pretty unlikely to hit them
144 // Right now LLD doesn't support the `@` syntax of passing an argument
145 // through files, so regardless of the platform we try to go to the OS
147 if let Program
::Lld(..) = self.program
{
151 // Ok so on Windows to spawn a process is 32,768 characters in its
152 // command line [1]. Unfortunately we don't actually have access to that
153 // as it's calculated just before spawning. Instead we perform a
154 // poor-man's guess as to how long our command line will be. We're
155 // assuming here that we don't have to escape every character...
157 // Turns out though that `cmd.exe` has even smaller limits, 8192
158 // characters [2]. Linkers can often be batch scripts (for example
159 // Emscripten, Gecko's current build system) which means that we're
160 // running through batch scripts. These linkers often just forward
161 // arguments elsewhere (and maybe tack on more), so if we blow 8192
162 // bytes we'll typically cause them to blow as well.
164 // Basically as a result just perform an inflated estimate of what our
165 // command line will look like and test if it's > 8192 (we actually
166 // test against 6k to artificially inflate our estimate). If all else
167 // fails we'll fall back to the normal unix logic of testing the OS
168 // error code if we fail to spawn and automatically re-spawning the
169 // linker with smaller arguments.
171 // [1]: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa
172 // [2]: https://devblogs.microsoft.com/oldnewthing/?p=41553
174 let estimated_command_line_len
= self.args
.iter().map(|a
| a
.len()).sum
::<usize>();
175 estimated_command_line_len
> 1024 * 6
179 impl fmt
::Debug
for Command
{
180 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
181 self.command().fmt(f
)