cook: don't stage submodules; refresh staged source in place

Two defects in the out-of-tree staging from 221a91cba4/a3d9240d62, both found
by actually running it rather than by inspection.

1. Submodules were staged. `git ls-files --error-unmatch` succeeds on a
   gitlink, so relibc looked like an ordinary tracked tree and was copied
   wholesale, losing the nested-repo context cargo needs -- crt0, crti, crtn
   and ld_so all failed to build. Fork submodules are already covered by the
   stricter REFUSING-TO-BUILD dirty gate, so skipping them loses no protection.

2. `rm -rf` before the copy made the staged path vanish mid-build. Qt bakes its
   COOKBOOK_SOURCE into installed cmake files (QtSeparateDebugInfo.cmake
   try_compile()s ${QT_SOURCE_TREE}/config.tests/...), so once that path was the
   staged tree, rebuilding qtbase pulled it out from under consumers:
     CMake Error: The source ".../qtbase/.../source-staged/config.tests/
       binary_for_strip/CMakeLists.txt" does not ...
   (qtsvg and qtshadertools). Now rsync -a --delete refreshes in place: same
   pristine result, but the directory never stops existing. Verified with 4000
   probes across a full kirigami rebuild -- 0 observations of it missing.

Staging is confirmed working: a 26-package run staged 10 trees, skipped relibc,
and left 0 dirty tracked source files (previously 22-29).
This commit is contained in:
2026-08-04 18:10:52 +03:00
parent 7ae9646968
commit a591bee846
+42 -6
View File
@@ -987,7 +987,22 @@ pub fn build(
// vendored tree look untracked, so staging silently never fired and
// builds kept mutating the git-tracked source.
let source_real = source_dir.canonicalize().unwrap_or_else(|_| source_dir.to_path_buf());
let source_is_tracked = std::env::var_os("REDBEAR_IN_TREE_BUILD").is_none()
// Never stage a SUBMODULE. `git ls-files --error-unmatch` succeeds
// on a submodule's gitlink, so relibc & co. looked like ordinary
// tracked trees and got copied wholesale -- which broke their cargo
// builds (crt0/crti/crtn/ld_so all failed) because the nested repo
// context did not survive the copy.
//
// Skipping them is principled, not a workaround: each fork
// submodule is already covered by the stricter REFUSING-TO-BUILD
// dirty gate, which forbids uncommitted edits outright. Vendored
// recipe `source/` trees have no such guarantee, and they are the
// ones recipes actually rewrite.
let source_is_submodule = source_real.join(".git").exists();
let source_is_tracked = !source_is_submodule
&& std::env::var_os("REDBEAR_IN_TREE_BUILD").is_none()
&& Command::new("git")
.arg("ls-files")
.arg("--error-unmatch")
@@ -1000,17 +1015,38 @@ pub fn build(
let effective_source = if source_is_tracked {
let staged = get_sub_target_dir(target_dir, "source-staged");
let _ = fs::remove_dir_all(&staged);
let status = Command::new("cp")
// Refresh IN PLACE; never remove the directory first.
//
// Qt bakes its COOKBOOK_SOURCE path into installed cmake files
// (QtSeparateDebugInfo.cmake try_compile()s against
// ${QT_SOURCE_TREE}/config.tests/...). Once that path is the
// staged tree, a `rm -rf` at the start of a rebuild makes it
// vanish underneath consumers that are configuring against it:
// CMake Error: The source ".../qtbase/.../source-staged/
// config.tests/binary_for_strip/CMakeLists.txt" does not ...
// (qtsvg and qtshadertools, both mid-configure).
//
// rsync --delete gives the same pristine result -- files the
// last build's seds added are removed, modified ones are
// restored from the tracked tree -- while the directory itself
// continues to exist throughout.
let _ = fs::create_dir_all(&staged);
let mut src_slash = source_real.clone().into_os_string();
src_slash.push("/");
let mut dst_slash = staged.clone().into_os_string();
dst_slash.push("/");
let status = Command::new("rsync")
.arg("-a")
.arg("--reflink=auto")
.arg(&source_real)
.arg("--delete")
.arg(src_slash)
.arg(dst_slash)
.arg(&staged)
.status()
.map_err(|e| format!("failed to stage source out of tree: {e}"))?;
if !status.success() {
return Err(format!(
"failed to stage source out of tree: cp exited {status}"
"failed to stage source out of tree: rsync exited {status}"
));
}
log_to_pty!(