diff options
Diffstat (limited to 'src/bootstrap/bootstrap.py')
-rw-r--r-- | src/bootstrap/bootstrap.py | 141 |
1 files changed, 75 insertions, 66 deletions
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index f44a05a6b..fac0cdf20 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -41,6 +41,10 @@ def get_cpus(): return 1 +def eprint(*args, **kwargs): + kwargs["file"] = sys.stderr + print(*args, **kwargs) + def get(base, url, path, checksums, verbose=False): with tempfile.NamedTemporaryFile(delete=False) as temp_file: @@ -57,23 +61,23 @@ def get(base, url, path, checksums, verbose=False): if os.path.exists(path): if verify(path, sha256, False): if verbose: - print("using already-download file", path, file=sys.stderr) + eprint("using already-download file", path) return else: if verbose: - print("ignoring already-download file", - path, "due to failed verification", file=sys.stderr) + eprint("ignoring already-download file", + path, "due to failed verification") os.unlink(path) download(temp_path, "{}/{}".format(base, url), True, verbose) if not verify(temp_path, sha256, verbose): raise RuntimeError("failed verification") if verbose: - print("moving {} to {}".format(temp_path, path), file=sys.stderr) + eprint("moving {} to {}".format(temp_path, path)) shutil.move(temp_path, path) finally: if os.path.isfile(temp_path): if verbose: - print("removing", temp_path, file=sys.stderr) + eprint("removing", temp_path) os.unlink(temp_path) @@ -83,7 +87,7 @@ def download(path, url, probably_big, verbose): _download(path, url, probably_big, verbose, True) return except RuntimeError: - print("\nspurious failure, trying again", file=sys.stderr) + eprint("\nspurious failure, trying again") _download(path, url, probably_big, verbose, False) @@ -94,7 +98,7 @@ def _download(path, url, probably_big, verbose, exception): # - If we are on win32 fallback to powershell # - Otherwise raise the error if appropriate if probably_big or verbose: - print("downloading {}".format(url), file=sys.stderr) + eprint("downloading {}".format(url)) try: if (probably_big or verbose) and "GITHUB_ACTIONS" not in os.environ: @@ -104,16 +108,15 @@ def _download(path, url, probably_big, verbose, exception): # If curl is not present on Win32, we should not sys.exit # but raise `CalledProcessError` or `OSError` instead require(["curl", "--version"], exception=platform_is_win32()) - with open(path, "wb") as outfile: - run(["curl", option, - "-L", # Follow redirect. - "-y", "30", "-Y", "10", # timeout if speed is < 10 bytes/sec for > 30 seconds - "--connect-timeout", "30", # timeout if cannot connect within 30 seconds - "--retry", "3", "-SRf", url], - stdout=outfile, #Implements cli redirect operator '>' - verbose=verbose, - exception=True, # Will raise RuntimeError on failure - ) + run(["curl", option, + "-L", # Follow redirect. + "-y", "30", "-Y", "10", # timeout if speed is < 10 bytes/sec for > 30 seconds + "--connect-timeout", "30", # timeout if cannot connect within 30 seconds + "-o", path, + "--retry", "3", "-SRf", url], + verbose=verbose, + exception=True, # Will raise RuntimeError on failure + ) except (subprocess.CalledProcessError, OSError, RuntimeError): # see http://serverfault.com/questions/301128/how-to-download if platform_is_win32(): @@ -130,20 +133,20 @@ def _download(path, url, probably_big, verbose, exception): def verify(path, expected, verbose): """Check if the sha256 sum of the given path is valid""" if verbose: - print("verifying", path, file=sys.stderr) + eprint("verifying", path) with open(path, "rb") as source: found = hashlib.sha256(source.read()).hexdigest() verified = found == expected if not verified: - print("invalid checksum:\n" + eprint("invalid checksum:\n" " found: {}\n" - " expected: {}".format(found, expected), file=sys.stderr) + " expected: {}".format(found, expected)) return verified def unpack(tarball, tarball_suffix, dst, verbose=False, match=None): """Unpack the given tarball file""" - print("extracting", tarball, file=sys.stderr) + eprint("extracting", tarball) fname = os.path.basename(tarball).replace(tarball_suffix, "") with contextlib.closing(tarfile.open(tarball)) as tar: for member in tar.getnames(): @@ -156,7 +159,7 @@ def unpack(tarball, tarball_suffix, dst, verbose=False, match=None): dst_path = os.path.join(dst, name) if verbose: - print(" extracting", member, file=sys.stderr) + eprint(" extracting", member) tar.extract(member, dst) src_path = os.path.join(dst, member) if os.path.isdir(src_path) and os.path.exists(dst_path): @@ -168,7 +171,7 @@ def unpack(tarball, tarball_suffix, dst, verbose=False, match=None): def run(args, verbose=False, exception=False, is_bootstrap=False, **kwargs): """Run a child program in a new process""" if verbose: - print("running: " + ' '.join(args), file=sys.stderr) + eprint("running: " + ' '.join(args)) sys.stdout.flush() # Ensure that the .exe is used on Windows just in case a Linux ELF has been # compiled in the same directory. @@ -208,8 +211,8 @@ def require(cmd, exit=True, exception=False): if exception: raise elif exit: - print("error: unable to run `{}`: {}".format(' '.join(cmd), exc), file=sys.stderr) - print("Please make sure it's installed and in the path.", file=sys.stderr) + eprint("error: unable to run `{}`: {}".format(' '.join(cmd), exc)) + eprint("Please make sure it's installed and in the path.") sys.exit(1) return None @@ -240,14 +243,12 @@ def default_build_triple(verbose): host = next(x for x in version.split('\n') if x.startswith("host: ")) triple = host.split("host: ")[1] if verbose: - print("detected default triple {} from pre-installed rustc".format(triple), - file=sys.stderr) + eprint("detected default triple {} from pre-installed rustc".format(triple)) return triple except Exception as e: if verbose: - print("pre-installed rustc not detected: {}".format(e), - file=sys.stderr) - print("falling back to auto-detect", file=sys.stderr) + eprint("pre-installed rustc not detected: {}".format(e)) + eprint("falling back to auto-detect") required = not platform_is_win32() uname = require(["uname", "-smp"], exit=required) @@ -266,7 +267,8 @@ def default_build_triple(verbose): 'FreeBSD': 'unknown-freebsd', 'Haiku': 'unknown-haiku', 'NetBSD': 'unknown-netbsd', - 'OpenBSD': 'unknown-openbsd' + 'OpenBSD': 'unknown-openbsd', + 'GNU': 'unknown-hurd', } # Consider the direct transformation first and then the special cases @@ -313,6 +315,14 @@ def default_build_triple(verbose): # non-standard string (e.g. gnuwin32 tools returns `windows32`). In # these cases, fall back to using sys.platform. return 'x86_64-pc-windows-msvc' + elif kernel == 'AIX': + # `uname -m` returns the machine ID rather than machine hardware on AIX, + # so we are unable to use cputype to form triple. AIX 7.2 and + # above supports 32-bit and 64-bit mode simultaneously and `uname -p` + # returns `powerpc`, however we only supports `powerpc64-ibm-aix` in + # rust on AIX. For above reasons, kerneltype_mapper and cputype_mapper + # are not used to infer AIX's triple. + return 'powerpc64-ibm-aix' else: err = "unknown OS type: {}".format(kernel) sys.exit(err) @@ -329,6 +339,7 @@ def default_build_triple(verbose): 'i386': 'i686', 'i486': 'i686', 'i686': 'i686', + 'i686-AT386': 'i686', 'i786': 'i686', 'loongarch64': 'loongarch64', 'm68k': 'm68k', @@ -623,7 +634,7 @@ class RustBuild(object): def should_fix_bins_and_dylibs(self): """Whether or not `fix_bin_or_dylib` needs to be run; can only be True - on NixOS. + on NixOS or if config.toml has `build.patch-binaries-for-nix` set. """ if self._should_fix_bins_and_dylibs is not None: return self._should_fix_bins_and_dylibs @@ -643,27 +654,34 @@ class RustBuild(object): if ostype != "Linux": return False - # If the user has asked binaries to be patched for Nix, then - # don't check for NixOS or `/lib`. + # If the user has explicitly indicated whether binaries should be + # patched for Nix, then don't check for NixOS. if self.get_toml("patch-binaries-for-nix", "build") == "true": return True + if self.get_toml("patch-binaries-for-nix", "build") == "false": + return False # Use `/etc/os-release` instead of `/etc/NIXOS`. # The latter one does not exist on NixOS when using tmpfs as root. try: with open("/etc/os-release", "r") as f: - if not any(ln.strip() in ("ID=nixos", "ID='nixos'", 'ID="nixos"') for ln in f): - return False + is_nixos = any(ln.strip() in ("ID=nixos", "ID='nixos'", 'ID="nixos"') + for ln in f) except FileNotFoundError: - return False - if os.path.exists("/lib"): - return False + is_nixos = False - return True + # If not on NixOS, then warn if user seems to be atop Nix shell + if not is_nixos: + in_nix_shell = os.getenv('IN_NIX_SHELL') + if in_nix_shell: + eprint("The IN_NIX_SHELL environment variable is `{}`;".format(in_nix_shell), + "you may need to set `patch-binaries-for-nix=true` in config.toml") + + return is_nixos answer = self._should_fix_bins_and_dylibs = get_answer() if answer: - print("info: You seem to be using Nix.", file=sys.stderr) + eprint("info: You seem to be using Nix.") return answer def fix_bin_or_dylib(self, fname): @@ -676,7 +694,7 @@ class RustBuild(object): Please see https://nixos.org/patchelf.html for more information """ assert self._should_fix_bins_and_dylibs is True - print("attempting to patch", fname, file=sys.stderr) + eprint("attempting to patch", fname) # Only build `.nix-deps` once. nix_deps_dir = self.nix_deps_dir @@ -709,7 +727,7 @@ class RustBuild(object): "nix-build", "-E", nix_expr, "-o", nix_deps_dir, ]) except subprocess.CalledProcessError as reason: - print("warning: failed to call nix-build:", reason, file=sys.stderr) + eprint("warning: failed to call nix-build:", reason) return self.nix_deps_dir = nix_deps_dir @@ -729,7 +747,7 @@ class RustBuild(object): try: subprocess.check_output([patchelf] + patchelf_args + [fname]) except subprocess.CalledProcessError as reason: - print("warning: failed to call patchelf:", reason, file=sys.stderr) + eprint("warning: failed to call patchelf:", reason) return def rustc_stamp(self): @@ -871,7 +889,7 @@ class RustBuild(object): if "GITHUB_ACTIONS" in env: print("::group::Building bootstrap") else: - print("Building bootstrap", file=sys.stderr) + eprint("Building bootstrap") args = self.build_bootstrap_cmd(env) # Run this from the source directory so cargo finds .cargo/config @@ -980,12 +998,9 @@ class RustBuild(object): if 'SUDO_USER' in os.environ and not self.use_vendored_sources: if os.getuid() == 0: self.use_vendored_sources = True - print('info: looks like you\'re trying to run this command as root', - file=sys.stderr) - print(' and so in order to preserve your $HOME this will now', - file=sys.stderr) - print(' use vendored sources by default.', - file=sys.stderr) + eprint('info: looks like you\'re trying to run this command as root') + eprint(' and so in order to preserve your $HOME this will now') + eprint(' use vendored sources by default.') cargo_dir = os.path.join(self.rust_root, '.cargo') if self.use_vendored_sources: @@ -995,18 +1010,14 @@ class RustBuild(object): "--sync ./src/tools/rust-analyzer/Cargo.toml " \ "--sync ./compiler/rustc_codegen_cranelift/Cargo.toml " \ "--sync ./src/bootstrap/Cargo.toml " - print('error: vendoring required, but vendor directory does not exist.', - file=sys.stderr) - print(' Run `cargo vendor {}` to initialize the ' - 'vendor directory.'.format(sync_dirs), - file=sys.stderr) - print('Alternatively, use the pre-vendored `rustc-src` dist component.', - file=sys.stderr) + eprint('error: vendoring required, but vendor directory does not exist.') + eprint(' Run `cargo vendor {}` to initialize the ' + 'vendor directory.'.format(sync_dirs)) + eprint('Alternatively, use the pre-vendored `rustc-src` dist component.') raise Exception("{} not found".format(vendor_dir)) if not os.path.exists(cargo_dir): - print('error: vendoring required, but .cargo/config does not exist.', - file=sys.stderr) + eprint('error: vendoring required, but .cargo/config does not exist.') raise Exception("{} not found".format(cargo_dir)) else: if os.path.exists(cargo_dir): @@ -1100,10 +1111,9 @@ def main(): # If the user is asking for help, let them know that the whole download-and-build # process has to happen before anything is printed out. if help_triggered: - print( + eprint( "info: Downloading and building bootstrap before processing --help command.\n" - " See src/bootstrap/README.md for help with common commands." - , file=sys.stderr) + " See src/bootstrap/README.md for help with common commands.") exit_code = 0 success_word = "successfully" @@ -1114,12 +1124,11 @@ def main(): exit_code = error.code else: exit_code = 1 - print(error, file=sys.stderr) + eprint(error) success_word = "unsuccessfully" if not help_triggered: - print("Build completed", success_word, "in", format_build_time(time() - start_time), - file=sys.stderr) + eprint("Build completed", success_word, "in", format_build_time(time() - start_time)) sys.exit(exit_code) |