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