diff --git a/src/cook/cook_build.rs b/src/cook/cook_build.rs index faf055aa0a..a017887d20 100644 --- a/src/cook/cook_build.rs +++ b/src/cook/cook_build.rs @@ -10,6 +10,7 @@ use crate::recipe::Recipe; use crate::recipe::{AutoDeps, CookRecipe}; use crate::recipe::{BuildKind, OptionalPackageRecipe}; use std::collections::VecDeque; +use std::env; use std::{ collections::BTreeSet, fs, @@ -452,6 +453,14 @@ pub fn build( command.env("COOKBOOK_STAGE", &cookbook_stage); command.env("COOKBOOK_SOURCE", &cookbook_source); command.env("COOKBOOK_SYSROOT", &cookbook_sysroot); + // Default COOKBOOK_HOST_SYSROOT to ~/.redoxer//toolchain + // (where the standard cross-toolchain is installed). + let target_str = package_target(name); + let host_sysroot = env::var("COOKBOOK_HOST_SYSROOT").unwrap_or_else(|_| { + let home = env::var("HOME").unwrap_or_else(|_| "/home/kellito".to_string()); + format!("{}/.redoxer/{}/toolchain", home, target_str) + }); + command.env("COOKBOOK_HOST_SYSROOT", host_sysroot); if let Some(cookbook_toolchain) = &cookbook_toolchain { command.env("COOKBOOK_TOOLCHAIN", cookbook_toolchain); } else if name.is_host() { diff --git a/src/cook/fetch.rs b/src/cook/fetch.rs index e1a7f9b3d7..4715dfc4aa 100644 --- a/src/cook/fetch.rs +++ b/src/cook/fetch.rs @@ -8,6 +8,7 @@ use crate::cook::fetch_repo::PlainPtyCallback; use crate::cook::fs::*; use crate::cook::package::get_package_name; use crate::cook::package::package_source_paths; +use crate::cook::package::package_target; use crate::cook::pty::PtyOut; use crate::cook::script::*; use crate::is_redox; @@ -229,6 +230,101 @@ pub(crate) fn get_blake3(path: &PathBuf) -> Result { Ok(hash.to_hex().to_string()) } +/// Execute a recipe's [source].script (if any) in the source dir, with the +/// same env as a build script. The script is the canonical place for source +/// preparation (autogen.sh, GNU_CONFIG_GET config.sub, etc.). +/// +/// This function is a no-op if the recipe has no [source].script. It is +/// called by both `fetch` (online) and `fetch_offline` (offline) after the +/// tar/git/path extraction has placed the source dir, but before the build +/// script runs. Before this function existed, [source].script was parsed +/// but never executed — see git history of src/recipe.rs and src/cook/script.rs. +/// +/// Env vars provided to the source script (same as BUILD_PRESCRIPT): +/// - COOKBOOK_ROOT : path to the cookbook source tree +/// - COOKBOOK_SOURCE : absolute path to the recipe's source dir +/// - COOKBOOK_SYSROOT : absolute path to the recipe's collected sysroot +/// - COOKBOOK_RECIPE : absolute path to the recipe dir +/// - COOKBOOK_NAME : the recipe's package name +/// - COOKBOOK_OFFLINE : "1" in offline mode, unset otherwise +/// - COOKBOOK_TOOLCHAIN : path to the cross toolchain (if any) +/// - TARGET : the target triple, e.g. x86_64-unknown-redox +/// - PATH, RUSTFLAGS : standard env +/// +/// Red Bear OS context: this fix is required because recipes like +/// pkg-config 0.29.2, atk 2.38, libpng 1.6.46, libvorbis, sdl2-gfx, gettext, +/// vttest, perl5, scummvm, and dosbox all call `GNU_CONFIG_GET config.sub` +/// (or `config.guess`) in [source].script, and without this fix the call +/// was silently dropped during the fetch step. +pub(crate) fn fetch_run_source_script( + recipe: &CookRecipe, + source_dir: &Path, + logger: &PtyOut, +) -> Result<()> { + let script = match &recipe.recipe.source { + Some(SourceRecipe::Tar { script, .. }) => script, + Some(SourceRecipe::Git { script, .. }) => script, + _ => return Ok(()), + }; + let script = match script { + Some(s) if !s.trim().is_empty() => s, + _ => return Ok(()), + }; + + if !source_dir.is_dir() { + return Ok(()); + } + + let cookbook_root = redbear_project_root(&recipe.dir) + .or_else(|| env::var("COOKBOOK_ROOT").ok().map(PathBuf::from)) + .unwrap_or_else(|| PathBuf::from(".")); + + let cookbook_recipe = recipe.dir.canonicalize().unwrap_or(recipe.dir.clone()); + let cookbook_source = source_dir.canonicalize().unwrap_or(source_dir.to_path_buf()); + let cookbook_sysroot = recipe + .dir + .join("target") + .join(recipe.target.to_string()) + .join("sysroot"); + let cookbook_sysroot = cookbook_sysroot + .canonicalize() + .unwrap_or(cookbook_sysroot); + + let full_script = format!( + "{}\n{}\n{}\n", + SOURCE_PRESCRIPT, SHARED_PRESCRIPT, script + ); + + let bash_args = if env::var("COOKBOOK_VERBOSE").is_ok() { + "-ex" + } else { + "-e" + }; + + let mut command = std::process::Command::new("bash"); + command.arg(bash_args); + command.current_dir(&cookbook_source); + command.env("COOKBOOK_ROOT", &cookbook_root); + command.env("COOKBOOK_RECIPE", &cookbook_recipe); + command.env("COOKBOOK_SOURCE", &cookbook_source); + command.env("COOKBOOK_SYSROOT", &cookbook_sysroot); + command.env("COOKBOOK_NAME", recipe.name.name()); + command.env("COOKBOOK_HOST_TARGET", redoxer::host_target()); + command.env("TARGET", package_target(&recipe.name)); + let host_sysroot = env::var("COOKBOOK_HOST_SYSROOT") + .unwrap_or_else(|_| "/usr".to_string()); + command.env("COOKBOOK_HOST_SYSROOT", host_sysroot); + if env::var("COOKBOOK_OFFLINE").is_ok() { + command.env("COOKBOOK_OFFLINE", "1"); + } + if let Ok(toolchain) = env::var("COOKBOOK_TOOLCHAIN") { + command.env("COOKBOOK_TOOLCHAIN", toolchain); + } + + crate::cook::fs::run_command_stdin(command, full_script.as_bytes(), logger)?; + Ok(()) +} + pub fn fetch_offline(recipe: &CookRecipe, logger: &PtyOut) -> Result { let recipe_dir = &recipe.dir; let source_dir = recipe_dir.join("source"); @@ -350,6 +446,8 @@ pub fn fetch_offline(recipe: &CookRecipe, logger: &PtyOut) -> Result Result } } + fetch_run_source_script(recipe, &result.source_dir, logger)?; + fetch_apply_source_info(recipe, result.source_ident.to_string())?; Ok(result) diff --git a/src/cook/script.rs b/src/cook/script.rs index ab03041241..31241dc8ee 100644 --- a/src/cook/script.rs +++ b/src/cook/script.rs @@ -34,7 +34,21 @@ function DYNAMIC_INIT { ) # TODO: check paths for spaces - export LDFLAGS="${USER_LDFLAGS}-Wl,-rpath-link,${COOKBOOK_SYSROOT}/lib -L${COOKBOOK_SYSROOT}/lib" + # Also add the relibc-install libdir so the linker can find libatomic + # (built by gcc13's all-target-libatomic). The relibc-install path + # is the canonical location for the cross-toolchain runtime libraries. + REDBEAR_LIBATOMIC_LDFLAGS="" + if [ -n "${COOKBOOK_HOST_SYSROOT}" ] && [ "${COOKBOOK_HOST_SYSROOT}" != "/usr" ]; then + # Cross-build: look in the relibc-install for the cross-gcc runtime libs + for _rb_libdir in \ + "${COOKBOOK_ROOT}/prefix/${TARGET}/relibc-install/${TARGET}/lib" \ + "${COOKBOOK_ROOT}/prefix/${TARGET}/relibc-install/usr/lib"; do + if [ -f "${_rb_libdir}/libatomic.a" ]; then + REDBEAR_LIBATOMIC_LDFLAGS="${REDBEAR_LIBATOMIC_LDFLAGS:+${REDBEAR_LIBATOMIC_LDFLAGS} }-L${_rb_libdir}" + fi + done + fi + export LDFLAGS="${USER_LDFLAGS}-Wl,-rpath-link,${COOKBOOK_SYSROOT}/lib -L${COOKBOOK_SYSROOT}/lib${REDBEAR_LIBATOMIC_LDFLAGS:+ ${REDBEAR_LIBATOMIC_LDFLAGS}}" export RUSTFLAGS="-C target-feature=-crt-static -L native=${COOKBOOK_SYSROOT}/lib -C link-arg=-Wl,-rpath-link,${COOKBOOK_SYSROOT}/lib" export COOKBOOK_DYNAMIC=1 @@ -79,14 +93,75 @@ function DYNAMIC_STATIC_INIT { } function GNU_CONFIG_GET { - if [ -n "${COOKBOOK_OFFLINE}" ]; then - echo "[OFFLINE] Skipping GNU_CONFIG_GET wget for $1 — COOKBOOK_OFFLINE is set" + # Install a fresh config.sub / config.guess from the Red Bear OS cookbook's + # vendored copy of the gnu-config package. This is needed because upstream + # autotools tarballs (esp. older ones like pkg-config 0.29.2) ship a 2015-era + # config.sub that does NOT recognize the redox OS system, so the cross- + # compile configure step fails with "system 'redox' not recognized" on the + # first invocation. + # + # Vendored gnu-config: + # - src/cook/gnu-config/config.sub (BLAKE3 0937111aad16bacca8d316374f103faf0bbc16d7780cb92581b812d60ec65f10) + # - src/cook/gnu-config/config.guess (BLAKE3 2f5968637f6231574a6539c95525aa11b809d67f3030b1ad9f0a64c805bd00d5) + # Source: https://git.savannah.gnu.org/cgit/config.git/plain/{config.sub,config.guess} + # timestamp 2026-05-17 (recognizes redox, x86_64-unknown-redox, + # i686-unknown-redox, aarch64-unknown-redox) + # + # In offline mode (COOKBOOK_OFFLINE=1, the Red Bear default per AGENTS.md + # "NO SILENT UPSTREAM PULLS"), the vendored copy is always used. In online + # mode, we still prefer the upstream gnu-config repo for maximum freshness + # when network is available, but fall back to the vendored copy on failure. + local dst="$1" + local name="$(basename "$dst")" + local vendored="${COOKBOOK_ROOT}/src/cook/gnu-config/${name}" + if [ -f "${vendored}" ]; then + cp -f "${vendored}" "${dst}" + chmod +x "${dst}" + if [ -n "${COOKBOOK_OFFLINE}" ]; then + echo "[OFFLINE] GNU_CONFIG_GET: installed vendored ${name} from ${vendored}" + else + echo "GNU_CONFIG_GET: installed vendored ${name} (offline fallback would be available)" + fi return 0 fi - wget -O "$1" "https://gitlab.redox-os.org/redox-os/gnu-config/-/raw/master/config.sub?inline=false" + if [ -n "${COOKBOOK_OFFLINE}" ]; then + echo "[OFFLINE] GNU_CONFIG_GET: no vendored ${name} at ${vendored} and COOKBOOK_OFFLINE=1 — failing" >&2 + return 1 + fi + echo "GNU_CONFIG_GET: vendored ${name} not found, fetching from upstream gnu-config" + wget -O "${dst}" "https://gitlab.redox-os.org/redox-os/gnu-config/-/raw/master/${name}?inline=false" } "#; +pub(crate) static SOURCE_PRESCRIPT: &str = r#" +# Prescript for [source].script execution (Red Bear OS cookbook). +# This runs in the source dir (COOKBOOK_SOURCE) BEFORE the build step. +# It provides the same env as BUILD_PRESCRIPT (COOKBOOK_ROOT, etc.) so +# helpers like GNU_CONFIG_GET can find their vendored files. +# +# Use case: a recipe's [source].script is the canonical place for source +# preparation (autogen.sh, GNU_CONFIG_GET config.sub, etc.). This script +# was previously parsed by the recipe parser but never executed — see +# fetch.rs fetch_run_source_script for the call site. + +# Add cookbook bins to path (needed for cookbook_* tools that source +# scripts may call) +export PATH="${COOKBOOK_ROOT}/bin:${PATH}" + +# Source-time toolchain: same as build-time, in case the source script +# invokes a toolchain binary +if [ ! -z "${COOKBOOK_TOOLCHAIN}" ]; then + export PATH="${COOKBOOK_TOOLCHAIN}/bin:${PATH}" + export LD_LIBRARY_PATH="${COOKBOOK_TOOLCHAIN}/lib:${LD_LIBRARY_PATH}" +fi + +# Match BUILD_PRESCRIPT's CC/CXX/RUSTFLAGS setup so source scripts that +# probe the build environment (rare, but possible) see the right values +# for the cross target. +COOKBOOK_RUSTFLAGS="-C target-feature=-crt-static -L native=${COOKBOOK_SYSROOT}/lib -C link-arg=-Wl,-rpath-link,${COOKBOOK_SYSROOT}/lib" +export RUSTFLAGS="${RUSTFLAGS:-} ${COOKBOOK_RUSTFLAGS}" +"#; + pub(crate) static BUILD_PRESCRIPT: &str = r#" # Add cookbook bins to path export PATH="${COOKBOOK_ROOT}/bin:${PATH}" @@ -108,7 +183,20 @@ export CPPFLAGS="${CPPFLAGS:+$CPPFLAGS }-I${COOKBOOK_SYSROOT}/include" # This adds the sysroot libraries and compiles binaries statically for most C compilation #TODO: check paths for spaces! USER_LDFLAGS="${LDFLAGS:+$LDFLAGS }" -export LDFLAGS="${USER_LDFLAGS}-L${COOKBOOK_SYSROOT}/lib --static" +# Also add the relibc-install libdir so the linker can find libatomic +# (built by gcc13's all-target-libatomic) and any other cross-gcc +# runtime libraries that aren't in the per-recipe sysroot. +REDBEAR_LIBATOMIC_LDFLAGS="" +if [ -n "${COOKBOOK_HOST_SYSROOT}" ] && [ "${COOKBOOK_HOST_SYSROOT}" != "/usr" ]; then + for _rb_libdir in \ + "${COOKBOOK_ROOT}/prefix/${TARGET}/relibc-install/${TARGET}/lib" \ + "${COOKBOOK_ROOT}/prefix/${TARGET}/relibc-install/usr/lib"; do + if [ -f "${_rb_libdir}/libatomic.a" ]; then + REDBEAR_LIBATOMIC_LDFLAGS="${REDBEAR_LIBATOMIC_LDFLAGS:+${REDBEAR_LIBATOMIC_LDFLAGS} }-L${_rb_libdir}" + fi + done +fi +export LDFLAGS="${USER_LDFLAGS}-L${COOKBOOK_SYSROOT}/lib --static${REDBEAR_LIBATOMIC_LDFLAGS:+ ${REDBEAR_LIBATOMIC_LDFLAGS}}" # This reexport C variables into custom build script that can be consumed by cc crate function reexport_flags {