diff --git a/src/cook/cook_build.rs b/src/cook/cook_build.rs index 2157dc2198..f68d4f2670 100644 --- a/src/cook/cook_build.rs +++ b/src/cook/cook_build.rs @@ -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!(