cookbook: add cookbook_apply_patches helper for external patch model (Rule 2)
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -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/<target>/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() {
|
||||
|
||||
@@ -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<String> {
|
||||
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<FetchResult> {
|
||||
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<FetchResult
|
||||
}
|
||||
};
|
||||
|
||||
fetch_run_source_script(recipe, &result.source_dir, logger)?;
|
||||
|
||||
fetch_apply_source_info(recipe, result.source_ident.clone())?;
|
||||
|
||||
Ok(result)
|
||||
@@ -678,6 +776,8 @@ pub fn fetch(recipe: &CookRecipe, check_source: bool, logger: &PtyOut) -> Result
|
||||
}
|
||||
}
|
||||
|
||||
fetch_run_source_script(recipe, &result.source_dir, logger)?;
|
||||
|
||||
fetch_apply_source_info(recipe, result.source_ident.to_string())?;
|
||||
|
||||
Ok(result)
|
||||
|
||||
+93
-5
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user