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