1 from __future__ import absolute_import, division, print_function
17 def get(url, path, verbose=False):
19 sha_url = url + suffix
20 with tempfile.NamedTemporaryFile(delete=False) as temp_file:
21 temp_path = temp_file.name
22 with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as sha_file:
23 sha_path = sha_file.name
26 download(sha_path, sha_url, False, verbose)
27 if os.path.exists(path):
28 if verify(path, sha_path, False):
30 print("using already-download file", path)
34 print("ignoring already-download file",
35 path, "due to failed verification")
37 download(temp_path, url, True, verbose)
38 if not verify(temp_path, sha_path, verbose):
39 raise RuntimeError("failed verification")
41 print("moving {} to {}".format(temp_path, path))
42 shutil.move(temp_path, path)
44 delete_if_present(sha_path, verbose)
45 delete_if_present(temp_path, verbose)
48 def delete_if_present(path, verbose):
49 """Remove the given file if present"""
50 if os.path.isfile(path):
52 print("removing", path)
56 def download(path, url, probably_big, verbose):
59 _download(path, url, probably_big, verbose, True)
62 print("\nspurious failure, trying again")
63 _download(path, url, probably_big, verbose, False)
66 def _download(path, url, probably_big, verbose, exception):
67 if probably_big or verbose:
68 print("downloading {}".format(url))
69 # see http://serverfault.com/questions/301128/how-to-download
70 if sys.platform == 'win32':
71 run(["PowerShell.exe", "/nologo", "-Command",
72 "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;",
73 "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')".format(url, path)],
77 if probably_big or verbose:
82 "-y", "30", "-Y", "10", # timeout if speed is < 10 bytes/sec for > 30 seconds
83 "--connect-timeout", "30", # timeout if cannot connect within 30 seconds
84 "--retry", "3", "-Sf", "-o", path, url],
89 def verify(path, sha_path, verbose):
90 """Check if the sha256 sum of the given path is valid"""
92 print("verifying", path)
93 with open(path, "rb") as source:
94 found = hashlib.sha256(source.read()).hexdigest()
95 with open(sha_path, "r") as sha256sum:
96 expected = sha256sum.readline().split()[0]
97 verified = found == expected
99 print("invalid checksum:\n"
101 " expected: {}".format(found, expected))
105 def unpack(tarball, tarball_suffix, dst, verbose=False, match=None):
106 """Unpack the given tarball file"""
107 print("extracting", tarball)
108 fname = os.path.basename(tarball).replace(tarball_suffix, "")
109 with contextlib.closing(tarfile.open(tarball)) as tar:
110 for member in tar.getnames():
111 if "/" not in member:
113 name = member.replace(fname + "/", "", 1)
114 if match is not None and not name.startswith(match):
116 name = name[len(match) + 1:]
118 dst_path = os.path.join(dst, name)
120 print(" extracting", member)
121 tar.extract(member, dst)
122 src_path = os.path.join(dst, member)
123 if os.path.isdir(src_path) and os.path.exists(dst_path):
125 shutil.move(src_path, dst_path)
126 shutil.rmtree(os.path.join(dst, fname))
129 def run(args, verbose=False, exception=False, **kwargs):
130 """Run a child program in a new process"""
132 print("running: " + ' '.join(args))
134 # Use Popen here instead of call() as it apparently allows powershell on
135 # Windows to not lock up waiting for input presumably.
136 ret = subprocess.Popen(args, **kwargs)
139 err = "failed to run: " + ' '.join(args)
140 if verbose or exception:
141 raise RuntimeError(err)
145 def stage0_data(rust_root):
146 """Build a dictionary from stage0.txt"""
147 nightlies = os.path.join(rust_root, "src/stage0.txt")
148 with open(nightlies, 'r') as nightlies:
149 lines = [line.rstrip() for line in nightlies
150 if not line.startswith("#")]
151 return dict([line.split(": ", 1) for line in lines if line])
154 def format_build_time(duration):
155 """Return a nicer format for build time
157 >>> format_build_time('300')
160 return str(datetime.timedelta(seconds=int(duration)))
163 def default_build_triple():
164 """Build triple as in LLVM"""
165 default_encoding = sys.getdefaultencoding()
167 ostype = subprocess.check_output(
168 ['uname', '-s']).strip().decode(default_encoding)
169 cputype = subprocess.check_output(
170 ['uname', '-m']).strip().decode(default_encoding)
171 except (subprocess.CalledProcessError, OSError):
172 if sys.platform == 'win32':
173 return 'x86_64-pc-windows-msvc'
174 err = "uname not found"
177 # The goal here is to come up with the same triple as LLVM would,
178 # at least for the subset of platforms we're willing to target.
180 'Darwin': 'apple-darwin',
181 'DragonFly': 'unknown-dragonfly',
182 'FreeBSD': 'unknown-freebsd',
183 'Haiku': 'unknown-haiku',
184 'NetBSD': 'unknown-netbsd',
185 'OpenBSD': 'unknown-openbsd'
188 # Consider the direct transformation first and then the special cases
189 if ostype in ostype_mapper:
190 ostype = ostype_mapper[ostype]
191 elif ostype == 'Linux':
192 os_from_sp = subprocess.check_output(
193 ['uname', '-o']).strip().decode(default_encoding)
194 if os_from_sp == 'Android':
195 ostype = 'linux-android'
197 ostype = 'unknown-linux-gnu'
198 elif ostype == 'SunOS':
199 ostype = 'sun-solaris'
200 # On Solaris, uname -m will return a machine classification instead
201 # of a cpu type, so uname -p is recommended instead. However, the
202 # output from that option is too generic for our purposes (it will
203 # always emit 'i386' on x86/amd64 systems). As such, isainfo -k
204 # must be used instead.
206 cputype = subprocess.check_output(
207 ['isainfo', '-k']).strip().decode(default_encoding)
208 except (subprocess.CalledProcessError, OSError):
209 err = "isainfo not found"
211 elif ostype.startswith('MINGW'):
212 # msys' `uname` does not print gcc configuration, but prints msys
213 # configuration. so we cannot believe `uname -m`:
214 # msys1 is always i686 and msys2 is always x86_64.
215 # instead, msys defines $MSYSTEM which is MINGW32 on i686 and
217 ostype = 'pc-windows-gnu'
219 if os.environ.get('MSYSTEM') == 'MINGW64':
221 elif ostype.startswith('MSYS'):
222 ostype = 'pc-windows-gnu'
223 elif ostype.startswith('CYGWIN_NT'):
225 if ostype.endswith('WOW64'):
227 ostype = 'pc-windows-gnu'
229 err = "unknown OS type: {}".format(ostype)
232 if cputype == 'powerpc' and ostype == 'unknown-freebsd':
233 cputype = subprocess.check_output(
234 ['uname', '-p']).strip().decode(default_encoding)
237 'aarch64': 'aarch64',
244 'powerpc': 'powerpc',
245 'powerpc64': 'powerpc64',
246 'powerpc64le': 'powerpc64le',
248 'ppc64': 'powerpc64',
249 'ppc64le': 'powerpc64le',
257 # Consider the direct transformation first and then the special cases
258 if cputype in cputype_mapper:
259 cputype = cputype_mapper[cputype]
260 elif cputype in {'xscale', 'arm'}:
262 if ostype == 'linux-android':
263 ostype = 'linux-androideabi'
264 elif ostype == 'unknown-freebsd':
265 cputype = subprocess.check_output(
266 ['uname', '-p']).strip().decode(default_encoding)
267 ostype = 'unknown-freebsd'
268 elif cputype == 'armv6l':
270 if ostype == 'linux-android':
271 ostype = 'linux-androideabi'
274 elif cputype in {'armv7l', 'armv8l'}:
276 if ostype == 'linux-android':
277 ostype = 'linux-androideabi'
280 elif cputype == 'mips':
281 if sys.byteorder == 'big':
283 elif sys.byteorder == 'little':
286 raise ValueError("unknown byteorder: {}".format(sys.byteorder))
287 elif cputype == 'mips64':
288 if sys.byteorder == 'big':
290 elif sys.byteorder == 'little':
293 raise ValueError('unknown byteorder: {}'.format(sys.byteorder))
294 # only the n64 ABI is supported, indicate it
296 elif cputype == 'sparc' or cputype == 'sparcv9' or cputype == 'sparc64':
299 err = "unknown cpu type: {}".format(cputype)
302 return "{}-{}".format(cputype, ostype)
305 @contextlib.contextmanager
306 def output(filepath):
307 tmp = filepath + '.tmp'
308 with open(tmp, 'w') as f:
311 os.remove(filepath) # PermissionError/OSError on Win32 if in use
312 os.rename(tmp, filepath)
314 shutil.copy2(tmp, filepath)
318 class RustBuild(object):
319 """Provide all the methods required to build Rust"""
321 self.cargo_channel = ''
323 self._download_url = ''
324 self.rustc_channel = ''
326 self.build_dir = os.path.join(os.getcwd(), "build")
328 self.config_toml = ''
330 self.use_locked_deps = ''
331 self.use_vendored_sources = ''
335 def download_stage0(self):
336 """Fetch the build system for Rust, written in Rust
338 This method will build a cache directory, then it will fetch the
339 tarball which has the stage0 compiler used to then bootstrap the Rust
342 Each downloaded tarball is extracted, after that, the script
343 will move all the content to the right place.
345 rustc_channel = self.rustc_channel
346 cargo_channel = self.cargo_channel
350 with tempfile.NamedTemporaryFile(delete=False) as temp_file:
351 temp_path = temp_file.name
352 with tarfile.open(temp_path, "w:xz") as tar:
355 except tarfile.CompressionError:
358 if self.rustc().startswith(self.bin_root()) and \
359 (not os.path.exists(self.rustc()) or
360 self.program_out_of_date(self.rustc_stamp())):
361 if os.path.exists(self.bin_root()):
362 shutil.rmtree(self.bin_root())
363 tarball_suffix = '.tar.xz' if support_xz() else '.tar.gz'
364 filename = "rust-std-{}-{}{}".format(
365 rustc_channel, self.build, tarball_suffix)
366 pattern = "rust-std-{}".format(self.build)
367 self._download_stage0_helper(filename, pattern, tarball_suffix)
369 filename = "rustc-{}-{}{}".format(rustc_channel, self.build,
371 self._download_stage0_helper(filename, "rustc", tarball_suffix)
372 self.fix_executable("{}/bin/rustc".format(self.bin_root()))
373 self.fix_executable("{}/bin/rustdoc".format(self.bin_root()))
374 with output(self.rustc_stamp()) as rust_stamp:
375 rust_stamp.write(self.date)
377 # This is required so that we don't mix incompatible MinGW
378 # libraries/binaries that are included in rust-std with
379 # the system MinGW ones.
380 if "pc-windows-gnu" in self.build:
381 filename = "rust-mingw-{}-{}{}".format(
382 rustc_channel, self.build, tarball_suffix)
383 self._download_stage0_helper(filename, "rust-mingw", tarball_suffix)
385 if self.cargo().startswith(self.bin_root()) and \
386 (not os.path.exists(self.cargo()) or
387 self.program_out_of_date(self.cargo_stamp())):
388 tarball_suffix = '.tar.xz' if support_xz() else '.tar.gz'
389 filename = "cargo-{}-{}{}".format(cargo_channel, self.build,
391 self._download_stage0_helper(filename, "cargo", tarball_suffix)
392 self.fix_executable("{}/bin/cargo".format(self.bin_root()))
393 with output(self.cargo_stamp()) as cargo_stamp:
394 cargo_stamp.write(self.date)
396 def _download_stage0_helper(self, filename, pattern, tarball_suffix):
397 cache_dst = os.path.join(self.build_dir, "cache")
398 rustc_cache = os.path.join(cache_dst, self.date)
399 if not os.path.exists(rustc_cache):
400 os.makedirs(rustc_cache)
402 url = "{}/dist/{}".format(self._download_url, self.date)
403 tarball = os.path.join(rustc_cache, filename)
404 if not os.path.exists(tarball):
405 get("{}/{}".format(url, filename), tarball, verbose=self.verbose)
406 unpack(tarball, tarball_suffix, self.bin_root(), match=pattern, verbose=self.verbose)
409 def fix_executable(fname):
410 """Modifies the interpreter section of 'fname' to fix the dynamic linker
412 This method is only required on NixOS and uses the PatchELF utility to
413 change the dynamic linker of ELF executables.
415 Please see https://nixos.org/patchelf.html for more information
417 default_encoding = sys.getdefaultencoding()
419 ostype = subprocess.check_output(
420 ['uname', '-s']).strip().decode(default_encoding)
421 except subprocess.CalledProcessError:
423 except OSError as reason:
424 if getattr(reason, 'winerror', None) is not None:
428 if ostype != "Linux":
431 if not os.path.exists("/etc/NIXOS"):
433 if os.path.exists("/lib"):
436 # At this point we're pretty sure the user is running NixOS
437 nix_os_msg = "info: you seem to be running NixOS. Attempting to patch"
438 print(nix_os_msg, fname)
441 interpreter = subprocess.check_output(
442 ["patchelf", "--print-interpreter", fname])
443 interpreter = interpreter.strip().decode(default_encoding)
444 except subprocess.CalledProcessError as reason:
445 print("warning: failed to call patchelf:", reason)
448 loader = interpreter.split("/")[-1]
451 ldd_output = subprocess.check_output(
452 ['ldd', '/run/current-system/sw/bin/sh'])
453 ldd_output = ldd_output.strip().decode(default_encoding)
454 except subprocess.CalledProcessError as reason:
455 print("warning: unable to call ldd:", reason)
458 for line in ldd_output.splitlines():
459 libname = line.split()[0]
460 if libname.endswith(loader):
461 loader_path = libname[:len(libname) - len(loader)]
464 print("warning: unable to find the path to the dynamic linker")
467 correct_interpreter = loader_path + loader
470 subprocess.check_output(
471 ["patchelf", "--set-interpreter", correct_interpreter, fname])
472 except subprocess.CalledProcessError as reason:
473 print("warning: failed to call patchelf:", reason)
476 def rustc_stamp(self):
477 """Return the path for .rustc-stamp
480 >>> rb.build_dir = "build"
481 >>> rb.rustc_stamp() == os.path.join("build", "stage0", ".rustc-stamp")
484 return os.path.join(self.bin_root(), '.rustc-stamp')
486 def cargo_stamp(self):
487 """Return the path for .cargo-stamp
490 >>> rb.build_dir = "build"
491 >>> rb.cargo_stamp() == os.path.join("build", "stage0", ".cargo-stamp")
494 return os.path.join(self.bin_root(), '.cargo-stamp')
496 def program_out_of_date(self, stamp_path):
497 """Check if the given program stamp is out of date"""
498 if not os.path.exists(stamp_path) or self.clean:
500 with open(stamp_path, 'r') as stamp:
501 return self.date != stamp.read()
504 """Return the binary root directory
507 >>> rb.build_dir = "build"
508 >>> rb.bin_root() == os.path.join("build", "stage0")
511 When the 'build' property is given should be a nested directory:
513 >>> rb.build = "devel"
514 >>> rb.bin_root() == os.path.join("build", "devel", "stage0")
517 return os.path.join(self.build_dir, self.build, "stage0")
519 def get_toml(self, key, section=None):
520 """Returns the value of the given key in config.toml, otherwise returns None
523 >>> rb.config_toml = 'key1 = "value1"\\nkey2 = "value2"'
524 >>> rb.get_toml("key2")
527 If the key does not exists, the result is None:
529 >>> rb.get_toml("key3") is None
532 Optionally also matches the section the key appears in
534 >>> rb.config_toml = '[a]\\nkey = "value1"\\n[b]\\nkey = "value2"'
535 >>> rb.get_toml('key', 'a')
537 >>> rb.get_toml('key', 'b')
539 >>> rb.get_toml('key', 'c') is None
542 >>> rb.config_toml = 'key1 = true'
543 >>> rb.get_toml("key1")
548 for line in self.config_toml.splitlines():
549 section_match = re.match(r'^\s*\[(.*)\]\s*$', line)
550 if section_match is not None:
551 cur_section = section_match.group(1)
553 match = re.match(r'^{}\s*=(.*)$'.format(key), line)
554 if match is not None:
555 value = match.group(1)
556 if section is None or section == cur_section:
557 return self.get_string(value) or value.strip()
561 """Return config path for cargo"""
562 return self.program_config('cargo')
565 """Return config path for rustc"""
566 return self.program_config('rustc')
568 def program_config(self, program):
569 """Return config path for the given program
572 >>> rb.config_toml = 'rustc = "rustc"\\n'
573 >>> rb.program_config('rustc')
575 >>> rb.config_toml = ''
576 >>> cargo_path = rb.program_config('cargo')
577 >>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(),
581 config = self.get_toml(program)
583 return os.path.expanduser(config)
584 return os.path.join(self.bin_root(), "bin", "{}{}".format(
585 program, self.exe_suffix()))
588 def get_string(line):
589 """Return the value between double quotes
591 >>> RustBuild.get_string(' "devel" ')
593 >>> RustBuild.get_string(" 'devel' ")
595 >>> RustBuild.get_string('devel') is None
597 >>> RustBuild.get_string(' "devel ')
600 start = line.find('"')
602 end = start + 1 + line[start + 1:].find('"')
603 return line[start + 1:end]
604 start = line.find('\'')
606 end = start + 1 + line[start + 1:].find('\'')
607 return line[start + 1:end]
612 """Return a suffix for executables"""
613 if sys.platform == 'win32':
617 def bootstrap_binary(self):
618 """Return the path of the bootstrap binary
621 >>> rb.build_dir = "build"
622 >>> rb.bootstrap_binary() == os.path.join("build", "bootstrap",
623 ... "debug", "bootstrap")
626 return os.path.join(self.build_dir, "bootstrap", "debug", "bootstrap")
628 def build_bootstrap(self):
629 """Build bootstrap"""
630 build_dir = os.path.join(self.build_dir, "bootstrap")
631 if self.clean and os.path.exists(build_dir):
632 shutil.rmtree(build_dir)
633 env = os.environ.copy()
634 env["RUSTC_BOOTSTRAP"] = '1'
635 env["CARGO_TARGET_DIR"] = build_dir
636 env["RUSTC"] = self.rustc()
637 env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
638 (os.pathsep + env["LD_LIBRARY_PATH"]) \
639 if "LD_LIBRARY_PATH" in env else ""
640 env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
641 (os.pathsep + env["DYLD_LIBRARY_PATH"]) \
642 if "DYLD_LIBRARY_PATH" in env else ""
643 env["LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
644 (os.pathsep + env["LIBRARY_PATH"]) \
645 if "LIBRARY_PATH" in env else ""
646 # preserve existing RUSTFLAGS
647 env.setdefault("RUSTFLAGS", "")
648 env["RUSTFLAGS"] += " -Cdebuginfo=2"
650 build_section = "target.{}".format(self.build_triple())
652 if self.get_toml("crt-static", build_section) == "true":
653 target_features += ["+crt-static"]
654 elif self.get_toml("crt-static", build_section) == "false":
655 target_features += ["-crt-static"]
657 env["RUSTFLAGS"] += " -C target-feature=" + (",".join(target_features))
658 target_linker = self.get_toml("linker", build_section)
659 if target_linker is not None:
660 env["RUSTFLAGS"] += " -C linker=" + target_linker
661 env["RUSTFLAGS"] += " -Wrust_2018_idioms -Wunused_lifetimes"
662 if self.get_toml("deny-warnings", "rust") != "false":
663 env["RUSTFLAGS"] += " -Dwarnings"
665 env["PATH"] = os.path.join(self.bin_root(), "bin") + \
666 os.pathsep + env["PATH"]
667 if not os.path.isfile(self.cargo()):
668 raise Exception("no cargo executable found at `{}`".format(
670 args = [self.cargo(), "build", "--manifest-path",
671 os.path.join(self.rust_root, "src/bootstrap/Cargo.toml")]
672 for _ in range(1, self.verbose):
673 args.append("--verbose")
674 if self.use_locked_deps:
675 args.append("--locked")
676 if self.use_vendored_sources:
677 args.append("--frozen")
678 run(args, env=env, verbose=self.verbose)
680 def build_triple(self):
681 """Build triple as in LLVM"""
682 config = self.get_toml('build')
685 return default_build_triple()
687 def check_submodule(self, module, slow_submodules):
688 if not slow_submodules:
689 checked_out = subprocess.Popen(["git", "rev-parse", "HEAD"],
690 cwd=os.path.join(self.rust_root, module),
691 stdout=subprocess.PIPE)
696 def update_submodule(self, module, checked_out, recorded_submodules):
697 module_path = os.path.join(self.rust_root, module)
699 if checked_out is not None:
700 default_encoding = sys.getdefaultencoding()
701 checked_out = checked_out.communicate()[0].decode(default_encoding).strip()
702 if recorded_submodules[module] == checked_out:
705 print("Updating submodule", module)
707 run(["git", "submodule", "-q", "sync", module],
708 cwd=self.rust_root, verbose=self.verbose)
710 run(["git", "submodule", "update",
711 "--init", "--recursive", "--progress", module],
712 cwd=self.rust_root, verbose=self.verbose, exception=True)
714 # Some versions of git don't support --progress.
715 run(["git", "submodule", "update",
716 "--init", "--recursive", module],
717 cwd=self.rust_root, verbose=self.verbose)
718 run(["git", "reset", "-q", "--hard"],
719 cwd=module_path, verbose=self.verbose)
720 run(["git", "clean", "-qdfx"],
721 cwd=module_path, verbose=self.verbose)
723 def update_submodules(self):
724 """Update submodules"""
725 if (not os.path.exists(os.path.join(self.rust_root, ".git"))) or \
726 self.get_toml('submodules') == "false":
729 # check the existence of 'git' command
731 subprocess.check_output(['git', '--version'])
732 except (subprocess.CalledProcessError, OSError):
733 print("error: `git` is not found, please make sure it's installed and in the path.")
736 slow_submodules = self.get_toml('fast-submodules') == "false"
739 print('Unconditionally updating all submodules')
741 print('Updating only changed submodules')
742 default_encoding = sys.getdefaultencoding()
743 submodules = [s.split(' ', 1)[1] for s in subprocess.check_output(
744 ["git", "config", "--file",
745 os.path.join(self.rust_root, ".gitmodules"),
746 "--get-regexp", "path"]
747 ).decode(default_encoding).splitlines()]
748 filtered_submodules = []
749 submodules_names = []
750 for module in submodules:
751 if module.endswith("llvm-project"):
752 if self.get_toml('llvm-config') and self.get_toml('lld') != 'true':
754 check = self.check_submodule(module, slow_submodules)
755 filtered_submodules.append((module, check))
756 submodules_names.append(module)
757 recorded = subprocess.Popen(["git", "ls-tree", "HEAD"] + submodules_names,
758 cwd=self.rust_root, stdout=subprocess.PIPE)
759 recorded = recorded.communicate()[0].decode(default_encoding).strip().splitlines()
760 recorded_submodules = {}
761 for data in recorded:
763 recorded_submodules[data[3]] = data[2]
764 for module in filtered_submodules:
765 self.update_submodule(module[0], module[1], recorded_submodules)
766 print("Submodules updated in %.2f seconds" % (time() - start_time))
768 def set_normal_environment(self):
769 """Set download URL for normal environment"""
770 if 'RUSTUP_DIST_SERVER' in os.environ:
771 self._download_url = os.environ['RUSTUP_DIST_SERVER']
773 self._download_url = 'https://static.rust-lang.org'
775 def set_dev_environment(self):
776 """Set download URL for development environment"""
777 if 'RUSTUP_DEV_DIST_SERVER' in os.environ:
778 self._download_url = os.environ['RUSTUP_DEV_DIST_SERVER']
780 self._download_url = 'https://dev-static.rust-lang.org'
782 def check_vendored_status(self):
783 """Check that vendoring is configured properly"""
784 vendor_dir = os.path.join(self.rust_root, 'vendor')
785 if 'SUDO_USER' in os.environ and not self.use_vendored_sources:
786 if os.environ.get('USER') != os.environ['SUDO_USER']:
787 self.use_vendored_sources = True
788 print('info: looks like you are running this command under `sudo`')
789 print(' and so in order to preserve your $HOME this will now')
790 print(' use vendored sources by default.')
791 if not os.path.exists(vendor_dir):
792 print('error: vendoring required, but vendor directory does not exist.')
793 print(' Run `cargo vendor` without sudo to initialize the '
795 raise Exception("{} not found".format(vendor_dir))
797 if self.use_vendored_sources:
798 if not os.path.exists('.cargo'):
799 os.makedirs('.cargo')
800 with output('.cargo/config') as cargo_config:
802 "[source.crates-io]\n"
803 "replace-with = 'vendored-sources'\n"
804 "registry = 'https://example.com'\n"
806 "[source.vendored-sources]\n"
807 "directory = '{}/vendor'\n"
808 .format(self.rust_root))
810 if os.path.exists('.cargo'):
811 shutil.rmtree('.cargo')
813 def ensure_vendored(self):
814 """Ensure that the vendored sources are available if needed"""
815 vendor_dir = os.path.join(self.rust_root, 'vendor')
816 # Note that this does not handle updating the vendored dependencies if
817 # the rust git repository is updated. Normal development usually does
818 # not use vendoring, so hopefully this isn't too much of a problem.
819 if self.use_vendored_sources and not os.path.exists(vendor_dir):
820 run([self.cargo(), "vendor"],
821 verbose=self.verbose, cwd=self.rust_root)
824 def bootstrap(help_triggered):
825 """Configure, fetch, build and run the initial bootstrap"""
827 # If the user is asking for help, let them know that the whole download-and-build
828 # process has to happen before anything is printed out.
830 print("info: Downloading and building bootstrap before processing --help")
831 print(" command. See src/bootstrap/README.md for help with common")
834 parser = argparse.ArgumentParser(description='Build rust')
835 parser.add_argument('--config')
836 parser.add_argument('--build')
837 parser.add_argument('--src')
838 parser.add_argument('--clean', action='store_true')
839 parser.add_argument('-v', '--verbose', action='count', default=0)
841 args = [a for a in sys.argv if a != '-h' and a != '--help']
842 args, _ = parser.parse_known_args(args)
844 # Configure initial bootstrap
846 build.rust_root = args.src or os.path.abspath(os.path.join(__file__, '../../..'))
847 build.verbose = args.verbose
848 build.clean = args.clean
851 with open(args.config or 'config.toml') as config:
852 build.config_toml = config.read()
853 except (OSError, IOError):
856 config_verbose = build.get_toml('verbose', 'build')
857 if config_verbose is not None:
858 build.verbose = max(build.verbose, int(config_verbose))
860 build.use_vendored_sources = build.get_toml('vendor', 'build') == 'true'
862 build.use_locked_deps = build.get_toml('locked-deps', 'build') == 'true'
864 build.check_vendored_status()
866 data = stage0_data(build.rust_root)
867 build.date = data['date']
868 build.rustc_channel = data['rustc']
869 build.cargo_channel = data['cargo']
872 build.set_dev_environment()
874 build.set_normal_environment()
876 build.update_submodules()
878 # Fetch/build the bootstrap
879 build.build = args.build or build.build_triple()
880 build.download_stage0()
882 build.ensure_vendored()
883 build.build_bootstrap()
887 args = [build.bootstrap_binary()]
888 args.extend(sys.argv[1:])
889 env = os.environ.copy()
890 env["BUILD"] = build.build
891 env["SRC"] = build.rust_root
892 env["BOOTSTRAP_PARENT_ID"] = str(os.getpid())
893 env["BOOTSTRAP_PYTHON"] = sys.executable
894 env["BUILD_DIR"] = build.build_dir
895 env["RUSTC_BOOTSTRAP"] = '1'
896 env["CARGO"] = build.cargo()
897 env["RUSTC"] = build.rustc()
898 run(args, env=env, verbose=build.verbose)
902 """Entry point for the bootstrap process"""
905 # x.py help <cmd> ...
906 if len(sys.argv) > 1 and sys.argv[1] == 'help':
907 sys.argv = [sys.argv[0], '-h'] + sys.argv[2:]
910 '-h' in sys.argv) or ('--help' in sys.argv) or (len(sys.argv) == 1)
912 bootstrap(help_triggered)
913 if not help_triggered:
914 print("Build completed successfully in {}".format(
915 format_build_time(time() - start_time)))
916 except (SystemExit, KeyboardInterrupt) as error:
917 if hasattr(error, 'code') and isinstance(error.code, int):
918 exit_code = error.code
922 if not help_triggered:
923 print("Build completed unsuccessfully in {}".format(
924 format_build_time(time() - start_time)))
928 if __name__ == '__main__':