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