From 499332ac7d2115a6b7fc7fe0d640667938023c78 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Thu, 12 Nov 2026 00:31:40 +0900 Subject: [PATCH 1/2] avoid redundant scans when extracting stage0 archives --- src/bootstrap/bootstrap.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index fcec091fed1c7..8dae5d4853f03 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -199,7 +199,8 @@ def unpack(tarball, tarball_suffix, dst, verbose=0, match=None): eprint("extracting", tarball) fname = os.path.basename(tarball).replace(tarball_suffix, "") with contextlib.closing(tarfile.open(tarball)) as tar: - for member in tar.getnames(): + for member_info in tar: + member = member_info.name if "/" not in member: continue name = member.replace(fname + "/", "", 1) @@ -210,7 +211,7 @@ def unpack(tarball, tarball_suffix, dst, verbose=0, match=None): dst_path = os.path.join(dst, name) if verbose > 0: eprint(" extracting", member) - tar.extract(member, dst) + tar.extract(member_info, dst) src_path = os.path.join(dst, member) if os.path.isdir(src_path) and os.path.exists(dst_path): continue From 832e0f3a2039f3142ae32d78a3ff0cd3040e494d Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Thu, 12 Nov 2026 17:39:01 +0900 Subject: [PATCH 2/2] prepare stage0 directories before parallel extraction --- src/bootstrap/bootstrap.py | 15 ++++++++++++--- src/tools/rust-installer/src/tarballer.rs | 2 ++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 8dae5d4853f03..ae6f5f1c9bbd3 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -194,12 +194,15 @@ def verify(path, expected, verbose): return verified -def unpack(tarball, tarball_suffix, dst, verbose=0, match=None): +def unpack(tarball, tarball_suffix, dst, verbose=0, match=None, directories_only=False): """Unpack the given tarball file""" - eprint("extracting", tarball) + if not directories_only: + eprint("extracting", tarball) fname = os.path.basename(tarball).replace(tarball_suffix, "") with contextlib.closing(tarfile.open(tarball)) as tar: for member_info in tar: + if directories_only and not member_info.isdir(): + break member = member_info.name if "/" not in member: continue @@ -536,13 +539,14 @@ def download_component(download_info): ) -def unpack_component(download_info): +def unpack_component(download_info, directories_only=False): unpack( download_info.tarball_path, download_info.tarball_suffix, download_info.bin_root, match=download_info.pattern, verbose=download_info.verbose, + directories_only=directories_only, ) @@ -697,6 +701,11 @@ def download_toolchain(self): for download_info in tarballs_download_info: download_component(download_info) + # rust-installer writes all directories before files. Move them serially + # so parallel extraction cannot race when creating shared directories. + for download_info in tarballs_download_info: + unpack_component(download_info, directories_only=True) + # Unpack the tarballs in parallel. # In Python 2.7, Pool cannot be used as a context manager. pool_size = min(len(tarballs_download_info), get_cpus()) diff --git a/src/tools/rust-installer/src/tarballer.rs b/src/tools/rust-installer/src/tarballer.rs index 854bb85f2c526..f999be962f3d1 100644 --- a/src/tools/rust-installer/src/tarballer.rs +++ b/src/tools/rust-installer/src/tarballer.rs @@ -67,6 +67,8 @@ impl Tarballer { // Write the tar into both encoded files. We write all directories // first, so files may be directly created. (See rust-lang/rustup.rs#1092.) + // Bootstrap also relies on this ordering. It prepares directories up to the + // first non-directory entry before extracting toolchain components in parallel. let buf = BufWriter::with_capacity(1024 * 1024, encoder); let mut builder = Builder::new(buf); // Make uid, gid and mtime deterministic to improve reproducibility