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