From 9c97cefc5740951ff9c018207e1107bcbe8a96f6 Mon Sep 17 00:00:00 2001 From: vasilito Date: Thu, 16 Jul 2026 23:23:37 +0900 Subject: [PATCH] recipes: adopt brush shell into local/recipes with Redox port patches Port the brush shell (reubeno/brush) to Redox and adopt the recipe as a Red Bear local recipe under local/recipes/shells/brush, with the standard recipes/shells/brush -> ../../local/recipes/shells/brush overlay symlink so it takes priority over any upstream WIP copy (per local/AGENTS.md "Local recipe priority vs upstream WIP"). Removes the old recipes/wip/shells/brush. The dependency tree did not build for Redox: nix 0.31 only partially cfg-enables Redox and the libc crate's Redox module omits POSIX symbols that relibc provides. Fixes are reviewable unified diffs under local/recipes/shells/brush/patches/: nix-0.31-redox.patch cfg-enable resource/Id/waitid/from_siginfo, rlimit import + repr(i32), SaFlags width casts, pty. libc-0.2-redox.patch add idtype_t, P_*/CLD_*, rusage, getrusage/waitid/ forkpty externs, siginfo child accessors. brush-umask-redox.patch mode_t is signed on Redox; cast, not u32::from. The recipe runs `cargo fetch` to materialise registry sources before patching (they are only unpacked during the build), resolves the right crate version from Cargo.lock (brush pulls both nix 0.26 and 0.31), and applies each patch idempotently, failing loudly on version drift. The binary target is named `brush` (not `brush-shell`), so install it explicitly. Verified: cooks from a pristine cargo cache using only the patch files. brush is shipped in redbear-mini as a package for in-image validation; the login shell stays zsh until brush is runtime-proven. --- .gitignore | 1 + config/redbear-mini.toml | 4 + .../brush/patches/brush-umask-redox.patch | 14 +++ .../shells/brush/patches/libc-0.2-redox.patch | 89 +++++++++++++++++++ .../shells/brush/patches/nix-0.31-redox.patch | 89 +++++++++++++++++++ local/recipes/shells/brush/recipe.toml | 78 ++++++++++++++++ recipes/shells/brush | 1 + recipes/wip/shells/brush/recipe.toml | 12 --- 8 files changed, 276 insertions(+), 12 deletions(-) create mode 100644 local/recipes/shells/brush/patches/brush-umask-redox.patch create mode 100644 local/recipes/shells/brush/patches/libc-0.2-redox.patch create mode 100644 local/recipes/shells/brush/patches/nix-0.31-redox.patch create mode 100644 local/recipes/shells/brush/recipe.toml create mode 120000 recipes/shells/brush delete mode 100644 recipes/wip/shells/brush/recipe.toml diff --git a/.gitignore b/.gitignore index 364b8ecf03..84483ac12e 100644 --- a/.gitignore +++ b/.gitignore @@ -74,3 +74,4 @@ sources/x86_64-unknown-redox/ sources/*.tar.gz Packages/*.pkgar .slim/deepwork/ +local/recipes/shells/brush/source diff --git a/config/redbear-mini.toml b/config/redbear-mini.toml index abc70059ef..11afb7eb96 100644 --- a/config/redbear-mini.toml +++ b/config/redbear-mini.toml @@ -49,6 +49,10 @@ tlc = {} #mc = {} redbear-info = {} +# brush: Rust shell, candidate default login shell (validated in-image before +# switching the [users.*] shell over from zsh). +brush = {} + # Keep package builder utility in live environment. cub = {} cpufreqd = {} diff --git a/local/recipes/shells/brush/patches/brush-umask-redox.patch b/local/recipes/shells/brush/patches/brush-umask-redox.patch new file mode 100644 index 0000000000..bc7d019f0e --- /dev/null +++ b/local/recipes/shells/brush/patches/brush-umask-redox.patch @@ -0,0 +1,14 @@ +--- a/brush-builtins/src/umask.rs ++++ b/brush-builtins/src/umask.rs +@@ -68,7 +68,10 @@ + fn get_umask() -> Result { + let u = nix::sys::stat::umask(Mode::empty()); + nix::sys::stat::umask(u); +- Ok(u32::from(u.bits())) ++ // mode_t is signed (c_int) on Redox and some other targets, so ++ // `u32::from` (widening only) does not apply; a value-preserving cast ++ // covers signed and unsigned mode_t alike. ++ Ok(u.bits() as u32) + } + } + } diff --git a/local/recipes/shells/brush/patches/libc-0.2-redox.patch b/local/recipes/shells/brush/patches/libc-0.2-redox.patch new file mode 100644 index 0000000000..c7f572344e --- /dev/null +++ b/local/recipes/shells/brush/patches/libc-0.2-redox.patch @@ -0,0 +1,89 @@ +--- a/src/unix/redox/mod.rs ++++ b/src/unix/redox/mod.rs +@@ -1289,6 +1289,12 @@ + termp: *const termios, + winp: *const crate::winsize, + ) -> c_int; ++ pub fn forkpty( ++ amaster: *mut c_int, ++ name: *mut c_char, ++ termp: *const termios, ++ winp: *const crate::winsize, ++ ) -> c_int; + + // pwd.h + pub fn getpwent() -> *mut passwd; +@@ -1385,6 +1391,13 @@ + pub fn setpriority(which: c_int, who: crate::id_t, prio: c_int) -> c_int; + pub fn getrlimit(resource: c_int, rlim: *mut crate::rlimit) -> c_int; + pub fn setrlimit(resource: c_int, rlim: *const crate::rlimit) -> c_int; ++ pub fn getrusage(resource: c_int, usage: *mut crate::rusage) -> c_int; ++ pub fn waitid( ++ idtype: crate::idtype_t, ++ id: crate::id_t, ++ infop: *mut siginfo_t, ++ options: c_int, ++ ) -> c_int; + + // sys/socket.h + pub fn CMSG_DATA(cmsg: *const cmsghdr) -> *mut c_uchar; +@@ -1431,3 +1444,59 @@ + // utmp.h + pub fn login_tty(fd: c_int) -> c_int; + } ++ ++// REDBEAR_REDOX_WAITID: symbols relibc provides but the libc crate omits. ++pub type idtype_t = c_uint; ++ ++// waitid idtype_t values. ++pub const P_ALL: idtype_t = 0; ++pub const P_PID: idtype_t = 1; ++pub const P_PGID: idtype_t = 2; ++ ++// siginfo_t.si_code values (child status change). ++pub const CLD_EXITED: c_int = 1; ++pub const CLD_KILLED: c_int = 2; ++pub const CLD_DUMPED: c_int = 3; ++pub const CLD_TRAPPED: c_int = 4; ++pub const CLD_STOPPED: c_int = 5; ++pub const CLD_CONTINUED: c_int = 6; ++ ++s! { ++ pub struct rusage { ++ pub ru_utime: crate::timeval, ++ pub ru_stime: crate::timeval, ++ pub ru_maxrss: c_long, ++ pub ru_ixrss: c_long, ++ pub ru_idrss: c_long, ++ pub ru_isrss: c_long, ++ pub ru_minflt: c_long, ++ pub ru_majflt: c_long, ++ pub ru_nswap: c_long, ++ pub ru_inblock: c_long, ++ pub ru_oublock: c_long, ++ pub ru_msgsnd: c_long, ++ pub ru_msgrcv: c_long, ++ pub ru_nsignals: c_long, ++ pub ru_nvcsw: c_long, ++ pub ru_nivcsw: c_long, ++ } ++} ++ ++impl siginfo_t { ++ // relibc's siginfo layout places these fields after the leading ++ // si_signo/si_errno/si_code triple: si_pid at c_int index 3 (byte 12), ++ // si_uid at index 4 (byte 16), si_status at index 8 (byte 32). Read them ++ // out of the padding region to match. ++ pub unsafe fn si_pid(&self) -> crate::pid_t { ++ let p = self as *const siginfo_t as *const c_int; ++ unsafe { *p.offset(3) } ++ } ++ pub unsafe fn si_uid(&self) -> crate::uid_t { ++ let p = self as *const siginfo_t as *const c_int; ++ unsafe { *p.offset(4) as crate::uid_t } ++ } ++ pub unsafe fn si_status(&self) -> c_int { ++ let p = self as *const siginfo_t as *const c_int; ++ unsafe { *p.offset(8) } ++ } ++} diff --git a/local/recipes/shells/brush/patches/nix-0.31-redox.patch b/local/recipes/shells/brush/patches/nix-0.31-redox.patch new file mode 100644 index 0000000000..66dcb13e69 --- /dev/null +++ b/local/recipes/shells/brush/patches/nix-0.31-redox.patch @@ -0,0 +1,89 @@ +--- a/src/lib.rs ++++ b/src/lib.rs +@@ -164,7 +164,7 @@ + #![feature = "poll"] + pub mod poll; + } +-#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] ++#[cfg(not(target_os = "fuchsia"))] + feature! { + #![feature = "term"] + #[deny(missing_docs)] +--- a/src/sys/mod.rs ++++ b/src/sys/mod.rs +@@ -95,7 +95,6 @@ + } + + #[cfg(not(any( +- target_os = "redox", + target_os = "fuchsia", + target_os = "solaris", + target_os = "haiku" +--- a/src/sys/signal.rs ++++ b/src/sys/signal.rs +@@ -856,7 +856,7 @@ + (*p).sa_flags = match handler { + #[cfg(not(target_os = "redox"))] + SigHandler::SigAction(_) => (flags | SaFlags::SA_SIGINFO).bits(), +- _ => (flags - SaFlags::SA_SIGINFO).bits(), ++ _ => (flags - SaFlags::SA_SIGINFO).bits() as _, + }; + (*p).sa_mask = mask.sigset; + +@@ -866,7 +866,7 @@ + + /// Returns the flags set on the action. + pub fn flags(&self) -> SaFlags { +- SaFlags::from_bits_truncate(self.sigaction.sa_flags) ++ SaFlags::from_bits_truncate(self.sigaction.sa_flags as _) + } + + /// Returns the set of signals that are blocked during execution of the action's +--- a/src/sys/resource.rs ++++ b/src/sys/resource.rs +@@ -21,7 +21,8 @@ + target_os = "aix", + target_os = "illumos", + all(target_os = "linux", not(target_env = "gnu")), +- target_os = "cygwin" ++ target_os = "cygwin", ++ target_os = "redox" + ))]{ + use libc::rlimit; + } +@@ -53,7 +54,8 @@ + target_os = "aix", + target_os = "illumos", + all(target_os = "linux", not(any(target_env = "gnu", target_env = "uclibc"))), +- target_os = "cygwin" ++ target_os = "cygwin", ++ target_os = "redox" + ), repr(i32))] + #[non_exhaustive] + pub enum Resource { +--- a/src/sys/wait.rs ++++ b/src/sys/wait.rs +@@ -242,6 +242,7 @@ + target_os = "android", + target_os = "freebsd", + target_os = "haiku", ++ target_os = "redox", + all(target_os = "linux", not(target_env = "uclibc")), + ))] + unsafe fn from_siginfo(siginfo: &libc::siginfo_t) -> Result { +@@ -327,6 +328,7 @@ + target_os = "android", + target_os = "freebsd", + target_os = "haiku", ++ target_os = "redox", + all(target_os = "linux", not(target_env = "uclibc")), + ))] + #[derive(Debug)] +@@ -355,6 +357,7 @@ + target_os = "android", + target_os = "freebsd", + target_os = "haiku", ++ target_os = "redox", + all(target_os = "linux", not(target_env = "uclibc")), + ))] + pub fn waitid(id: Id, flags: WaitPidFlag) -> Result { diff --git a/local/recipes/shells/brush/recipe.toml b/local/recipes/shells/brush/recipe.toml new file mode 100644 index 0000000000..71ad7bf9d9 --- /dev/null +++ b/local/recipes/shells/brush/recipe.toml @@ -0,0 +1,78 @@ +[package] +name = "brush" +version = "0.1.0" + +[source] +git = "https://github.com/reubeno/brush" + +[build] +template = "custom" +script = """ +# --------------------------------------------------------------------------- +# RedBear Redox port for brush. +# +# brush pulls `nix` 0.31.x and `libc` 0.2.x, neither of which builds for Redox +# as-is: upstream nix only partially cfg-enables Redox, and the `libc` crate's +# Redox module omits several POSIX symbols that relibc actually provides. The +# fixes live as reviewable unified diffs under patches/: +# +# patches/nix-0.31-redox.patch cfg-enable resource/Id/waitid/from_siginfo, +# rlimit import + repr(i32), SaFlags width +# casts, and pty for Redox. +# patches/libc-0.2-redox.patch add idtype_t, P_*/CLD_*, rusage, getrusage +# + waitid externs, siginfo child accessors. +# patches/brush-umask-redox.patch mode_t is signed on Redox; use a cast +# instead of u32::from (widening-only). +# +# Registry dependency sources are only unpacked during the build, so we run +# `cargo fetch` first to materialise them, then patch. apply_patch is +# idempotent (skips if already applied) but fails loudly if a patch does not +# apply cleanly -- the intended signal that a version bump moved the code. +# --------------------------------------------------------------------------- + +PATCHES="${COOKBOOK_RECIPE}/patches" + +# Materialise all dependency sources into the cargo registry before patching. +cargo fetch --manifest-path "${COOKBOOK_SOURCE}/Cargo.toml" + +# Resolve the extracted registry directory for a crate whose Cargo.lock version +# begins with the given prefix (brush may depend on several majors of the same +# crate, e.g. nix 0.26 and 0.31; the patch targets one specific series). +crate_dir() { + local name="$1" prefix="$2" ver + ver="$(awk -v n="name = \\"$1\\"" -v p="$2" ' + $0 == n { getline; gsub(/[^0-9.]/, "", $0); + if (index($0, p) == 1) { print; exit } } + ' "${COOKBOOK_SOURCE}/Cargo.lock")" + [ -n "$ver" ] || { echo "crate_dir: ${name} ${prefix}* not found in Cargo.lock" >&2; return 1; } + local d + for d in "${HOME}/.cargo/registry/src/"*/"${name}-${ver}"; do + [ -d "$d" ] && { echo "$d"; return 0; } + done + echo "crate_dir: ${name}-${ver} not extracted" >&2 + return 1 +} + +# apply_patch : apply forward, or skip if the patch is +# already applied (detected via a clean reverse dry-run). Any other failure is +# fatal. +apply_patch() { + local dir="$1" patch="$2" + if patch -p1 -R --dry-run -f -d "$dir" < "$patch" >/dev/null 2>&1; then + echo "apply_patch: $(basename "$patch") already applied in $dir, skipping" + return 0 + fi + echo "apply_patch: applying $(basename "$patch") in $dir" + patch -p1 -f -d "$dir" < "$patch" +} + +apply_patch "$(crate_dir nix 0.31)" "${PATCHES}/nix-0.31-redox.patch" +apply_patch "$(crate_dir libc 0.2)" "${PATCHES}/libc-0.2-redox.patch" +apply_patch "${COOKBOOK_SOURCE}" "${PATCHES}/brush-umask-redox.patch" + +# The shell lives in the `brush-shell` package but its binary target is named +# `brush` (see brush-shell/Cargo.toml `[[bin]] name = "brush"`), so +# cookbook_cargo_packages (which copies a binary matching the package name) +# cannot find it. Build the package explicitly and install the `brush` binary. +bin_name="brush" bin_flags="--package brush-shell --bin brush" bin_final_name="brush" cookbook_cargo_build +""" diff --git a/recipes/shells/brush b/recipes/shells/brush new file mode 120000 index 0000000000..051c905b67 --- /dev/null +++ b/recipes/shells/brush @@ -0,0 +1 @@ +../../local/recipes/shells/brush \ No newline at end of file diff --git a/recipes/wip/shells/brush/recipe.toml b/recipes/wip/shells/brush/recipe.toml deleted file mode 100644 index 233bb472e3..0000000000 --- a/recipes/wip/shells/brush/recipe.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "brush" -version = "0.1.0" - -#TODO redox is not supported by the procfs crate -[source] -git = "https://github.com/reubeno/brush" -[build] -template = "custom" -script = """ -cookbook_cargo_packages brush-shell -"""