]> git.proxmox.com Git - rustc.git/blob - src/bootstrap/bootstrap.py
Imported Upstream version 1.9.0+dfsg1
[rustc.git] / src / bootstrap / bootstrap.py
1 # Copyright 2015-2016 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 import argparse
12 import contextlib
13 import os
14 import shutil
15 import subprocess
16 import sys
17 import tarfile
18
19 def get(url, path, verbose=False):
20 print("downloading " + url)
21 # see http://serverfault.com/questions/301128/how-to-download
22 if sys.platform == 'win32':
23 run(["PowerShell.exe", "/nologo", "-Command",
24 "(New-Object System.Net.WebClient).DownloadFile('" + url +
25 "', '" + path + "')"], verbose=verbose)
26 else:
27 run(["curl", "-o", path, url], verbose=verbose)
28
29 def unpack(tarball, dst, verbose=False, match=None):
30 print("extracting " + tarball)
31 fname = os.path.basename(tarball).replace(".tar.gz", "")
32 with contextlib.closing(tarfile.open(tarball)) as tar:
33 for p in tar.getnames():
34 if "/" not in p:
35 continue
36 name = p.replace(fname + "/", "", 1)
37 if match is not None and not name.startswith(match):
38 continue
39 name = name[len(match) + 1:]
40
41 fp = os.path.join(dst, name)
42 if verbose:
43 print(" extracting " + p)
44 tar.extract(p, dst)
45 tp = os.path.join(dst, p)
46 if os.path.isdir(tp) and os.path.exists(fp):
47 continue
48 shutil.move(tp, fp)
49 shutil.rmtree(os.path.join(dst, fname))
50
51 def run(args, verbose=False):
52 if verbose:
53 print("running: " + ' '.join(args))
54 sys.stdout.flush()
55 # Use Popen here instead of call() as it apparently allows powershell on
56 # Windows to not lock up waiting for input presumably.
57 ret = subprocess.Popen(args)
58 code = ret.wait()
59 if code != 0:
60 if not verbose:
61 print("failed to run: " + ' '.join(args))
62 raise RuntimeError("failed to run command")
63
64 class RustBuild:
65 def download_rust_nightly(self):
66 cache_dst = os.path.join(self.build_dir, "cache")
67 rustc_cache = os.path.join(cache_dst, self.snap_rustc_date())
68 cargo_cache = os.path.join(cache_dst, self.snap_cargo_date())
69 if not os.path.exists(rustc_cache):
70 os.makedirs(rustc_cache)
71 if not os.path.exists(cargo_cache):
72 os.makedirs(cargo_cache)
73
74 if self.rustc().startswith(self.bin_root()) and \
75 (not os.path.exists(self.rustc()) or self.rustc_out_of_date()):
76 if os.path.exists(self.bin_root()):
77 shutil.rmtree(self.bin_root())
78 filename = "rust-std-nightly-" + self.build + ".tar.gz"
79 url = "https://static.rust-lang.org/dist/" + self.snap_rustc_date()
80 tarball = os.path.join(rustc_cache, filename)
81 if not os.path.exists(tarball):
82 get(url + "/" + filename, tarball, verbose=self.verbose)
83 unpack(tarball, self.bin_root(),
84 match="rust-std-" + self.build,
85 verbose=self.verbose)
86
87 filename = "rustc-nightly-" + self.build + ".tar.gz"
88 url = "https://static.rust-lang.org/dist/" + self.snap_rustc_date()
89 tarball = os.path.join(rustc_cache, filename)
90 if not os.path.exists(tarball):
91 get(url + "/" + filename, tarball, verbose=self.verbose)
92 unpack(tarball, self.bin_root(), match="rustc", verbose=self.verbose)
93 with open(self.rustc_stamp(), 'w') as f:
94 f.write(self.snap_rustc_date())
95
96 if self.cargo().startswith(self.bin_root()) and \
97 (not os.path.exists(self.cargo()) or self.cargo_out_of_date()):
98 filename = "cargo-nightly-" + self.build + ".tar.gz"
99 url = "https://static.rust-lang.org/cargo-dist/" + self.snap_cargo_date()
100 tarball = os.path.join(cargo_cache, filename)
101 if not os.path.exists(tarball):
102 get(url + "/" + filename, tarball, verbose=self.verbose)
103 unpack(tarball, self.bin_root(), match="cargo", verbose=self.verbose)
104 with open(self.cargo_stamp(), 'w') as f:
105 f.write(self.snap_cargo_date())
106
107 def snap_cargo_date(self):
108 return self._cargo_date
109
110 def snap_rustc_date(self):
111 return self._rustc_date
112
113 def rustc_stamp(self):
114 return os.path.join(self.bin_root(), '.rustc-stamp')
115
116 def cargo_stamp(self):
117 return os.path.join(self.bin_root(), '.cargo-stamp')
118
119 def rustc_out_of_date(self):
120 if not os.path.exists(self.rustc_stamp()):
121 return True
122 with open(self.rustc_stamp(), 'r') as f:
123 return self.snap_rustc_date() != f.read()
124
125 def cargo_out_of_date(self):
126 if not os.path.exists(self.cargo_stamp()):
127 return True
128 with open(self.cargo_stamp(), 'r') as f:
129 return self.snap_cargo_date() != f.read()
130
131 def bin_root(self):
132 return os.path.join(self.build_dir, self.build, "stage0")
133
134 def get_toml(self, key):
135 for line in self.config_toml.splitlines():
136 if line.startswith(key + ' ='):
137 return self.get_string(line)
138 return None
139
140 def get_mk(self, key):
141 for line in iter(self.config_mk.splitlines()):
142 if line.startswith(key):
143 return line[line.find(':=') + 2:].strip()
144 return None
145
146 def cargo(self):
147 config = self.get_toml('cargo')
148 if config:
149 return config
150 return os.path.join(self.bin_root(), "bin/cargo" + self.exe_suffix())
151
152 def rustc(self):
153 config = self.get_toml('rustc')
154 if config:
155 return config
156 config = self.get_mk('CFG_LOCAL_RUST')
157 if config:
158 return config + '/bin/rustc' + self.exe_suffix()
159 return os.path.join(self.bin_root(), "bin/rustc" + self.exe_suffix())
160
161 def get_string(self, line):
162 start = line.find('"')
163 end = start + 1 + line[start+1:].find('"')
164 return line[start+1:end]
165
166 def exe_suffix(self):
167 if sys.platform == 'win32':
168 return '.exe'
169 else:
170 return ''
171
172 def parse_nightly_dates(self):
173 nightlies = os.path.join(self.rust_root, "src/nightlies.txt")
174 with open(nightlies, 'r') as nightlies:
175 rustc, cargo = nightlies.read().split("\n")[:2]
176 assert rustc.startswith("rustc: ")
177 assert cargo.startswith("cargo: ")
178 self._rustc_date = rustc[len("rustc: "):]
179 self._cargo_date = cargo[len("cargo: "):]
180
181 def build_bootstrap(self):
182 env = os.environ.copy()
183 env["CARGO_TARGET_DIR"] = os.path.join(self.build_dir, "bootstrap")
184 env["RUSTC"] = self.rustc()
185 env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib")
186 env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib")
187 env["PATH"] = os.path.join(self.bin_root(), "bin") + \
188 os.pathsep + env["PATH"]
189 self.run([self.cargo(), "build", "--manifest-path",
190 os.path.join(self.rust_root, "src/bootstrap/Cargo.toml")],
191 env)
192
193 def run(self, args, env):
194 proc = subprocess.Popen(args, env = env)
195 ret = proc.wait()
196 if ret != 0:
197 sys.exit(ret)
198
199 def build_triple(self):
200 config = self.get_toml('build')
201 if config:
202 return config
203 config = self.get_mk('CFG_BUILD')
204 if config:
205 return config
206 try:
207 ostype = subprocess.check_output(['uname', '-s']).strip()
208 cputype = subprocess.check_output(['uname', '-m']).strip()
209 except FileNotFoundError:
210 if sys.platform == 'win32':
211 return 'x86_64-pc-windows-msvc'
212 else:
213 raise
214
215 # Darwin's `uname -s` lies and always returns i386. We have to use
216 # sysctl instead.
217 if ostype == 'Darwin' and cputype == 'i686':
218 sysctl = subprocess.check_output(['sysctl', 'hw.optional.x86_64'])
219 if sysctl.contains(': 1'):
220 cputype = 'x86_64'
221
222 # The goal here is to come up with the same triple as LLVM would,
223 # at least for the subset of platforms we're willing to target.
224 if ostype == 'Linux':
225 ostype = 'unknown-linux-gnu'
226 elif ostype == 'FreeBSD':
227 ostype = 'unknown-freebsd'
228 elif ostype == 'DragonFly':
229 ostype = 'unknown-dragonfly'
230 elif ostype == 'Bitrig':
231 ostype = 'unknown-bitrig'
232 elif ostype == 'OpenBSD':
233 ostype = 'unknown-openbsd'
234 elif ostype == 'NetBSD':
235 ostype = 'unknown-netbsd'
236 elif ostype == 'Darwin':
237 ostype = 'apple-darwin'
238 elif ostype.startswith('MINGW'):
239 # msys' `uname` does not print gcc configuration, but prints msys
240 # configuration. so we cannot believe `uname -m`:
241 # msys1 is always i686 and msys2 is always x86_64.
242 # instead, msys defines $MSYSTEM which is MINGW32 on i686 and
243 # MINGW64 on x86_64.
244 ostype = 'pc-windows-gnu'
245 cputype = 'i686'
246 if os.environ.get('MSYSTEM') == 'MINGW64':
247 cputype = 'x86_64'
248 elif ostype.startswith('MSYS'):
249 ostype = 'pc-windows-gnu'
250 elif ostype.startswith('CYGWIN_NT'):
251 cputype = 'i686'
252 if ostype.endswith('WOW64'):
253 cputype = 'x86_64'
254 ostype = 'pc-windows-gnu'
255 else:
256 raise ValueError("unknown OS type: " + ostype)
257
258 if cputype in {'i386', 'i486', 'i686', 'i786', 'x86'}:
259 cputype = 'i686'
260 elif cputype in {'xscale', 'arm'}:
261 cputype = 'arm'
262 elif cputype == 'armv7l':
263 cputype = 'arm'
264 ostype += 'eabihf'
265 elif cputype == 'aarch64':
266 cputype = 'aarch64'
267 elif cputype in {'powerpc', 'ppc', 'ppc64'}:
268 cputype = 'powerpc'
269 elif cputype in {'amd64', 'x86_64', 'x86-64', 'x64'}:
270 cputype = 'x86_64'
271 else:
272 raise ValueError("unknown cpu type: " + cputype)
273
274 return cputype + '-' + ostype
275
276 parser = argparse.ArgumentParser(description='Build rust')
277 parser.add_argument('--config')
278 parser.add_argument('-v', '--verbose', action='store_true')
279
280 args = [a for a in sys.argv if a != '-h']
281 args, _ = parser.parse_known_args(args)
282
283 # Configure initial bootstrap
284 rb = RustBuild()
285 rb.config_toml = ''
286 rb.config_mk = ''
287 rb.rust_root = os.path.abspath(os.path.join(__file__, '../../..'))
288 rb.build_dir = os.path.join(os.getcwd(), "build")
289 rb.verbose = args.verbose
290
291 try:
292 with open(args.config or 'config.toml') as config:
293 rb.config_toml = config.read()
294 except:
295 pass
296 try:
297 rb.config_mk = open('config.mk').read()
298 except:
299 pass
300
301 # Fetch/build the bootstrap
302 rb.build = rb.build_triple()
303 rb.parse_nightly_dates()
304 rb.download_rust_nightly()
305 sys.stdout.flush()
306 rb.build_bootstrap()
307 sys.stdout.flush()
308
309 # Run the bootstrap
310 args = [os.path.join(rb.build_dir, "bootstrap/debug/bootstrap")]
311 args.extend(sys.argv[1:])
312 args.append('--src')
313 args.append(rb.rust_root)
314 args.append('--build')
315 args.append(rb.build)
316 env = os.environ.copy()
317 env["BOOTSTRAP_PARENT_ID"] = str(os.getpid())
318 rb.run(args, env)