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 = ''
325 self.rustfmt_channel = ''
327 self.build_dir = os.path.join(os.getcwd(), "build")
329 self.config_toml = ''
331 self.use_locked_deps = ''
332 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
347 rustfmt_channel = self.rustfmt_channel
351 with tempfile.NamedTemporaryFile(delete=False) as temp_file:
352 temp_path = temp_file.name
353 with tarfile.open(temp_path, "w:xz"):
356 except tarfile.CompressionError:
359 if self.rustc().startswith(self.bin_root()) and \
360 (not os.path.exists(self.rustc()) or
361 self.program_out_of_date(self.rustc_stamp())):
362 if os.path.exists(self.bin_root()):
363 shutil.rmtree(self.bin_root())
364 tarball_suffix = '.tar.xz' if support_xz() else '.tar.gz'
365 filename = "rust-std-{}-{}{}".format(
366 rustc_channel, self.build, tarball_suffix)
367 pattern = "rust-std-{}".format(self.build)
368 self._download_stage0_helper(filename, pattern, tarball_suffix)
370 filename = "rustc-{}-{}{}".format(rustc_channel, self.build,
372 self._download_stage0_helper(filename, "rustc", tarball_suffix)
373 self.fix_executable("{}/bin/rustc".format(self.bin_root()))
374 self.fix_executable("{}/bin/rustdoc".format(self.bin_root()))
375 with output(self.rustc_stamp()) as rust_stamp:
376 rust_stamp.write(self.date)
378 # This is required so that we don't mix incompatible MinGW
379 # libraries/binaries that are included in rust-std with
380 # the system MinGW ones.
381 if "pc-windows-gnu" in self.build:
382 filename = "rust-mingw-{}-{}{}".format(
383 rustc_channel, self.build, tarball_suffix)
384 self._download_stage0_helper(filename, "rust-mingw", tarball_suffix)
386 if self.cargo().startswith(self.bin_root()) and \
387 (not os.path.exists(self.cargo()) or
388 self.program_out_of_date(self.cargo_stamp())):
389 tarball_suffix = '.tar.xz' if support_xz() else '.tar.gz'
390 filename = "cargo-{}-{}{}".format(cargo_channel, self.build,
392 self._download_stage0_helper(filename, "cargo", tarball_suffix)
393 self.fix_executable("{}/bin/cargo".format(self.bin_root()))
394 with output(self.cargo_stamp()) as cargo_stamp:
395 cargo_stamp.write(self.date)
397 if self.rustfmt() and self.rustfmt().startswith(self.bin_root()) and (
398 not os.path.exists(self.rustfmt())
399 or self.program_out_of_date(self.rustfmt_stamp(), self.rustfmt_channel)
402 tarball_suffix = '.tar.xz' if support_xz() else '.tar.gz'
403 [channel, date] = rustfmt_channel.split('-', 1)
404 filename = "rustfmt-{}-{}{}".format(channel, self.build, tarball_suffix)
405 self._download_stage0_helper(filename, "rustfmt-preview", tarball_suffix, date)
406 self.fix_executable("{}/bin/rustfmt".format(self.bin_root()))
407 self.fix_executable("{}/bin/cargo-fmt".format(self.bin_root()))
408 with output(self.rustfmt_stamp()) as rustfmt_stamp:
409 rustfmt_stamp.write(self.date + self.rustfmt_channel)
411 def _download_stage0_helper(self, filename, pattern, tarball_suffix, date=None):
414 cache_dst = os.path.join(self.build_dir, "cache")
415 rustc_cache = os.path.join(cache_dst, date)
416 if not os.path.exists(rustc_cache):
417 os.makedirs(rustc_cache)
419 url = "{}/dist/{}".format(self._download_url, date)
420 tarball = os.path.join(rustc_cache, filename)
421 if not os.path.exists(tarball):
422 get("{}/{}".format(url, filename), tarball, verbose=self.verbose)
423 unpack(tarball, tarball_suffix, self.bin_root(), match=pattern, verbose=self.verbose)
426 def fix_executable(fname):
427 """Modifies the interpreter section of 'fname' to fix the dynamic linker
429 This method is only required on NixOS and uses the PatchELF utility to
430 change the dynamic linker of ELF executables.
432 Please see https://nixos.org/patchelf.html for more information
434 default_encoding = sys.getdefaultencoding()
436 ostype = subprocess.check_output(
437 ['uname', '-s']).strip().decode(default_encoding)
438 except subprocess.CalledProcessError:
440 except OSError as reason:
441 if getattr(reason, 'winerror', None) is not None:
445 if ostype != "Linux":
448 if not os.path.exists("/etc/NIXOS"):
450 if os.path.exists("/lib"):
453 # At this point we're pretty sure the user is running NixOS
454 nix_os_msg = "info: you seem to be running NixOS. Attempting to patch"
455 print(nix_os_msg, fname)
458 interpreter = subprocess.check_output(
459 ["patchelf", "--print-interpreter", fname])
460 interpreter = interpreter.strip().decode(default_encoding)
461 except subprocess.CalledProcessError as reason:
462 print("warning: failed to call patchelf:", reason)
465 loader = interpreter.split("/")[-1]
468 ldd_output = subprocess.check_output(
469 ['ldd', '/run/current-system/sw/bin/sh'])
470 ldd_output = ldd_output.strip().decode(default_encoding)
471 except subprocess.CalledProcessError as reason:
472 print("warning: unable to call ldd:", reason)
475 for line in ldd_output.splitlines():
476 libname = line.split()[0]
477 if libname.endswith(loader):
478 loader_path = libname[:len(libname) - len(loader)]
481 print("warning: unable to find the path to the dynamic linker")
484 correct_interpreter = loader_path + loader
487 subprocess.check_output(
488 ["patchelf", "--set-interpreter", correct_interpreter, fname])
489 except subprocess.CalledProcessError as reason:
490 print("warning: failed to call patchelf:", reason)
493 def rustc_stamp(self):
494 """Return the path for .rustc-stamp
497 >>> rb.build_dir = "build"
498 >>> rb.rustc_stamp() == os.path.join("build", "stage0", ".rustc-stamp")
501 return os.path.join(self.bin_root(), '.rustc-stamp')
503 def cargo_stamp(self):
504 """Return the path for .cargo-stamp
507 >>> rb.build_dir = "build"
508 >>> rb.cargo_stamp() == os.path.join("build", "stage0", ".cargo-stamp")
511 return os.path.join(self.bin_root(), '.cargo-stamp')
513 def rustfmt_stamp(self):
514 """Return the path for .rustfmt-stamp
517 >>> rb.build_dir = "build"
518 >>> rb.rustfmt_stamp() == os.path.join("build", "stage0", ".rustfmt-stamp")
521 return os.path.join(self.bin_root(), '.rustfmt-stamp')
523 def program_out_of_date(self, stamp_path, extra=""):
524 """Check if the given program stamp is out of date"""
525 if not os.path.exists(stamp_path) or self.clean:
527 with open(stamp_path, 'r') as stamp:
528 return (self.date + extra) != stamp.read()
531 """Return the binary root directory
534 >>> rb.build_dir = "build"
535 >>> rb.bin_root() == os.path.join("build", "stage0")
538 When the 'build' property is given should be a nested directory:
540 >>> rb.build = "devel"
541 >>> rb.bin_root() == os.path.join("build", "devel", "stage0")
544 return os.path.join(self.build_dir, self.build, "stage0")
546 def get_toml(self, key, section=None):
547 """Returns the value of the given key in config.toml, otherwise returns None
550 >>> rb.config_toml = 'key1 = "value1"\\nkey2 = "value2"'
551 >>> rb.get_toml("key2")
554 If the key does not exists, the result is None:
556 >>> rb.get_toml("key3") is None
559 Optionally also matches the section the key appears in
561 >>> rb.config_toml = '[a]\\nkey = "value1"\\n[b]\\nkey = "value2"'
562 >>> rb.get_toml('key', 'a')
564 >>> rb.get_toml('key', 'b')
566 >>> rb.get_toml('key', 'c') is None
569 >>> rb.config_toml = 'key1 = true'
570 >>> rb.get_toml("key1")
575 for line in self.config_toml.splitlines():
576 section_match = re.match(r'^\s*\[(.*)\]\s*$', line)
577 if section_match is not None:
578 cur_section = section_match.group(1)
580 match = re.match(r'^{}\s*=(.*)$'.format(key), line)
581 if match is not None:
582 value = match.group(1)
583 if section is None or section == cur_section:
584 return self.get_string(value) or value.strip()
588 """Return config path for cargo"""
589 return self.program_config('cargo')
592 """Return config path for rustc"""
593 return self.program_config('rustc')
596 """Return config path for rustfmt"""
597 if not self.rustfmt_channel:
599 return self.program_config('rustfmt')
601 def program_config(self, program):
602 """Return config path for the given program
605 >>> rb.config_toml = 'rustc = "rustc"\\n'
606 >>> rb.program_config('rustc')
608 >>> rb.config_toml = ''
609 >>> cargo_path = rb.program_config('cargo')
610 >>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(),
614 config = self.get_toml(program)
616 return os.path.expanduser(config)
617 return os.path.join(self.bin_root(), "bin", "{}{}".format(
618 program, self.exe_suffix()))
621 def get_string(line):
622 """Return the value between double quotes
624 >>> RustBuild.get_string(' "devel" ')
626 >>> RustBuild.get_string(" 'devel' ")
628 >>> RustBuild.get_string('devel') is None
630 >>> RustBuild.get_string(' "devel ')
633 start = line.find('"')
635 end = start + 1 + line[start + 1:].find('"')
636 return line[start + 1:end]
637 start = line.find('\'')
639 end = start + 1 + line[start + 1:].find('\'')
640 return line[start + 1:end]
645 """Return a suffix for executables"""
646 if sys.platform == 'win32':
650 def bootstrap_binary(self):
651 """Return the path of the bootstrap binary
654 >>> rb.build_dir = "build"
655 >>> rb.bootstrap_binary() == os.path.join("build", "bootstrap",
656 ... "debug", "bootstrap")
659 return os.path.join(self.build_dir, "bootstrap", "debug", "bootstrap")
661 def build_bootstrap(self):
662 """Build bootstrap"""
663 build_dir = os.path.join(self.build_dir, "bootstrap")
664 if self.clean and os.path.exists(build_dir):
665 shutil.rmtree(build_dir)
666 env = os.environ.copy()
667 env["RUSTC_BOOTSTRAP"] = '1'
668 env["CARGO_TARGET_DIR"] = build_dir
669 env["RUSTC"] = self.rustc()
670 env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
671 (os.pathsep + env["LD_LIBRARY_PATH"]) \
672 if "LD_LIBRARY_PATH" in env else ""
673 env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
674 (os.pathsep + env["DYLD_LIBRARY_PATH"]) \
675 if "DYLD_LIBRARY_PATH" in env else ""
676 env["LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
677 (os.pathsep + env["LIBRARY_PATH"]) \
678 if "LIBRARY_PATH" in env else ""
679 # preserve existing RUSTFLAGS
680 env.setdefault("RUSTFLAGS", "")
681 env["RUSTFLAGS"] += " -Cdebuginfo=2"
683 build_section = "target.{}".format(self.build_triple())
685 if self.get_toml("crt-static", build_section) == "true":
686 target_features += ["+crt-static"]
687 elif self.get_toml("crt-static", build_section) == "false":
688 target_features += ["-crt-static"]
690 env["RUSTFLAGS"] += " -C target-feature=" + (",".join(target_features))
691 target_linker = self.get_toml("linker", build_section)
692 if target_linker is not None:
693 env["RUSTFLAGS"] += " -C linker=" + target_linker
694 env["RUSTFLAGS"] += " -Wrust_2018_idioms -Wunused_lifetimes"
695 if self.get_toml("deny-warnings", "rust") != "false":
696 env["RUSTFLAGS"] += " -Dwarnings"
698 env["PATH"] = os.path.join(self.bin_root(), "bin") + \
699 os.pathsep + env["PATH"]
700 if not os.path.isfile(self.cargo()):
701 raise Exception("no cargo executable found at `{}`".format(
703 args = [self.cargo(), "build", "--manifest-path",
704 os.path.join(self.rust_root, "src/bootstrap/Cargo.toml")]
705 for _ in range(1, self.verbose):
706 args.append("--verbose")
707 if self.use_locked_deps:
708 args.append("--locked")
709 if self.use_vendored_sources:
710 args.append("--frozen")
711 run(args, env=env, verbose=self.verbose)
713 def build_triple(self):
714 """Build triple as in LLVM"""
715 config = self.get_toml('build')
718 return default_build_triple()
720 def check_submodule(self, module, slow_submodules):
721 if not slow_submodules:
722 checked_out = subprocess.Popen(["git", "rev-parse", "HEAD"],
723 cwd=os.path.join(self.rust_root, module),
724 stdout=subprocess.PIPE)
729 def update_submodule(self, module, checked_out, recorded_submodules):
730 module_path = os.path.join(self.rust_root, module)
732 if checked_out is not None:
733 default_encoding = sys.getdefaultencoding()
734 checked_out = checked_out.communicate()[0].decode(default_encoding).strip()
735 if recorded_submodules[module] == checked_out:
738 print("Updating submodule", module)
740 run(["git", "submodule", "-q", "sync", module],
741 cwd=self.rust_root, verbose=self.verbose)
743 run(["git", "submodule", "update",
744 "--init", "--recursive", "--progress", module],
745 cwd=self.rust_root, verbose=self.verbose, exception=True)
747 # Some versions of git don't support --progress.
748 run(["git", "submodule", "update",
749 "--init", "--recursive", module],
750 cwd=self.rust_root, verbose=self.verbose)
751 run(["git", "reset", "-q", "--hard"],
752 cwd=module_path, verbose=self.verbose)
753 run(["git", "clean", "-qdfx"],
754 cwd=module_path, verbose=self.verbose)
756 def update_submodules(self):
757 """Update submodules"""
758 if (not os.path.exists(os.path.join(self.rust_root, ".git"))) or \
759 self.get_toml('submodules') == "false":
762 # check the existence of 'git' command
764 subprocess.check_output(['git', '--version'])
765 except (subprocess.CalledProcessError, OSError):
766 print("error: `git` is not found, please make sure it's installed and in the path.")
769 slow_submodules = self.get_toml('fast-submodules') == "false"
772 print('Unconditionally updating all submodules')
774 print('Updating only changed submodules')
775 default_encoding = sys.getdefaultencoding()
776 submodules = [s.split(' ', 1)[1] for s in subprocess.check_output(
777 ["git", "config", "--file",
778 os.path.join(self.rust_root, ".gitmodules"),
779 "--get-regexp", "path"]
780 ).decode(default_encoding).splitlines()]
781 filtered_submodules = []
782 submodules_names = []
783 for module in submodules:
784 if module.endswith("llvm-project"):
785 if self.get_toml('llvm-config') and self.get_toml('lld') != 'true':
787 check = self.check_submodule(module, slow_submodules)
788 filtered_submodules.append((module, check))
789 submodules_names.append(module)
790 recorded = subprocess.Popen(["git", "ls-tree", "HEAD"] + submodules_names,
791 cwd=self.rust_root, stdout=subprocess.PIPE)
792 recorded = recorded.communicate()[0].decode(default_encoding).strip().splitlines()
793 recorded_submodules = {}
794 for data in recorded:
796 recorded_submodules[data[3]] = data[2]
797 for module in filtered_submodules:
798 self.update_submodule(module[0], module[1], recorded_submodules)
799 print("Submodules updated in %.2f seconds" % (time() - start_time))
801 def set_normal_environment(self):
802 """Set download URL for normal environment"""
803 if 'RUSTUP_DIST_SERVER' in os.environ:
804 self._download_url = os.environ['RUSTUP_DIST_SERVER']
806 self._download_url = 'https://static.rust-lang.org'
808 def set_dev_environment(self):
809 """Set download URL for development environment"""
810 if 'RUSTUP_DEV_DIST_SERVER' in os.environ:
811 self._download_url = os.environ['RUSTUP_DEV_DIST_SERVER']
813 self._download_url = 'https://dev-static.rust-lang.org'
815 def check_vendored_status(self):
816 """Check that vendoring is configured properly"""
817 vendor_dir = os.path.join(self.rust_root, 'vendor')
818 if 'SUDO_USER' in os.environ and not self.use_vendored_sources:
819 if os.environ.get('USER') != os.environ['SUDO_USER']:
820 self.use_vendored_sources = True
821 print('info: looks like you are running this command under `sudo`')
822 print(' and so in order to preserve your $HOME this will now')
823 print(' use vendored sources by default.')
824 if not os.path.exists(vendor_dir):
825 print('error: vendoring required, but vendor directory does not exist.')
826 print(' Run `cargo vendor` without sudo to initialize the '
828 raise Exception("{} not found".format(vendor_dir))
830 if self.use_vendored_sources:
831 if not os.path.exists('.cargo'):
832 os.makedirs('.cargo')
833 with output('.cargo/config') as cargo_config:
835 "[source.crates-io]\n"
836 "replace-with = 'vendored-sources'\n"
837 "registry = 'https://example.com'\n"
839 "[source.vendored-sources]\n"
840 "directory = '{}/vendor'\n"
841 .format(self.rust_root))
843 if os.path.exists('.cargo'):
844 shutil.rmtree('.cargo')
846 def ensure_vendored(self):
847 """Ensure that the vendored sources are available if needed"""
848 vendor_dir = os.path.join(self.rust_root, 'vendor')
849 # Note that this does not handle updating the vendored dependencies if
850 # the rust git repository is updated. Normal development usually does
851 # not use vendoring, so hopefully this isn't too much of a problem.
852 if self.use_vendored_sources and not os.path.exists(vendor_dir):
853 run([self.cargo(), "vendor"],
854 verbose=self.verbose, cwd=self.rust_root)
857 def bootstrap(help_triggered):
858 """Configure, fetch, build and run the initial bootstrap"""
860 # If the user is asking for help, let them know that the whole download-and-build
861 # process has to happen before anything is printed out.
863 print("info: Downloading and building bootstrap before processing --help")
864 print(" command. See src/bootstrap/README.md for help with common")
867 parser = argparse.ArgumentParser(description='Build rust')
868 parser.add_argument('--config')
869 parser.add_argument('--build')
870 parser.add_argument('--src')
871 parser.add_argument('--clean', action='store_true')
872 parser.add_argument('-v', '--verbose', action='count', default=0)
874 args = [a for a in sys.argv if a != '-h' and a != '--help']
875 args, _ = parser.parse_known_args(args)
877 # Configure initial bootstrap
879 build.rust_root = args.src or os.path.abspath(os.path.join(__file__, '../../..'))
880 build.verbose = args.verbose
881 build.clean = args.clean
884 with open(args.config or 'config.toml') as config:
885 build.config_toml = config.read()
886 except (OSError, IOError):
889 config_verbose = build.get_toml('verbose', 'build')
890 if config_verbose is not None:
891 build.verbose = max(build.verbose, int(config_verbose))
893 build.use_vendored_sources = build.get_toml('vendor', 'build') == 'true'
895 build.use_locked_deps = build.get_toml('locked-deps', 'build') == 'true'
897 build.check_vendored_status()
899 data = stage0_data(build.rust_root)
900 build.date = data['date']
901 build.rustc_channel = data['rustc']
902 build.cargo_channel = data['cargo']
904 if "rustfmt" in data:
905 build.rustfmt_channel = data['rustfmt']
908 build.set_dev_environment()
910 build.set_normal_environment()
912 build.update_submodules()
914 # Fetch/build the bootstrap
915 build.build = args.build or build.build_triple()
916 build.download_stage0()
918 build.ensure_vendored()
919 build.build_bootstrap()
923 args = [build.bootstrap_binary()]
924 args.extend(sys.argv[1:])
925 env = os.environ.copy()
926 env["BUILD"] = build.build
927 env["SRC"] = build.rust_root
928 env["BOOTSTRAP_PARENT_ID"] = str(os.getpid())
929 env["BOOTSTRAP_PYTHON"] = sys.executable
930 env["BUILD_DIR"] = build.build_dir
931 env["RUSTC_BOOTSTRAP"] = '1'
932 env["CARGO"] = build.cargo()
933 env["RUSTC"] = build.rustc()
935 env["RUSTFMT"] = build.rustfmt()
936 run(args, env=env, verbose=build.verbose)
940 """Entry point for the bootstrap process"""
943 # x.py help <cmd> ...
944 if len(sys.argv) > 1 and sys.argv[1] == 'help':
945 sys.argv = [sys.argv[0], '-h'] + sys.argv[2:]
948 '-h' in sys.argv) or ('--help' in sys.argv) or (len(sys.argv) == 1)
950 bootstrap(help_triggered)
951 if not help_triggered:
952 print("Build completed successfully in {}".format(
953 format_build_time(time() - start_time)))
954 except (SystemExit, KeyboardInterrupt) as error:
955 if hasattr(error, 'code') and isinstance(error.code, int):
956 exit_code = error.code
960 if not help_triggered:
961 print("Build completed unsuccessfully in {}".format(
962 format_build_time(time() - start_time)))
966 if __name__ == '__main__':