]>
Commit | Line | Data |
---|---|---|
abe05a73 | 1 | from __future__ import absolute_import, division, print_function |
7453a54e SL |
2 | import argparse |
3 | import contextlib | |
3157f602 | 4 | import datetime |
f9f354fc | 5 | import distutils.version |
a7813a04 | 6 | import hashlib |
7453a54e | 7 | import os |
7cac9316 | 8 | import re |
7453a54e SL |
9 | import shutil |
10 | import subprocess | |
11 | import sys | |
12 | import tarfile | |
a7813a04 XL |
13 | import tempfile |
14 | ||
3157f602 XL |
15 | from time import time |
16 | ||
7453a54e SL |
17 | |
18 | def get(url, path, verbose=False): | |
041b39d2 XL |
19 | suffix = '.sha256' |
20 | sha_url = url + suffix | |
a7813a04 XL |
21 | with tempfile.NamedTemporaryFile(delete=False) as temp_file: |
22 | temp_path = temp_file.name | |
041b39d2 | 23 | with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as sha_file: |
a7813a04 XL |
24 | sha_path = sha_file.name |
25 | ||
26 | try: | |
476ff2be | 27 | download(sha_path, sha_url, False, verbose) |
5bcae85e SL |
28 | if os.path.exists(path): |
29 | if verify(path, sha_path, False): | |
476ff2be | 30 | if verbose: |
3b2f2976 | 31 | print("using already-download file", path) |
5bcae85e SL |
32 | return |
33 | else: | |
476ff2be | 34 | if verbose: |
3b2f2976 XL |
35 | print("ignoring already-download file", |
36 | path, "due to failed verification") | |
5bcae85e | 37 | os.unlink(path) |
476ff2be SL |
38 | download(temp_path, url, True, verbose) |
39 | if not verify(temp_path, sha_path, verbose): | |
5bcae85e | 40 | raise RuntimeError("failed verification") |
476ff2be SL |
41 | if verbose: |
42 | print("moving {} to {}".format(temp_path, path)) | |
a7813a04 XL |
43 | shutil.move(temp_path, path) |
44 | finally: | |
476ff2be SL |
45 | delete_if_present(sha_path, verbose) |
46 | delete_if_present(temp_path, verbose) | |
a7813a04 XL |
47 | |
48 | ||
476ff2be | 49 | def delete_if_present(path, verbose): |
041b39d2 | 50 | """Remove the given file if present""" |
a7813a04 | 51 | if os.path.isfile(path): |
476ff2be | 52 | if verbose: |
3b2f2976 | 53 | print("removing", path) |
a7813a04 XL |
54 | os.unlink(path) |
55 | ||
56 | ||
476ff2be | 57 | def download(path, url, probably_big, verbose): |
3b2f2976 | 58 | for _ in range(0, 4): |
8bb4bdeb XL |
59 | try: |
60 | _download(path, url, probably_big, verbose, True) | |
61 | return | |
62 | except RuntimeError: | |
63 | print("\nspurious failure, trying again") | |
64 | _download(path, url, probably_big, verbose, False) | |
65 | ||
66 | ||
67 | def _download(path, url, probably_big, verbose, exception): | |
476ff2be SL |
68 | if probably_big or verbose: |
69 | print("downloading {}".format(url)) | |
7453a54e SL |
70 | # see http://serverfault.com/questions/301128/how-to-download |
71 | if sys.platform == 'win32': | |
72 | run(["PowerShell.exe", "/nologo", "-Command", | |
a1dfa0c6 XL |
73 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;", |
74 | "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')".format(url, path)], | |
8bb4bdeb XL |
75 | verbose=verbose, |
76 | exception=exception) | |
7453a54e | 77 | else: |
476ff2be SL |
78 | if probably_big or verbose: |
79 | option = "-#" | |
80 | else: | |
81 | option = "-s" | |
f9f354fc | 82 | require(["curl", "--version"]) |
b7449926 XL |
83 | run(["curl", option, |
84 | "-y", "30", "-Y", "10", # timeout if speed is < 10 bytes/sec for > 30 seconds | |
74b04a01 | 85 | "--connect-timeout", "30", # timeout if cannot connect within 30 seconds |
b7449926 | 86 | "--retry", "3", "-Sf", "-o", path, url], |
8bb4bdeb XL |
87 | verbose=verbose, |
88 | exception=exception) | |
7453a54e | 89 | |
a7813a04 XL |
90 | |
91 | def verify(path, sha_path, verbose): | |
041b39d2 | 92 | """Check if the sha256 sum of the given path is valid""" |
476ff2be | 93 | if verbose: |
3b2f2976 | 94 | print("verifying", path) |
041b39d2 XL |
95 | with open(path, "rb") as source: |
96 | found = hashlib.sha256(source.read()).hexdigest() | |
97 | with open(sha_path, "r") as sha256sum: | |
98 | expected = sha256sum.readline().split()[0] | |
5bcae85e | 99 | verified = found == expected |
476ff2be | 100 | if not verified: |
5bcae85e | 101 | print("invalid checksum:\n" |
7cac9316 XL |
102 | " found: {}\n" |
103 | " expected: {}".format(found, expected)) | |
5bcae85e | 104 | return verified |
a7813a04 XL |
105 | |
106 | ||
60c5eb7d | 107 | def unpack(tarball, tarball_suffix, dst, verbose=False, match=None): |
041b39d2 | 108 | """Unpack the given tarball file""" |
3b2f2976 | 109 | print("extracting", tarball) |
60c5eb7d | 110 | fname = os.path.basename(tarball).replace(tarball_suffix, "") |
7453a54e | 111 | with contextlib.closing(tarfile.open(tarball)) as tar: |
3b2f2976 XL |
112 | for member in tar.getnames(): |
113 | if "/" not in member: | |
7453a54e | 114 | continue |
3b2f2976 | 115 | name = member.replace(fname + "/", "", 1) |
7453a54e SL |
116 | if match is not None and not name.startswith(match): |
117 | continue | |
118 | name = name[len(match) + 1:] | |
119 | ||
3b2f2976 | 120 | dst_path = os.path.join(dst, name) |
7453a54e | 121 | if verbose: |
3b2f2976 XL |
122 | print(" extracting", member) |
123 | tar.extract(member, dst) | |
124 | src_path = os.path.join(dst, member) | |
125 | if os.path.isdir(src_path) and os.path.exists(dst_path): | |
7453a54e | 126 | continue |
3b2f2976 | 127 | shutil.move(src_path, dst_path) |
7453a54e SL |
128 | shutil.rmtree(os.path.join(dst, fname)) |
129 | ||
041b39d2 | 130 | |
7cac9316 | 131 | def run(args, verbose=False, exception=False, **kwargs): |
3b2f2976 | 132 | """Run a child program in a new process""" |
7453a54e SL |
133 | if verbose: |
134 | print("running: " + ' '.join(args)) | |
135 | sys.stdout.flush() | |
136 | # Use Popen here instead of call() as it apparently allows powershell on | |
137 | # Windows to not lock up waiting for input presumably. | |
7cac9316 | 138 | ret = subprocess.Popen(args, **kwargs) |
7453a54e SL |
139 | code = ret.wait() |
140 | if code != 0: | |
a7813a04 | 141 | err = "failed to run: " + ' '.join(args) |
8bb4bdeb | 142 | if verbose or exception: |
a7813a04 XL |
143 | raise RuntimeError(err) |
144 | sys.exit(err) | |
145 | ||
7cac9316 | 146 | |
f9f354fc XL |
147 | def require(cmd, exit=True): |
148 | '''Run a command, returning its output. | |
149 | On error, | |
150 | If `exit` is `True`, exit the process. | |
151 | Otherwise, return None.''' | |
152 | try: | |
153 | return subprocess.check_output(cmd).strip() | |
154 | except (subprocess.CalledProcessError, OSError) as exc: | |
155 | if not exit: | |
156 | return None | |
157 | print("error: unable to run `{}`: {}".format(' '.join(cmd), exc)) | |
158 | print("Please make sure it's installed and in the path.") | |
159 | sys.exit(1) | |
160 | ||
161 | ||
a7813a04 | 162 | def stage0_data(rust_root): |
3b2f2976 | 163 | """Build a dictionary from stage0.txt""" |
a7813a04 XL |
164 | nightlies = os.path.join(rust_root, "src/stage0.txt") |
165 | with open(nightlies, 'r') as nightlies: | |
3b2f2976 XL |
166 | lines = [line.rstrip() for line in nightlies |
167 | if not line.startswith("#")] | |
168 | return dict([line.split(": ", 1) for line in lines if line]) | |
3157f602 | 169 | |
7cac9316 | 170 | |
3157f602 | 171 | def format_build_time(duration): |
3b2f2976 XL |
172 | """Return a nicer format for build time |
173 | ||
174 | >>> format_build_time('300') | |
175 | '0:05:00' | |
176 | """ | |
3157f602 | 177 | return str(datetime.timedelta(seconds=int(duration))) |
7453a54e | 178 | |
9e0c209e | 179 | |
ea8adc8c XL |
180 | def default_build_triple(): |
181 | """Build triple as in LLVM""" | |
182 | default_encoding = sys.getdefaultencoding() | |
f9f354fc XL |
183 | required = sys.platform != 'win32' |
184 | ostype = require(["uname", "-s"], exit=required) | |
185 | cputype = require(['uname', '-m'], exit=required) | |
186 | ||
f035d41b | 187 | # If we do not have `uname`, assume Windows. |
f9f354fc XL |
188 | if ostype is None or cputype is None: |
189 | return 'x86_64-pc-windows-msvc' | |
190 | ||
191 | ostype = ostype.decode(default_encoding) | |
192 | cputype = cputype.decode(default_encoding) | |
ea8adc8c XL |
193 | |
194 | # The goal here is to come up with the same triple as LLVM would, | |
195 | # at least for the subset of platforms we're willing to target. | |
196 | ostype_mapper = { | |
ea8adc8c XL |
197 | 'Darwin': 'apple-darwin', |
198 | 'DragonFly': 'unknown-dragonfly', | |
199 | 'FreeBSD': 'unknown-freebsd', | |
200 | 'Haiku': 'unknown-haiku', | |
201 | 'NetBSD': 'unknown-netbsd', | |
202 | 'OpenBSD': 'unknown-openbsd' | |
203 | } | |
204 | ||
205 | # Consider the direct transformation first and then the special cases | |
206 | if ostype in ostype_mapper: | |
207 | ostype = ostype_mapper[ostype] | |
208 | elif ostype == 'Linux': | |
209 | os_from_sp = subprocess.check_output( | |
210 | ['uname', '-o']).strip().decode(default_encoding) | |
211 | if os_from_sp == 'Android': | |
212 | ostype = 'linux-android' | |
213 | else: | |
214 | ostype = 'unknown-linux-gnu' | |
215 | elif ostype == 'SunOS': | |
216 | ostype = 'sun-solaris' | |
217 | # On Solaris, uname -m will return a machine classification instead | |
218 | # of a cpu type, so uname -p is recommended instead. However, the | |
219 | # output from that option is too generic for our purposes (it will | |
220 | # always emit 'i386' on x86/amd64 systems). As such, isainfo -k | |
221 | # must be used instead. | |
f9f354fc | 222 | cputype = require(['isainfo', '-k']).decode(default_encoding) |
ea8adc8c XL |
223 | elif ostype.startswith('MINGW'): |
224 | # msys' `uname` does not print gcc configuration, but prints msys | |
225 | # configuration. so we cannot believe `uname -m`: | |
226 | # msys1 is always i686 and msys2 is always x86_64. | |
227 | # instead, msys defines $MSYSTEM which is MINGW32 on i686 and | |
228 | # MINGW64 on x86_64. | |
229 | ostype = 'pc-windows-gnu' | |
230 | cputype = 'i686' | |
231 | if os.environ.get('MSYSTEM') == 'MINGW64': | |
232 | cputype = 'x86_64' | |
233 | elif ostype.startswith('MSYS'): | |
234 | ostype = 'pc-windows-gnu' | |
235 | elif ostype.startswith('CYGWIN_NT'): | |
236 | cputype = 'i686' | |
237 | if ostype.endswith('WOW64'): | |
238 | cputype = 'x86_64' | |
239 | ostype = 'pc-windows-gnu' | |
f035d41b XL |
240 | elif sys.platform == 'win32': |
241 | # Some Windows platforms might have a `uname` command that returns a | |
242 | # non-standard string (e.g. gnuwin32 tools returns `windows32`). In | |
243 | # these cases, fall back to using sys.platform. | |
244 | return 'x86_64-pc-windows-msvc' | |
ea8adc8c XL |
245 | else: |
246 | err = "unknown OS type: {}".format(ostype) | |
247 | sys.exit(err) | |
248 | ||
9fa01778 XL |
249 | if cputype == 'powerpc' and ostype == 'unknown-freebsd': |
250 | cputype = subprocess.check_output( | |
251 | ['uname', '-p']).strip().decode(default_encoding) | |
ea8adc8c XL |
252 | cputype_mapper = { |
253 | 'BePC': 'i686', | |
254 | 'aarch64': 'aarch64', | |
255 | 'amd64': 'x86_64', | |
256 | 'arm64': 'aarch64', | |
257 | 'i386': 'i686', | |
258 | 'i486': 'i686', | |
259 | 'i686': 'i686', | |
260 | 'i786': 'i686', | |
261 | 'powerpc': 'powerpc', | |
262 | 'powerpc64': 'powerpc64', | |
263 | 'powerpc64le': 'powerpc64le', | |
264 | 'ppc': 'powerpc', | |
265 | 'ppc64': 'powerpc64', | |
266 | 'ppc64le': 'powerpc64le', | |
267 | 's390x': 's390x', | |
268 | 'x64': 'x86_64', | |
269 | 'x86': 'i686', | |
270 | 'x86-64': 'x86_64', | |
271 | 'x86_64': 'x86_64' | |
272 | } | |
273 | ||
274 | # Consider the direct transformation first and then the special cases | |
275 | if cputype in cputype_mapper: | |
276 | cputype = cputype_mapper[cputype] | |
277 | elif cputype in {'xscale', 'arm'}: | |
278 | cputype = 'arm' | |
279 | if ostype == 'linux-android': | |
280 | ostype = 'linux-androideabi' | |
532ac7d7 XL |
281 | elif ostype == 'unknown-freebsd': |
282 | cputype = subprocess.check_output( | |
283 | ['uname', '-p']).strip().decode(default_encoding) | |
284 | ostype = 'unknown-freebsd' | |
ea8adc8c XL |
285 | elif cputype == 'armv6l': |
286 | cputype = 'arm' | |
287 | if ostype == 'linux-android': | |
288 | ostype = 'linux-androideabi' | |
289 | else: | |
290 | ostype += 'eabihf' | |
291 | elif cputype in {'armv7l', 'armv8l'}: | |
292 | cputype = 'armv7' | |
293 | if ostype == 'linux-android': | |
294 | ostype = 'linux-androideabi' | |
295 | else: | |
296 | ostype += 'eabihf' | |
297 | elif cputype == 'mips': | |
298 | if sys.byteorder == 'big': | |
299 | cputype = 'mips' | |
300 | elif sys.byteorder == 'little': | |
301 | cputype = 'mipsel' | |
302 | else: | |
303 | raise ValueError("unknown byteorder: {}".format(sys.byteorder)) | |
304 | elif cputype == 'mips64': | |
305 | if sys.byteorder == 'big': | |
306 | cputype = 'mips64' | |
307 | elif sys.byteorder == 'little': | |
308 | cputype = 'mips64el' | |
309 | else: | |
310 | raise ValueError('unknown byteorder: {}'.format(sys.byteorder)) | |
311 | # only the n64 ABI is supported, indicate it | |
312 | ostype += 'abi64' | |
0531ce1d | 313 | elif cputype == 'sparc' or cputype == 'sparcv9' or cputype == 'sparc64': |
ea8adc8c XL |
314 | pass |
315 | else: | |
316 | err = "unknown cpu type: {}".format(cputype) | |
317 | sys.exit(err) | |
318 | ||
319 | return "{}-{}".format(cputype, ostype) | |
320 | ||
abe05a73 | 321 | |
8faf50e0 XL |
322 | @contextlib.contextmanager |
323 | def output(filepath): | |
324 | tmp = filepath + '.tmp' | |
325 | with open(tmp, 'w') as f: | |
326 | yield f | |
327 | try: | |
328 | os.remove(filepath) # PermissionError/OSError on Win32 if in use | |
329 | os.rename(tmp, filepath) | |
330 | except OSError: | |
331 | shutil.copy2(tmp, filepath) | |
332 | os.remove(tmp) | |
333 | ||
334 | ||
9e0c209e | 335 | class RustBuild(object): |
3b2f2976 XL |
336 | """Provide all the methods required to build Rust""" |
337 | def __init__(self): | |
338 | self.cargo_channel = '' | |
339 | self.date = '' | |
e1599b0c | 340 | self._download_url = '' |
3b2f2976 | 341 | self.rustc_channel = '' |
dfeec247 | 342 | self.rustfmt_channel = '' |
3b2f2976 | 343 | self.build = '' |
f9f354fc | 344 | self.build_dir = '' |
3b2f2976 | 345 | self.clean = False |
3b2f2976 | 346 | self.config_toml = '' |
83c7162d | 347 | self.rust_root = '' |
3b2f2976 XL |
348 | self.use_locked_deps = '' |
349 | self.use_vendored_sources = '' | |
350 | self.verbose = False | |
f9f354fc | 351 | self.git_version = None |
7cac9316 | 352 | |
a7813a04 | 353 | def download_stage0(self): |
3b2f2976 XL |
354 | """Fetch the build system for Rust, written in Rust |
355 | ||
356 | This method will build a cache directory, then it will fetch the | |
357 | tarball which has the stage0 compiler used to then bootstrap the Rust | |
358 | compiler itself. | |
cc61c64b | 359 | |
3b2f2976 XL |
360 | Each downloaded tarball is extracted, after that, the script |
361 | will move all the content to the right place. | |
362 | """ | |
363 | rustc_channel = self.rustc_channel | |
364 | cargo_channel = self.cargo_channel | |
dfeec247 | 365 | rustfmt_channel = self.rustfmt_channel |
7453a54e | 366 | |
60c5eb7d XL |
367 | def support_xz(): |
368 | try: | |
369 | with tempfile.NamedTemporaryFile(delete=False) as temp_file: | |
370 | temp_path = temp_file.name | |
74b04a01 | 371 | with tarfile.open(temp_path, "w:xz"): |
60c5eb7d XL |
372 | pass |
373 | return True | |
374 | except tarfile.CompressionError: | |
375 | return False | |
376 | ||
7453a54e | 377 | if self.rustc().startswith(self.bin_root()) and \ |
3b2f2976 XL |
378 | (not os.path.exists(self.rustc()) or |
379 | self.program_out_of_date(self.rustc_stamp())): | |
54a0048b SL |
380 | if os.path.exists(self.bin_root()): |
381 | shutil.rmtree(self.bin_root()) | |
60c5eb7d XL |
382 | tarball_suffix = '.tar.xz' if support_xz() else '.tar.gz' |
383 | filename = "rust-std-{}-{}{}".format( | |
384 | rustc_channel, self.build, tarball_suffix) | |
3b2f2976 | 385 | pattern = "rust-std-{}".format(self.build) |
60c5eb7d | 386 | self._download_stage0_helper(filename, pattern, tarball_suffix) |
7453a54e | 387 | |
60c5eb7d XL |
388 | filename = "rustc-{}-{}{}".format(rustc_channel, self.build, |
389 | tarball_suffix) | |
390 | self._download_stage0_helper(filename, "rustc", tarball_suffix) | |
3b2f2976 XL |
391 | self.fix_executable("{}/bin/rustc".format(self.bin_root())) |
392 | self.fix_executable("{}/bin/rustdoc".format(self.bin_root())) | |
8faf50e0 | 393 | with output(self.rustc_stamp()) as rust_stamp: |
3b2f2976 | 394 | rust_stamp.write(self.date) |
7453a54e | 395 | |
0531ce1d XL |
396 | # This is required so that we don't mix incompatible MinGW |
397 | # libraries/binaries that are included in rust-std with | |
398 | # the system MinGW ones. | |
399 | if "pc-windows-gnu" in self.build: | |
60c5eb7d XL |
400 | filename = "rust-mingw-{}-{}{}".format( |
401 | rustc_channel, self.build, tarball_suffix) | |
402 | self._download_stage0_helper(filename, "rust-mingw", tarball_suffix) | |
0531ce1d | 403 | |
7453a54e | 404 | if self.cargo().startswith(self.bin_root()) and \ |
3b2f2976 XL |
405 | (not os.path.exists(self.cargo()) or |
406 | self.program_out_of_date(self.cargo_stamp())): | |
60c5eb7d XL |
407 | tarball_suffix = '.tar.xz' if support_xz() else '.tar.gz' |
408 | filename = "cargo-{}-{}{}".format(cargo_channel, self.build, | |
409 | tarball_suffix) | |
410 | self._download_stage0_helper(filename, "cargo", tarball_suffix) | |
3b2f2976 | 411 | self.fix_executable("{}/bin/cargo".format(self.bin_root())) |
8faf50e0 | 412 | with output(self.cargo_stamp()) as cargo_stamp: |
3b2f2976 XL |
413 | cargo_stamp.write(self.date) |
414 | ||
dfeec247 XL |
415 | if self.rustfmt() and self.rustfmt().startswith(self.bin_root()) and ( |
416 | not os.path.exists(self.rustfmt()) | |
74b04a01 | 417 | or self.program_out_of_date(self.rustfmt_stamp(), self.rustfmt_channel) |
dfeec247 XL |
418 | ): |
419 | if rustfmt_channel: | |
420 | tarball_suffix = '.tar.xz' if support_xz() else '.tar.gz' | |
421 | [channel, date] = rustfmt_channel.split('-', 1) | |
422 | filename = "rustfmt-{}-{}{}".format(channel, self.build, tarball_suffix) | |
423 | self._download_stage0_helper(filename, "rustfmt-preview", tarball_suffix, date) | |
424 | self.fix_executable("{}/bin/rustfmt".format(self.bin_root())) | |
425 | self.fix_executable("{}/bin/cargo-fmt".format(self.bin_root())) | |
426 | with output(self.rustfmt_stamp()) as rustfmt_stamp: | |
74b04a01 | 427 | rustfmt_stamp.write(self.date + self.rustfmt_channel) |
dfeec247 XL |
428 | |
429 | def _download_stage0_helper(self, filename, pattern, tarball_suffix, date=None): | |
430 | if date is None: | |
431 | date = self.date | |
3b2f2976 | 432 | cache_dst = os.path.join(self.build_dir, "cache") |
dfeec247 | 433 | rustc_cache = os.path.join(cache_dst, date) |
3b2f2976 XL |
434 | if not os.path.exists(rustc_cache): |
435 | os.makedirs(rustc_cache) | |
436 | ||
dfeec247 | 437 | url = "{}/dist/{}".format(self._download_url, date) |
3b2f2976 XL |
438 | tarball = os.path.join(rustc_cache, filename) |
439 | if not os.path.exists(tarball): | |
440 | get("{}/{}".format(url, filename), tarball, verbose=self.verbose) | |
60c5eb7d | 441 | unpack(tarball, tarball_suffix, self.bin_root(), match=pattern, verbose=self.verbose) |
8bb4bdeb | 442 | |
3b2f2976 XL |
443 | @staticmethod |
444 | def fix_executable(fname): | |
445 | """Modifies the interpreter section of 'fname' to fix the dynamic linker | |
446 | ||
447 | This method is only required on NixOS and uses the PatchELF utility to | |
448 | change the dynamic linker of ELF executables. | |
449 | ||
450 | Please see https://nixos.org/patchelf.html for more information | |
451 | """ | |
8bb4bdeb XL |
452 | default_encoding = sys.getdefaultencoding() |
453 | try: | |
7cac9316 XL |
454 | ostype = subprocess.check_output( |
455 | ['uname', '-s']).strip().decode(default_encoding) | |
3b2f2976 | 456 | except subprocess.CalledProcessError: |
8bb4bdeb | 457 | return |
3b2f2976 XL |
458 | except OSError as reason: |
459 | if getattr(reason, 'winerror', None) is not None: | |
460 | return | |
461 | raise reason | |
8bb4bdeb XL |
462 | |
463 | if ostype != "Linux": | |
464 | return | |
465 | ||
466 | if not os.path.exists("/etc/NIXOS"): | |
467 | return | |
468 | if os.path.exists("/lib"): | |
469 | return | |
470 | ||
471 | # At this point we're pretty sure the user is running NixOS | |
041b39d2 XL |
472 | nix_os_msg = "info: you seem to be running NixOS. Attempting to patch" |
473 | print(nix_os_msg, fname) | |
8bb4bdeb XL |
474 | |
475 | try: | |
7cac9316 XL |
476 | interpreter = subprocess.check_output( |
477 | ["patchelf", "--print-interpreter", fname]) | |
8bb4bdeb | 478 | interpreter = interpreter.strip().decode(default_encoding) |
3b2f2976 XL |
479 | except subprocess.CalledProcessError as reason: |
480 | print("warning: failed to call patchelf:", reason) | |
8bb4bdeb XL |
481 | return |
482 | ||
483 | loader = interpreter.split("/")[-1] | |
484 | ||
485 | try: | |
7cac9316 XL |
486 | ldd_output = subprocess.check_output( |
487 | ['ldd', '/run/current-system/sw/bin/sh']) | |
8bb4bdeb | 488 | ldd_output = ldd_output.strip().decode(default_encoding) |
3b2f2976 XL |
489 | except subprocess.CalledProcessError as reason: |
490 | print("warning: unable to call ldd:", reason) | |
8bb4bdeb XL |
491 | return |
492 | ||
493 | for line in ldd_output.splitlines(): | |
494 | libname = line.split()[0] | |
495 | if libname.endswith(loader): | |
496 | loader_path = libname[:len(libname) - len(loader)] | |
497 | break | |
498 | else: | |
499 | print("warning: unable to find the path to the dynamic linker") | |
500 | return | |
501 | ||
502 | correct_interpreter = loader_path + loader | |
503 | ||
504 | try: | |
7cac9316 XL |
505 | subprocess.check_output( |
506 | ["patchelf", "--set-interpreter", correct_interpreter, fname]) | |
3b2f2976 XL |
507 | except subprocess.CalledProcessError as reason: |
508 | print("warning: failed to call patchelf:", reason) | |
8bb4bdeb XL |
509 | return |
510 | ||
7453a54e | 511 | def rustc_stamp(self): |
3b2f2976 XL |
512 | """Return the path for .rustc-stamp |
513 | ||
514 | >>> rb = RustBuild() | |
515 | >>> rb.build_dir = "build" | |
516 | >>> rb.rustc_stamp() == os.path.join("build", "stage0", ".rustc-stamp") | |
517 | True | |
518 | """ | |
7453a54e SL |
519 | return os.path.join(self.bin_root(), '.rustc-stamp') |
520 | ||
521 | def cargo_stamp(self): | |
3b2f2976 | 522 | """Return the path for .cargo-stamp |
7453a54e | 523 | |
3b2f2976 XL |
524 | >>> rb = RustBuild() |
525 | >>> rb.build_dir = "build" | |
526 | >>> rb.cargo_stamp() == os.path.join("build", "stage0", ".cargo-stamp") | |
527 | True | |
528 | """ | |
529 | return os.path.join(self.bin_root(), '.cargo-stamp') | |
7453a54e | 530 | |
dfeec247 XL |
531 | def rustfmt_stamp(self): |
532 | """Return the path for .rustfmt-stamp | |
533 | ||
534 | >>> rb = RustBuild() | |
535 | >>> rb.build_dir = "build" | |
536 | >>> rb.rustfmt_stamp() == os.path.join("build", "stage0", ".rustfmt-stamp") | |
537 | True | |
538 | """ | |
539 | return os.path.join(self.bin_root(), '.rustfmt-stamp') | |
540 | ||
74b04a01 | 541 | def program_out_of_date(self, stamp_path, extra=""): |
3b2f2976 XL |
542 | """Check if the given program stamp is out of date""" |
543 | if not os.path.exists(stamp_path) or self.clean: | |
7453a54e | 544 | return True |
3b2f2976 | 545 | with open(stamp_path, 'r') as stamp: |
74b04a01 | 546 | return (self.date + extra) != stamp.read() |
7453a54e SL |
547 | |
548 | def bin_root(self): | |
3b2f2976 XL |
549 | """Return the binary root directory |
550 | ||
551 | >>> rb = RustBuild() | |
552 | >>> rb.build_dir = "build" | |
553 | >>> rb.bin_root() == os.path.join("build", "stage0") | |
554 | True | |
555 | ||
556 | When the 'build' property is given should be a nested directory: | |
557 | ||
558 | >>> rb.build = "devel" | |
559 | >>> rb.bin_root() == os.path.join("build", "devel", "stage0") | |
560 | True | |
561 | """ | |
7453a54e SL |
562 | return os.path.join(self.build_dir, self.build, "stage0") |
563 | ||
94b46f34 | 564 | def get_toml(self, key, section=None): |
3b2f2976 XL |
565 | """Returns the value of the given key in config.toml, otherwise returns None |
566 | ||
567 | >>> rb = RustBuild() | |
568 | >>> rb.config_toml = 'key1 = "value1"\\nkey2 = "value2"' | |
569 | >>> rb.get_toml("key2") | |
570 | 'value2' | |
571 | ||
572 | If the key does not exists, the result is None: | |
573 | ||
abe05a73 | 574 | >>> rb.get_toml("key3") is None |
3b2f2976 | 575 | True |
94b46f34 XL |
576 | |
577 | Optionally also matches the section the key appears in | |
578 | ||
579 | >>> rb.config_toml = '[a]\\nkey = "value1"\\n[b]\\nkey = "value2"' | |
580 | >>> rb.get_toml('key', 'a') | |
581 | 'value1' | |
582 | >>> rb.get_toml('key', 'b') | |
583 | 'value2' | |
584 | >>> rb.get_toml('key', 'c') is None | |
585 | True | |
e1599b0c XL |
586 | |
587 | >>> rb.config_toml = 'key1 = true' | |
588 | >>> rb.get_toml("key1") | |
589 | 'true' | |
3b2f2976 | 590 | """ |
94b46f34 XL |
591 | |
592 | cur_section = None | |
7453a54e | 593 | for line in self.config_toml.splitlines(): |
94b46f34 XL |
594 | section_match = re.match(r'^\s*\[(.*)\]\s*$', line) |
595 | if section_match is not None: | |
596 | cur_section = section_match.group(1) | |
597 | ||
7cac9316 XL |
598 | match = re.match(r'^{}\s*=(.*)$'.format(key), line) |
599 | if match is not None: | |
600 | value = match.group(1) | |
94b46f34 XL |
601 | if section is None or section == cur_section: |
602 | return self.get_string(value) or value.strip() | |
7453a54e SL |
603 | return None |
604 | ||
7453a54e | 605 | def cargo(self): |
3b2f2976 XL |
606 | """Return config path for cargo""" |
607 | return self.program_config('cargo') | |
7453a54e SL |
608 | |
609 | def rustc(self): | |
3b2f2976 XL |
610 | """Return config path for rustc""" |
611 | return self.program_config('rustc') | |
612 | ||
dfeec247 XL |
613 | def rustfmt(self): |
614 | """Return config path for rustfmt""" | |
615 | if not self.rustfmt_channel: | |
616 | return None | |
617 | return self.program_config('rustfmt') | |
618 | ||
3b2f2976 XL |
619 | def program_config(self, program): |
620 | """Return config path for the given program | |
621 | ||
622 | >>> rb = RustBuild() | |
623 | >>> rb.config_toml = 'rustc = "rustc"\\n' | |
3b2f2976 XL |
624 | >>> rb.program_config('rustc') |
625 | 'rustc' | |
3b2f2976 | 626 | >>> rb.config_toml = '' |
3b2f2976 XL |
627 | >>> cargo_path = rb.program_config('cargo') |
628 | >>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(), | |
629 | ... "bin", "cargo") | |
630 | True | |
631 | """ | |
632 | config = self.get_toml(program) | |
7453a54e | 633 | if config: |
abe05a73 | 634 | return os.path.expanduser(config) |
3b2f2976 XL |
635 | return os.path.join(self.bin_root(), "bin", "{}{}".format( |
636 | program, self.exe_suffix())) | |
637 | ||
638 | @staticmethod | |
639 | def get_string(line): | |
640 | """Return the value between double quotes | |
641 | ||
642 | >>> RustBuild.get_string(' "devel" ') | |
643 | 'devel' | |
e1599b0c XL |
644 | >>> RustBuild.get_string(" 'devel' ") |
645 | 'devel' | |
646 | >>> RustBuild.get_string('devel') is None | |
647 | True | |
648 | >>> RustBuild.get_string(' "devel ') | |
649 | '' | |
3b2f2976 | 650 | """ |
7453a54e | 651 | start = line.find('"') |
ea8adc8c XL |
652 | if start != -1: |
653 | end = start + 1 + line[start + 1:].find('"') | |
654 | return line[start + 1:end] | |
655 | start = line.find('\'') | |
656 | if start != -1: | |
657 | end = start + 1 + line[start + 1:].find('\'') | |
658 | return line[start + 1:end] | |
659 | return None | |
7453a54e | 660 | |
3b2f2976 XL |
661 | @staticmethod |
662 | def exe_suffix(): | |
663 | """Return a suffix for executables""" | |
7453a54e SL |
664 | if sys.platform == 'win32': |
665 | return '.exe' | |
041b39d2 | 666 | return '' |
7453a54e | 667 | |
476ff2be | 668 | def bootstrap_binary(self): |
0bf4aa26 | 669 | """Return the path of the bootstrap binary |
3b2f2976 XL |
670 | |
671 | >>> rb = RustBuild() | |
672 | >>> rb.build_dir = "build" | |
673 | >>> rb.bootstrap_binary() == os.path.join("build", "bootstrap", | |
674 | ... "debug", "bootstrap") | |
675 | True | |
676 | """ | |
677 | return os.path.join(self.build_dir, "bootstrap", "debug", "bootstrap") | |
476ff2be | 678 | |
7453a54e | 679 | def build_bootstrap(self): |
3b2f2976 | 680 | """Build bootstrap""" |
3157f602 XL |
681 | build_dir = os.path.join(self.build_dir, "bootstrap") |
682 | if self.clean and os.path.exists(build_dir): | |
683 | shutil.rmtree(build_dir) | |
7453a54e | 684 | env = os.environ.copy() |
ba9703b0 XL |
685 | # `CARGO_BUILD_TARGET` breaks bootstrap build. |
686 | # See also: <https://github.com/rust-lang/rust/issues/70208>. | |
687 | if "CARGO_BUILD_TARGET" in env: | |
688 | del env["CARGO_BUILD_TARGET"] | |
041b39d2 | 689 | env["RUSTC_BOOTSTRAP"] = '1' |
3157f602 | 690 | env["CARGO_TARGET_DIR"] = build_dir |
7453a54e | 691 | env["RUSTC"] = self.rustc() |
8bb4bdeb | 692 | env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \ |
7cac9316 XL |
693 | (os.pathsep + env["LD_LIBRARY_PATH"]) \ |
694 | if "LD_LIBRARY_PATH" in env else "" | |
8bb4bdeb | 695 | env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \ |
7cac9316 XL |
696 | (os.pathsep + env["DYLD_LIBRARY_PATH"]) \ |
697 | if "DYLD_LIBRARY_PATH" in env else "" | |
698 | env["LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \ | |
699 | (os.pathsep + env["LIBRARY_PATH"]) \ | |
700 | if "LIBRARY_PATH" in env else "" | |
60c5eb7d XL |
701 | # preserve existing RUSTFLAGS |
702 | env.setdefault("RUSTFLAGS", "") | |
703 | env["RUSTFLAGS"] += " -Cdebuginfo=2" | |
94b46f34 XL |
704 | |
705 | build_section = "target.{}".format(self.build_triple()) | |
706 | target_features = [] | |
707 | if self.get_toml("crt-static", build_section) == "true": | |
708 | target_features += ["+crt-static"] | |
709 | elif self.get_toml("crt-static", build_section) == "false": | |
710 | target_features += ["-crt-static"] | |
711 | if target_features: | |
60c5eb7d | 712 | env["RUSTFLAGS"] += " -C target-feature=" + (",".join(target_features)) |
13cf67c4 XL |
713 | target_linker = self.get_toml("linker", build_section) |
714 | if target_linker is not None: | |
60c5eb7d XL |
715 | env["RUSTFLAGS"] += " -C linker=" + target_linker |
716 | env["RUSTFLAGS"] += " -Wrust_2018_idioms -Wunused_lifetimes" | |
e1599b0c | 717 | if self.get_toml("deny-warnings", "rust") != "false": |
60c5eb7d | 718 | env["RUSTFLAGS"] += " -Dwarnings" |
94b46f34 | 719 | |
7453a54e | 720 | env["PATH"] = os.path.join(self.bin_root(), "bin") + \ |
7cac9316 | 721 | os.pathsep + env["PATH"] |
32a655c1 | 722 | if not os.path.isfile(self.cargo()): |
3b2f2976 XL |
723 | raise Exception("no cargo executable found at `{}`".format( |
724 | self.cargo())) | |
476ff2be SL |
725 | args = [self.cargo(), "build", "--manifest-path", |
726 | os.path.join(self.rust_root, "src/bootstrap/Cargo.toml")] | |
0531ce1d | 727 | for _ in range(1, self.verbose): |
7cac9316 | 728 | args.append("--verbose") |
8bb4bdeb XL |
729 | if self.use_locked_deps: |
730 | args.append("--locked") | |
476ff2be SL |
731 | if self.use_vendored_sources: |
732 | args.append("--frozen") | |
7cac9316 | 733 | run(args, env=env, verbose=self.verbose) |
7453a54e SL |
734 | |
735 | def build_triple(self): | |
3b2f2976 | 736 | """Build triple as in LLVM""" |
7453a54e SL |
737 | config = self.get_toml('build') |
738 | if config: | |
739 | return config | |
ea8adc8c | 740 | return default_build_triple() |
7453a54e | 741 | |
0531ce1d XL |
742 | def check_submodule(self, module, slow_submodules): |
743 | if not slow_submodules: | |
744 | checked_out = subprocess.Popen(["git", "rev-parse", "HEAD"], | |
745 | cwd=os.path.join(self.rust_root, module), | |
746 | stdout=subprocess.PIPE) | |
747 | return checked_out | |
748 | else: | |
749 | return None | |
750 | ||
751 | def update_submodule(self, module, checked_out, recorded_submodules): | |
752 | module_path = os.path.join(self.rust_root, module) | |
753 | ||
e1599b0c | 754 | if checked_out is not None: |
0531ce1d XL |
755 | default_encoding = sys.getdefaultencoding() |
756 | checked_out = checked_out.communicate()[0].decode(default_encoding).strip() | |
757 | if recorded_submodules[module] == checked_out: | |
758 | return | |
759 | ||
760 | print("Updating submodule", module) | |
761 | ||
762 | run(["git", "submodule", "-q", "sync", module], | |
763 | cwd=self.rust_root, verbose=self.verbose) | |
f9f354fc XL |
764 | |
765 | update_args = ["git", "submodule", "update", "--init", "--recursive"] | |
766 | if self.git_version >= distutils.version.LooseVersion("2.11.0"): | |
767 | update_args.append("--progress") | |
768 | update_args.append(module) | |
769 | run(update_args, cwd=self.rust_root, verbose=self.verbose, exception=True) | |
770 | ||
0531ce1d XL |
771 | run(["git", "reset", "-q", "--hard"], |
772 | cwd=module_path, verbose=self.verbose) | |
773 | run(["git", "clean", "-qdfx"], | |
774 | cwd=module_path, verbose=self.verbose) | |
775 | ||
7cac9316 | 776 | def update_submodules(self): |
3b2f2976 | 777 | """Update submodules""" |
7cac9316 | 778 | if (not os.path.exists(os.path.join(self.rust_root, ".git"))) or \ |
ea8adc8c | 779 | self.get_toml('submodules') == "false": |
7cac9316 | 780 | return |
e1599b0c | 781 | |
f9f354fc XL |
782 | default_encoding = sys.getdefaultencoding() |
783 | ||
784 | # check the existence and version of 'git' command | |
785 | git_version_str = require(['git', '--version']).split()[2].decode(default_encoding) | |
786 | self.git_version = distutils.version.LooseVersion(git_version_str) | |
e1599b0c | 787 | |
0531ce1d XL |
788 | slow_submodules = self.get_toml('fast-submodules') == "false" |
789 | start_time = time() | |
790 | if slow_submodules: | |
791 | print('Unconditionally updating all submodules') | |
792 | else: | |
793 | print('Updating only changed submodules') | |
7cac9316 | 794 | default_encoding = sys.getdefaultencoding() |
7cac9316 | 795 | submodules = [s.split(' ', 1)[1] for s in subprocess.check_output( |
041b39d2 XL |
796 | ["git", "config", "--file", |
797 | os.path.join(self.rust_root, ".gitmodules"), | |
7cac9316 XL |
798 | "--get-regexp", "path"] |
799 | ).decode(default_encoding).splitlines()] | |
2c00a5a8 | 800 | filtered_submodules = [] |
0531ce1d | 801 | submodules_names = [] |
2c00a5a8 | 802 | for module in submodules: |
9fa01778 XL |
803 | if module.endswith("llvm-project"): |
804 | if self.get_toml('llvm-config') and self.get_toml('lld') != 'true': | |
2c00a5a8 | 805 | continue |
0531ce1d XL |
806 | check = self.check_submodule(module, slow_submodules) |
807 | filtered_submodules.append((module, check)) | |
808 | submodules_names.append(module) | |
809 | recorded = subprocess.Popen(["git", "ls-tree", "HEAD"] + submodules_names, | |
810 | cwd=self.rust_root, stdout=subprocess.PIPE) | |
811 | recorded = recorded.communicate()[0].decode(default_encoding).strip().splitlines() | |
812 | recorded_submodules = {} | |
813 | for data in recorded: | |
814 | data = data.split() | |
815 | recorded_submodules[data[3]] = data[2] | |
816 | for module in filtered_submodules: | |
817 | self.update_submodule(module[0], module[1], recorded_submodules) | |
818 | print("Submodules updated in %.2f seconds" % (time() - start_time)) | |
7cac9316 | 819 | |
e1599b0c XL |
820 | def set_normal_environment(self): |
821 | """Set download URL for normal environment""" | |
822 | if 'RUSTUP_DIST_SERVER' in os.environ: | |
823 | self._download_url = os.environ['RUSTUP_DIST_SERVER'] | |
824 | else: | |
825 | self._download_url = 'https://static.rust-lang.org' | |
826 | ||
3b2f2976 XL |
827 | def set_dev_environment(self): |
828 | """Set download URL for development environment""" | |
e1599b0c XL |
829 | if 'RUSTUP_DEV_DIST_SERVER' in os.environ: |
830 | self._download_url = os.environ['RUSTUP_DEV_DIST_SERVER'] | |
831 | else: | |
832 | self._download_url = 'https://dev-static.rust-lang.org' | |
3b2f2976 | 833 | |
416331ca XL |
834 | def check_vendored_status(self): |
835 | """Check that vendoring is configured properly""" | |
836 | vendor_dir = os.path.join(self.rust_root, 'vendor') | |
837 | if 'SUDO_USER' in os.environ and not self.use_vendored_sources: | |
838 | if os.environ.get('USER') != os.environ['SUDO_USER']: | |
839 | self.use_vendored_sources = True | |
840 | print('info: looks like you are running this command under `sudo`') | |
841 | print(' and so in order to preserve your $HOME this will now') | |
842 | print(' use vendored sources by default.') | |
843 | if not os.path.exists(vendor_dir): | |
844 | print('error: vendoring required, but vendor directory does not exist.') | |
845 | print(' Run `cargo vendor` without sudo to initialize the ' | |
74b04a01 | 846 | 'vendor directory.') |
416331ca XL |
847 | raise Exception("{} not found".format(vendor_dir)) |
848 | ||
849 | if self.use_vendored_sources: | |
850 | if not os.path.exists('.cargo'): | |
851 | os.makedirs('.cargo') | |
852 | with output('.cargo/config') as cargo_config: | |
853 | cargo_config.write( | |
854 | "[source.crates-io]\n" | |
855 | "replace-with = 'vendored-sources'\n" | |
856 | "registry = 'https://example.com'\n" | |
857 | "\n" | |
858 | "[source.vendored-sources]\n" | |
859 | "directory = '{}/vendor'\n" | |
74b04a01 | 860 | .format(self.rust_root)) |
416331ca XL |
861 | else: |
862 | if os.path.exists('.cargo'): | |
863 | shutil.rmtree('.cargo') | |
864 | ||
865 | def ensure_vendored(self): | |
866 | """Ensure that the vendored sources are available if needed""" | |
867 | vendor_dir = os.path.join(self.rust_root, 'vendor') | |
868 | # Note that this does not handle updating the vendored dependencies if | |
869 | # the rust git repository is updated. Normal development usually does | |
870 | # not use vendoring, so hopefully this isn't too much of a problem. | |
871 | if self.use_vendored_sources and not os.path.exists(vendor_dir): | |
f035d41b | 872 | run([self.cargo(), "vendor", "--sync=./src/tools/rust-analyzer/Cargo.toml"], |
416331ca XL |
873 | verbose=self.verbose, cwd=self.rust_root) |
874 | ||
7cac9316 | 875 | |
0531ce1d | 876 | def bootstrap(help_triggered): |
3b2f2976 | 877 | """Configure, fetch, build and run the initial bootstrap""" |
0531ce1d XL |
878 | |
879 | # If the user is asking for help, let them know that the whole download-and-build | |
880 | # process has to happen before anything is printed out. | |
881 | if help_triggered: | |
882 | print("info: Downloading and building bootstrap before processing --help") | |
883 | print(" command. See src/bootstrap/README.md for help with common") | |
884 | print(" commands.") | |
885 | ||
a7813a04 XL |
886 | parser = argparse.ArgumentParser(description='Build rust') |
887 | parser.add_argument('--config') | |
3b2f2976 | 888 | parser.add_argument('--build') |
83c7162d | 889 | parser.add_argument('--src') |
3157f602 | 890 | parser.add_argument('--clean', action='store_true') |
0531ce1d | 891 | parser.add_argument('-v', '--verbose', action='count', default=0) |
a7813a04 | 892 | |
5bcae85e | 893 | args = [a for a in sys.argv if a != '-h' and a != '--help'] |
a7813a04 XL |
894 | args, _ = parser.parse_known_args(args) |
895 | ||
896 | # Configure initial bootstrap | |
3b2f2976 | 897 | build = RustBuild() |
83c7162d | 898 | build.rust_root = args.src or os.path.abspath(os.path.join(__file__, '../../..')) |
3b2f2976 XL |
899 | build.verbose = args.verbose |
900 | build.clean = args.clean | |
a7813a04 | 901 | |
f035d41b XL |
902 | # Read from `RUST_BOOTSTRAP_CONFIG`, then `--config`, then fallback to `config.toml` (if it |
903 | # exists). | |
904 | toml_path = os.getenv('RUST_BOOTSTRAP_CONFIG') or args.config | |
905 | if not toml_path and os.path.exists('config.toml'): | |
906 | toml_path = 'config.toml' | |
907 | ||
908 | if toml_path: | |
f9f354fc XL |
909 | if not os.path.exists(toml_path): |
910 | toml_path = os.path.join(build.rust_root, toml_path) | |
911 | ||
912 | with open(toml_path) as config: | |
3b2f2976 | 913 | build.config_toml = config.read() |
a7813a04 | 914 | |
e1599b0c XL |
915 | config_verbose = build.get_toml('verbose', 'build') |
916 | if config_verbose is not None: | |
917 | build.verbose = max(build.verbose, int(config_verbose)) | |
7cac9316 | 918 | |
e1599b0c | 919 | build.use_vendored_sources = build.get_toml('vendor', 'build') == 'true' |
476ff2be | 920 | |
e1599b0c | 921 | build.use_locked_deps = build.get_toml('locked-deps', 'build') == 'true' |
8bb4bdeb | 922 | |
416331ca | 923 | build.check_vendored_status() |
476ff2be | 924 | |
f9f354fc XL |
925 | build_dir = build.get_toml('build-dir', 'build') or 'build' |
926 | build.build_dir = os.path.abspath(build_dir.replace("$ROOT", build.rust_root)) | |
927 | ||
3b2f2976 XL |
928 | data = stage0_data(build.rust_root) |
929 | build.date = data['date'] | |
930 | build.rustc_channel = data['rustc'] | |
931 | build.cargo_channel = data['cargo'] | |
932 | ||
dfeec247 XL |
933 | if "rustfmt" in data: |
934 | build.rustfmt_channel = data['rustfmt'] | |
935 | ||
7cac9316 | 936 | if 'dev' in data: |
3b2f2976 | 937 | build.set_dev_environment() |
e1599b0c XL |
938 | else: |
939 | build.set_normal_environment() | |
7cac9316 | 940 | |
83c7162d | 941 | build.update_submodules() |
a7813a04 XL |
942 | |
943 | # Fetch/build the bootstrap | |
3b2f2976 XL |
944 | build.build = args.build or build.build_triple() |
945 | build.download_stage0() | |
a7813a04 | 946 | sys.stdout.flush() |
416331ca | 947 | build.ensure_vendored() |
3b2f2976 | 948 | build.build_bootstrap() |
a7813a04 XL |
949 | sys.stdout.flush() |
950 | ||
951 | # Run the bootstrap | |
3b2f2976 | 952 | args = [build.bootstrap_binary()] |
a7813a04 XL |
953 | args.extend(sys.argv[1:]) |
954 | env = os.environ.copy() | |
3b2f2976 XL |
955 | env["BUILD"] = build.build |
956 | env["SRC"] = build.rust_root | |
a7813a04 | 957 | env["BOOTSTRAP_PARENT_ID"] = str(os.getpid()) |
041b39d2 | 958 | env["BOOTSTRAP_PYTHON"] = sys.executable |
83c7162d | 959 | env["BUILD_DIR"] = build.build_dir |
8faf50e0 XL |
960 | env["RUSTC_BOOTSTRAP"] = '1' |
961 | env["CARGO"] = build.cargo() | |
962 | env["RUSTC"] = build.rustc() | |
f035d41b XL |
963 | if toml_path: |
964 | env["BOOTSTRAP_CONFIG"] = toml_path | |
dfeec247 XL |
965 | if build.rustfmt(): |
966 | env["RUSTFMT"] = build.rustfmt() | |
3b2f2976 | 967 | run(args, env=env, verbose=build.verbose) |
7cac9316 | 968 | |
a7813a04 | 969 | |
8bb4bdeb | 970 | def main(): |
3b2f2976 | 971 | """Entry point for the bootstrap process""" |
8bb4bdeb | 972 | start_time = time() |
0bf4aa26 XL |
973 | |
974 | # x.py help <cmd> ... | |
975 | if len(sys.argv) > 1 and sys.argv[1] == 'help': | |
532ac7d7 | 976 | sys.argv = [sys.argv[0], '-h'] + sys.argv[2:] |
0bf4aa26 | 977 | |
7cac9316 XL |
978 | help_triggered = ( |
979 | '-h' in sys.argv) or ('--help' in sys.argv) or (len(sys.argv) == 1) | |
8bb4bdeb | 980 | try: |
0531ce1d | 981 | bootstrap(help_triggered) |
cc61c64b | 982 | if not help_triggered: |
3b2f2976 XL |
983 | print("Build completed successfully in {}".format( |
984 | format_build_time(time() - start_time))) | |
985 | except (SystemExit, KeyboardInterrupt) as error: | |
986 | if hasattr(error, 'code') and isinstance(error.code, int): | |
987 | exit_code = error.code | |
8bb4bdeb XL |
988 | else: |
989 | exit_code = 1 | |
3b2f2976 | 990 | print(error) |
cc61c64b | 991 | if not help_triggered: |
3b2f2976 XL |
992 | print("Build completed unsuccessfully in {}".format( |
993 | format_build_time(time() - start_time))) | |
8bb4bdeb | 994 | sys.exit(exit_code) |
3157f602 | 995 | |
041b39d2 | 996 | |
a7813a04 XL |
997 | if __name__ == '__main__': |
998 | main() |