diff --git a/config/redbear-full.toml b/config/redbear-full.toml index a0324cc865..332f67a164 100644 --- a/config/redbear-full.toml +++ b/config/redbear-full.toml @@ -262,10 +262,16 @@ redbear-meta = {} redbear-power = {} # Native build toolchain (Phase 3: GCC + binutils running on redox) -gcc-native = "ignore" +# All four are first-class members of this target. gcc-native was set to +# "ignore" and llvm-native/rust-native were commented out as "not needed for +# greeter proof"; both forms are forbidden by AGENTS.md § ABSOLUTE RULE — +# NEVER DELETE, NEVER IGNORE, NEVER COMMENT OUT and by local/AGENTS.md +# § "No 'build-excluded until ported' packages". A package that does not build +# gets ported, not excluded. +gcc-native = {} binutils-native = {} -# llvm-native = {} # suppressed: Redox C++/pthread header gaps; not needed for greeter proof -# rust-native = {} # suppressed: depends on llvm-native; not needed for greeter proof +llvm-native = {} +rust-native = {} # Desktop fonts and icons dejavu = {} diff --git a/src/cook/cook_build.rs b/src/cook/cook_build.rs index baa300c2a0..9264657b94 100644 --- a/src/cook/cook_build.rs +++ b/src/cook/cook_build.rs @@ -853,7 +853,7 @@ pub fn build( let build_dir = get_sub_target_dir(target_dir, "build"); if !stage_dir.is_dir() { // Create stage.tmp - let stage_dir_tmp = target_dir.join("stage.tmp"); + let stage_dir_tmp = unique_tmp_dir(target_dir, "stage"); create_dir_clean(&stage_dir_tmp)?; // Create build dir, if it does not exist @@ -1139,18 +1139,21 @@ fn build_deps_dir( deps_modified: SystemTime, ) -> Result { // Per-call staging path. A single shared ".tmp" is unsafe: + // + // (see unique_tmp_dir() below for the general form of this problem) // package variants of one recipe (llvm-native{,.dev,.runtime}) share this // target directory and are cooked concurrently by the cook_jobs thread // pool, so create_dir_clean() below would wipe a sibling's half-populated // staging tree while it was still extracting into it. The counter (not a // pid -- the pool is threads inside one process) keeps each cook's staging // private; the rename at the end is what publishes it. - static SYSROOT_TMP_SEQ: AtomicUsize = AtomicUsize::new(0); - let deps_dir_tmp = deps_dir.with_added_extension(format!( - "tmp.{}.{}", - std::process::id(), - SYSROOT_TMP_SEQ.fetch_add(1, AtomicOrdering::Relaxed) - )); + let deps_dir_tmp = unique_tmp_dir( + deps_dir.parent().unwrap_or(Path::new(".")), + &deps_dir + .file_name() + .map(|s| s.to_string_lossy().into_owned()) + .unwrap_or_else(|| "sysroot".to_string()), + ); if deps_dir.is_dir() { let tags_dir = deps_dir.join(".tags"); let sysroot_modified = modified_dir(&tags_dir).unwrap_or(SystemTime::UNIX_EPOCH); @@ -1252,6 +1255,32 @@ fn build_deps_dir( Ok(false) } +/// Build a staging path that no other concurrent cook can collide with. +/// +/// Several package variants of one recipe (e.g. `llvm-native`, +/// `llvm-native.dev`, `llvm-native.runtime`) share a single `target//` +/// directory and are cooked concurrently by the `cook_jobs` thread pool. Every +/// staging path under that directory was a fixed name — `stage.tmp`, +/// `sysroot.tmp` — so the variants raced: one would rename or remove the +/// shared directory while a sibling was still writing into it, producing +/// +/// Canonicalize stage dir failed at ".../stage.tmp": No such file or directory +/// Renaming failed from ".../sysroot.tmp" to ".../sysroot": File exists +/// +/// which failed the whole recipe. `llvm-native` cooks clean on its own but +/// failed in every parallel build for exactly this reason. +/// +/// A pid is not enough to disambiguate — the pool is threads inside one +/// process — so a process-wide counter supplies the unique component. +fn unique_tmp_dir(base: &Path, name: &str) -> PathBuf { + static TMP_SEQ: AtomicUsize = AtomicUsize::new(0); + base.join(format!( + "{name}.tmp.{}.{}", + std::process::id(), + TMP_SEQ.fetch_add(1, AtomicOrdering::Relaxed) + )) +} + /// Calculate automatic dependencies fn build_auto_deps( recipe: &Recipe, @@ -1311,7 +1340,7 @@ pub fn build_remote( if !stage_dir.is_dir() { let (_, source_pkgar, _) = package_source_paths(package, target_dir); - let stage_dir_tmp = target_dir.join("stage.tmp"); + let stage_dir_tmp = unique_tmp_dir(target_dir, "stage"); pkgar::extract(source_pubkey, &source_pkgar, &stage_dir_tmp).map_err(|err| { format!( "failed to install '{}' in '{}': {:?}",