diff --git a/AGENTS.md b/AGENTS.md index 0eb3db0e97..6f065a98af 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -421,6 +421,62 @@ KDE recipes, and all recipes carrying Red Bear patches (Qt, DRM, Mesa, Wayland, glib, etc.). Any recipe with a `redox.patch` or local patches is a candidate for protection — add it when adding patches. +### Local Fork Priority and Version Sync (MANDATORY) + +**Rule**: If a recipe exists in `local/sources//` (a local fork), it +**always takes precedence** over the upstream version in `recipes//`. +However, the build system **must guarantee** that the local fork is at the +**latest available version** and that **all Red Bear patches are applied cleanly** +on top of that version. + +**Rationale**: Red Bear forks are designed to be drop-in compatible with +upstream. Transitive dependencies from crates.io may pull in newer versions +of shared crates (e.g. `redox_syscall`, `redox-scheme`). If the local fork is +at a lower version than what the dependency graph requires, two consequences +follow: +1. **Lockfile collision**: Cargo sees `redox_syscall v0.8.1 (local/sources/syscall)` + and `redox_syscall v0.8.1 (recipes/core/base/syscall)` as different sources + even when they resolve to the same directory via symlink. +2. **Type mismatch**: Transitive deps built against `redox_syscall 0.9.x` (from + crates.io) produce types incompatible with our local `0.8.x` fork, + causing `error[E0308]: mismatched types` at link/compile time. + +**Detection**: The build system MUST detect these conditions automatically. +On every build, the cookbook MUST: +1. Compare the version in `local/sources//Cargo.toml` against the + highest version required by any transitive dependency in the build graph. +2. Compare the version in `local/sources//Cargo.toml` against the + upstream `recipes//source/Cargo.toml` (if available). +3. If the local version is lower than either: + a. Record the required version in a manifest (`local/sources//.required-version`) + b. Emit a clear actionable error: `LOCAL FORK OUTDATED: local/sources/ is at vX.Y.Z but vA.B.C is required by ` + c. Do NOT silently proceed with a broken build. + +**Remediation (automatic, when invoked)**: The `local/scripts/bump-fork.sh` +script (or equivalent `repo bump-fork ` command) MUST: +1. Fetch the upstream source at the required version. +2. Apply all Red Bear patches from `local/patches//` using the + atomic patch application mechanism (see "Atomic Patch Application" below). +3. Update the version field in the local fork's `Cargo.toml`. +4. Commit the result to the `submodule/` branch in the RedBear-OS repo. +5. Rebuild the affected packages. + +**Symlink aliasing**: `recipes//` MUST be a symlink to +`../../../local/sources//` when a local fork exists. This ensures +backward compatibility for recipes that reference the `recipes/` path while +still preferring the local fork as the source of truth. + +**Implementation status**: Detection is partially implemented via Cargo's +own lockfile collision errors. Full automatic detection and remediation +requires changes to `src/cook/fetch.rs` (version comparison logic) and the +addition of `local/scripts/bump-fork.sh`. Until fully automated, the manual +process is: +1. `cd local/sources/` +2. Edit `Cargo.toml` version field to match upstream +3. `git pull` or fetch upstream at the new tag +4. Reapply all `local/patches//*.patch` (see AGENTS.md "Atomic Patch Application") +5. Commit to `submodule/` branch + ### Offline-First By Default Red Bear OS is a **fork with frozen sources**. The cookbook tool defaults to diff --git a/local/recipes/drivers/ehcid/source/Cargo.toml b/local/recipes/drivers/ehcid/source/Cargo.toml index 2919168aab..e98156c0a8 100644 --- a/local/recipes/drivers/ehcid/source/Cargo.toml +++ b/local/recipes/drivers/ehcid/source/Cargo.toml @@ -10,11 +10,15 @@ path = "src/main.rs" [dependencies] usb-core = { path = "../../usb-core/source" } -libredox = { version = "0.1", features = ["call", "std"] } +libredox = { path = "../../../../../local/sources/libredox", features = ["call", "std"] } log = { version = "0.4", features = ["std"] } redox-driver-sys = { path = "../../redox-driver-sys/source" } -redox-scheme = "0.11" -syscall = { package = "redox_syscall", version = "0.8", features = ["std"] } +redox-scheme = "<0.11.2" +syscall = { package = "redox_syscall", path = "../../../../../local/sources/syscall", features = ["std"] } [target.'cfg(target_os = "redox")'.dependencies] redox-driver-sys = { path = "../../redox-driver-sys/source", features = ["redox"] } + +[patch.crates-io] +redox_syscall = { path = "../../../../../local/sources/syscall" } +libredox = { path = "../../../../../local/sources/libredox" } diff --git a/local/recipes/drivers/linux-kpi/source/Cargo.toml b/local/recipes/drivers/linux-kpi/source/Cargo.toml index cc7d5021e9..c42d1fd25f 100644 --- a/local/recipes/drivers/linux-kpi/source/Cargo.toml +++ b/local/recipes/drivers/linux-kpi/source/Cargo.toml @@ -6,8 +6,8 @@ description = "Linux Kernel API compatibility layer for Redox OS (LinuxKPI-style license = "MIT" [dependencies] -libredox = "0.1" -redox_syscall = { version = "0.8", features = ["std"] } +libredox = { path = "../../../../../local/sources/libredox" } +redox_syscall = { path = "../../../../../local/sources/syscall", features = ["std"] } log = "0.4" thiserror = "2" lazy_static = "1.4" @@ -15,3 +15,7 @@ redox-driver-sys = { path = "../../redox-driver-sys/source" } [lib] crate-type = ["rlib", "staticlib"] + +[patch.crates-io] +redox_syscall = { path = "../../../../../local/sources/syscall" } +libredox = { path = "../../../../../local/sources/libredox" } diff --git a/local/recipes/drivers/ohcid/source/Cargo.toml b/local/recipes/drivers/ohcid/source/Cargo.toml index d047fd542e..d9d31691c2 100644 --- a/local/recipes/drivers/ohcid/source/Cargo.toml +++ b/local/recipes/drivers/ohcid/source/Cargo.toml @@ -10,5 +10,9 @@ path = "src/main.rs" [dependencies] usb-core = { path = "../../usb-core/source" } -redox_syscall = "0.8" +redox_syscall = { path = "../../../../../local/sources/syscall" } log = "0.4" + +[patch.crates-io] +redox_syscall = { path = "../../../../../local/sources/syscall" } +libredox = { path = "../../../../../local/sources/libredox" } diff --git a/local/recipes/drivers/redbear-btusb/source/Cargo.toml b/local/recipes/drivers/redbear-btusb/source/Cargo.toml index cc19a9aa7f..ad22334e6b 100644 --- a/local/recipes/drivers/redbear-btusb/source/Cargo.toml +++ b/local/recipes/drivers/redbear-btusb/source/Cargo.toml @@ -9,7 +9,11 @@ path = "src/main.rs" [dependencies] libc = "0.2" -libredox = { version = "0.1", features = ["call", "std"] } +libredox = { path = "../../../../../local/sources/libredox", features = ["call", "std"] } log = { version = "0.4", features = ["std"] } -redox-scheme = "0.11" -syscall = { package = "redox_syscall", version = "0.8", features = ["std"] } +redox-scheme = "<0.11.2" +syscall = { package = "redox_syscall", path = "../../../../../local/sources/syscall", features = ["std"] } + +[patch.crates-io] +redox_syscall = { path = "../../../../../local/sources/syscall" } +libredox = { path = "../../../../../local/sources/libredox" } diff --git a/local/recipes/drivers/redox-driver-pci/source/Cargo.toml b/local/recipes/drivers/redox-driver-pci/source/Cargo.toml index 219e902275..b7cdd3c5c9 100644 --- a/local/recipes/drivers/redox-driver-pci/source/Cargo.toml +++ b/local/recipes/drivers/redox-driver-pci/source/Cargo.toml @@ -6,4 +6,8 @@ description = "PCI bus backend for redox-driver-core" [dependencies] redox-driver-core = { path = "../../redox-driver-core/source" } -redox_syscall = "0.8" +redox_syscall = { path = "../../../../../local/sources/syscall" } + +[patch.crates-io] +redox_syscall = { path = "../../../../../local/sources/syscall" } +libredox = { path = "../../../../../local/sources/libredox" } diff --git a/local/recipes/drivers/redox-driver-sys/source/Cargo.toml b/local/recipes/drivers/redox-driver-sys/source/Cargo.toml index 8f67e50988..42537e238c 100644 --- a/local/recipes/drivers/redox-driver-sys/source/Cargo.toml +++ b/local/recipes/drivers/redox-driver-sys/source/Cargo.toml @@ -5,8 +5,8 @@ edition = "2021" description = "Safe Rust wrappers for Redox OS scheme-based hardware access" [dependencies] -libredox = "0.1.0" -redox_syscall = { version = "0.8", features = ["std"] } +libredox = { path = "../../../../../local/sources/libredox" } +redox_syscall = { path = "../../../../../local/sources/syscall", features = ["std"] } log = "0.4" thiserror = "2" bitflags = "2" @@ -28,3 +28,7 @@ linux-kpi = { path = "../../linux-kpi/source" } name = "smoke_test" harness = false required-features = ["redox"] + +[patch.crates-io] +redox_syscall = { path = "../../../../../local/sources/syscall" } +libredox = { path = "../../../../../local/sources/libredox" } diff --git a/local/recipes/drivers/uhcid/source/Cargo.toml b/local/recipes/drivers/uhcid/source/Cargo.toml index 318d903eba..2e80c921cb 100644 --- a/local/recipes/drivers/uhcid/source/Cargo.toml +++ b/local/recipes/drivers/uhcid/source/Cargo.toml @@ -10,5 +10,9 @@ path = "src/main.rs" [dependencies] usb-core = { path = "../../usb-core/source" } -redox_syscall = "0.8" +redox_syscall = { path = "../../../../../local/sources/syscall" } log = "0.4" + +[patch.crates-io] +redox_syscall = { path = "../../../../../local/sources/syscall" } +libredox = { path = "../../../../../local/sources/libredox" } diff --git a/local/recipes/drivers/virtio-inputd/source/Cargo.toml b/local/recipes/drivers/virtio-inputd/source/Cargo.toml index 0fc64a5c7b..8ce6bc0333 100644 --- a/local/recipes/drivers/virtio-inputd/source/Cargo.toml +++ b/local/recipes/drivers/virtio-inputd/source/Cargo.toml @@ -11,9 +11,13 @@ path = "src/main.rs" [dependencies] anyhow = "1" log = "0.4" -libredox = { version = "=0.1.16", features = ["call", "std"] } -redox_syscall = { version = "0.7", features = ["std"] } +libredox = { path = "../../../../../local/sources/libredox", features = ["call", "std"] } +redox_syscall = { path = "../../../../../local/sources/syscall", features = ["std"] } redox-driver-sys = { path = "../../redox-driver-sys/source" } -syscall = { package = "redox_syscall", version = "0.7", features = ["std"] } +syscall = { package = "redox_syscall", path = "../../../../../local/sources/syscall", features = ["std"] } inputd = { path = "../../../../sources/base/drivers/inputd" } common = { path = "../../../../sources/base/drivers/common" } + +[patch.crates-io] +redox_syscall = { path = "../../../../../local/sources/syscall" } +libredox = { path = "../../../../../local/sources/libredox" } diff --git a/local/recipes/system/driver-manager/source/Cargo.toml b/local/recipes/system/driver-manager/source/Cargo.toml index 9df4f0ee22..1b11aa3207 100644 --- a/local/recipes/system/driver-manager/source/Cargo.toml +++ b/local/recipes/system/driver-manager/source/Cargo.toml @@ -12,8 +12,11 @@ path = "src/main.rs" redox-driver-core = { path = "../../../drivers/redox-driver-core/source" } redox-driver-pci = { path = "../../../drivers/redox-driver-pci/source" } pcid_interface = { path = "../../../../../local/sources/base/drivers/pcid", package = "pcid" } -redox-scheme = "0.11" -syscall = { package = "redox_syscall", version = "0.8" } +redox-scheme = "=0.11.1" log = "0.4" toml = "0.8" serde = { version = "1", features = ["derive"] } + +[patch.crates-io] +redox_syscall = { path = "../../../../../local/sources/syscall" } +libredox = { path = "../../../../../local/sources/libredox" } \ No newline at end of file diff --git a/local/scripts/bump-fork.sh b/local/scripts/bump-fork.sh new file mode 100755 index 0000000000..fb5883dd72 --- /dev/null +++ b/local/scripts/bump-fork.sh @@ -0,0 +1,165 @@ +#!/usr/bin/env bash +# bump-fork.sh — Auto-bump a local fork to match upstream version +# +# Usage: bump-fork.sh [--version=X.Y.Z] +# +# If --version is provided, bumps to that version. +# If --version is omitted, queries Cargo to determine the required version +# from the build graph. +# +# Implements: AGENTS.md "Local Fork Priority and Version Sync (MANDATORY)" + +set -euo pipefail + +COMPONENT="${1:-}" +if [[ -z "$COMPONENT" ]]; then + echo "Usage: $0 [--version=X.Y.Z]" + echo "" + echo "Components with local forks:" + for d in local/sources/*/; do + if [[ -d "$d" ]] && [[ -f "${d}Cargo.toml" ]]; then + echo " $(basename "$d")" + fi + done + exit 1 +fi + +shift + +VERSION="" +while [[ $# -gt 0 ]]; do + case "$1" in + --version=*) + VERSION="${1#*=}" + shift + ;; + *) + echo "Unknown option: $1" >&2 + exit 1 + ;; + esac +done + +LOCAL_DIR="local/sources/${COMPONENT}" +RECIPE_DIR="recipes/core/${COMPONENT}" + +if [[ ! -d "$LOCAL_DIR" ]]; then + echo "ERROR: $LOCAL_DIR does not exist" >&2 + exit 1 +fi + +# Detect current local version +if [[ -f "${LOCAL_DIR}/Cargo.toml" ]]; then + CURRENT_VERSION=$(grep -E '^version\s*=' "${LOCAL_DIR}/Cargo.toml" | head -1 | sed 's/.*"\(.*\)".*/\1/') + echo "Current local version: ${CURRENT_VERSION:-unknown}" +else + echo "WARNING: ${LOCAL_DIR}/Cargo.toml not found — not a Cargo crate?" + CURRENT_VERSION="" +fi + +# Detect required version from build graph if not specified +if [[ -z "$VERSION" ]]; then + echo "Querying build graph for required version of ${COMPONENT}..." + + # Search Cargo.lock files for the required version + REQUIRED_VERSION="" + while IFS= read -r lockfile; do + # Look for [[package]] entries that match this component + entry=$(awk ' + /^name = "'"${COMPONENT}"'"$/ { found=1; next } + found && /^version = / { gsub(/"/, ""); print; exit } + ' "$lockfile" 2>/dev/null) + if [[ -n "$entry" ]]; then + REQUIRED_VERSION="$entry" + break + fi + done < <(find . -name 'Cargo.lock' -not -path '*/.git/*' 2>/dev/null) + + if [[ -z "$REQUIRED_VERSION" ]]; then + echo "ERROR: Could not determine required version for ${COMPONENT}" >&2 + echo " Pass --version=X.Y.Z explicitly" >&2 + exit 1 + fi + + VERSION="$REQUIRED_VERSION" + echo "Required version (from Cargo.lock): ${VERSION}" +fi + +# Check if bump is needed +if [[ -n "$CURRENT_VERSION" ]] && [[ "$CURRENT_VERSION" == "$VERSION" ]]; then + echo "Local fork already at v${VERSION} — no bump needed" + exit 0 +fi + +echo "Bumping ${COMPONENT}: v${CURRENT_VERSION:-unknown} → v${VERSION}" + +# Fetch upstream source at the required version +echo "Fetching upstream source at v${VERSION}..." + +# Find the upstream repo URL from the recipe +if [[ -f "${RECIPE_DIR}/recipe.toml" ]]; then + UPSTREAM_GIT=$(grep -E '^git\s*=' "${RECIPE_DIR}/recipe.toml" | head -1 | sed 's/.*"\(.*\)".*/\1/') +fi + +if [[ -z "${UPSTREAM_GIT:-}" ]]; then + echo "ERROR: Cannot determine upstream git URL from ${RECIPE_DIR}/recipe.toml" >&2 + echo " Set up the recipe or pass --upstream-git=" >&2 + exit 1 +fi + +echo "Upstream git: ${UPSTREAM_GIT}" + +# Fetch into a staging directory +STAGING_DIR="/tmp/redbear-bump-${COMPONENT}-$$" +trap "rm -rf ${STAGING_DIR}" EXIT + +echo "Cloning ${UPSTREAM_GIT} at v${VERSION} into ${STAGING_DIR}..." +git clone --depth 1 --branch "v${VERSION}" "${UPSTREAM_GIT}" "${STAGING_DIR}" 2>/dev/null \ + || git clone --depth 1 "${UPSTREAM_GIT}" "${STAGING_DIR}" + +cd "${STAGING_DIR}" +git fetch --tags +if git tag -e "v${VERSION}" 2>/dev/null; then + git checkout "v${VERSION}" +else + echo "WARNING: tag v${VERSION} not found — using main branch HEAD" +fi +cd - >/dev/null + +# Apply Red Bear patches +PATCH_DIR="local/patches/${COMPONENT}" +if [[ -d "$PATCH_DIR" ]]; then + echo "Applying Red Bear patches from ${PATCH_DIR}..." + for patch in "${PATCH_DIR}"/*.patch; do + if [[ -f "$patch" ]]; then + echo " Applying $(basename "$patch")..." + if ! patch -p1 -d "${STAGING_DIR}" --dry-run < "$patch" >/dev/null 2>&1; then + echo "ERROR: Patch $(basename "$patch") fails to apply to upstream v${VERSION}" >&2 + echo " Manual intervention required — patch may need rebasing" >&2 + exit 1 + fi + patch -p1 -d "${STAGING_DIR}" < "$patch" + fi + done +fi + +# Update version in Cargo.toml +if [[ -f "${STAGING_DIR}/Cargo.toml" ]]; then + sed -i "s/^version = \"${CURRENT_VERSION}\"/version = \"${VERSION}\"/" "${STAGING_DIR}/Cargo.toml" +fi + +# Replace local fork contents with staged (atomic via temp dir rename) +echo "Updating ${LOCAL_DIR}..." +mv "${LOCAL_DIR}" "${LOCAL_DIR}.old-${$$}" +mv "${STAGING_DIR}" "${LOCAL_DIR}" +rm -rf "${LOCAL_DIR}.old-${$$}" + +echo "" +echo "SUCCESS: ${COMPONENT} bumped to v${VERSION}" +echo "" +echo "Next steps:" +echo " 1. cd ${LOCAL_DIR}" +echo " 2. git diff (verify patches applied correctly)" +echo " 3. git add -A && git commit -m 'bump: ${COMPONENT} v${CURRENT_VERSION} -> v${VERSION}'" +echo " 4. git push origin submodule/${COMPONENT}" +echo " 5. Rebuild affected packages: ./local/scripts/build-redbear.sh redbear-mini" \ No newline at end of file diff --git a/local/sources/relibc b/local/sources/relibc index 26595f1624..89a4aa8a05 160000 --- a/local/sources/relibc +++ b/local/sources/relibc @@ -1 +1 @@ -Subproject commit 26595f1624563770e8d9f8a516cd8f74b4c0407d +Subproject commit 89a4aa8a05c073b7a24762a7959c0a3f99d4f223