# Package Build Quirks — Known Cross-Compilation Issues and Fixes **Created:** 2026-06-19 **Scope:** All recipes in `recipes/` and `local/recipes/` that have known build quirks **Purpose:** Central reference for cross-compilation issues encountered when building Red Bear OS packages, their root causes, and fixes. --- ## Overview Building packages for the `x86_64-unknown-redox` target from a Linux x86_64 host involves a cross-compilation environment managed by the cookbook tool. Several packages require specific adjustments to their recipe or source to build correctly in this environment. This document captures all known quirks and their fixes. --- ## Cookbook Environment: `DYNAMIC_INIT` ### Problem The cookbook's `DYNAMIC_INIT` function (defined in `src/cook/script.rs`) sets up the cross-compilation environment. **It overwrites several environment variables entirely**, including: - `CFLAGS` — set to `-I/include` - `CXXFLAGS` — set to `-I/include` - `LDFLAGS` — set to `-L/lib --static -L/lib` - `CPPFLAGS` — set to `-I/include` - `PKG_CONFIG_LIBDIR`, `PKG_CONFIG_SYSROOT_DIR` - `RUSTFLAGS` - `CARGO_TARGET_DIR` - `PATH` ### Impact Any `export FOO="..."` that runs **BEFORE** `DYNAMIC_INIT` will be overwritten. Any `export FOO="..."` that runs **AFTER** `DYNAMIC_INIT` will persist. ### Fix Pattern For variables that `DYNAMIC_INIT` overwrites, you must set them **AFTER** `DYNAMIC_INIT`: ```bash # WRONG: LDFLAGS overwritten by DYNAMIC_INIT export LDFLAGS="-Wl,--allow-multiple-definition ${LDFLAGS}" DYNAMIC_INIT # At this point, LDFLAGS = "-L/lib --static ..." (our flag is gone) # CORRECT: Set after DYNAMIC_INIT preserves the flag DYNAMIC_INIT export LDFLAGS="-Wl,--allow-multiple-definition ${LDFLAGS}" # At this point, LDFLAGS = "-Wl,--allow-multiple-definition -L/lib ..." ``` For `CFLAGS` specifically, the pre-`DYNAMIC_INIT` approach **can** work because the `reexport_flags` function (called by `cookbook_configure`) re-reads the current `$CFLAGS` and incorporates it into `CFLAGS_x86_64_unknown_redox`. But this is fragile — the safest approach is to set flags **AFTER** `DYNAMIC_INIT` for ALL variables. ### Exceptions `CFLAGS` set BEFORE `DYNAMIC_INIT` works because `reexport_flags` (called inside `cookbook_configure`) reads `$CFLAGS` at configure time and prepends it to `CFLAGS_x86_64_unknown_redox`. However, `LDFLAGS` set BEFORE `DYNAMIC_INIT` is overwritten by `DYNAMIC_INIT` and NOT re-read by `reexport_flags`. **Safe rule: Set ALL custom flags AFTER `DYNAMIC_INIT`.** ### Reference - `src/cook/script.rs` — `DYNAMIC_INIT` function definition - `src/cook/script.rs` — `reexport_flags` function definition --- ## Package: m4 (GNU M4 macro processor) **Recipe:** `local/recipes/dev/m4/recipe.toml` **Source:** `https://ftp.gnu.org/gnu/m4/m4-1.14.21.tar.xz` **Template:** `custom` (uses `cookbook_configure`) ### Quirk 1: `-fcommon` Required (GCC 10+ Compatibility) **Problem:** GCC 10+ defaults to `-fno-common`, which causes multiple definition errors for gnulib's `rpl_realloc` wrapper. The gnulib replacement function is defined as a tentative symbol across multiple translation units. **Error:** ``` /usr/bin/x86_64-unknown-redox-ld: libm4.a(libm4_a-malloc.o):(.bss+0x0): multiple definition of `rpl_realloc'; libm4_a-realloc.o:(.bss+0x0): first defined here ``` **Fix:** Add `-fcommon` to CFLAGS: ```bash export CFLAGS="-fcommon ${CFLAGS}" ``` **Placement:** BEFORE `DYNAMIC_INIT` (works because `reexport_flags` re-reads `$CFLAGS` at configure time). **Better placement (recommended for future):** AFTER `DYNAMIC_INIT` — same result, more robust. ### Quirk 2: `--allow-multiple-definition` Required (Linker) **Problem:** Even with `-fcommon`, the linker encounters multiple definitions of `rpl_realloc` because gnulib provides it in multiple object files and the static library linking order causes duplicates. **Error:** ``` /usr/bin/x86_64-unknown-redox-ld: libm4.a(libm4_a-malloc.o):(.bss+0x0): multiple definition of `rpl_realloc'; libm4_a-realloc.o:(.bss+0x0): first defined here /usr/bin/x86_64-unknown-redox-ld: error: ld returned 1 exit status ``` **Fix:** Add `--allow-multiple-definition` to LDFLAGS: ```bash DYNAMIC_INIT export LDFLAGS="-Wl,--allow-multiple-definition ${LDFLAGS}" ``` **Placement:** **MUST be AFTER `DYNAMIC_INIT`** — `DYNAMIC_INIT` overwrites `LDFLAGS` entirely. Setting it before `DYNAMIC_INIT` has no effect. ### Quirk 3: Locale Platform Detection **Problem:** m4's gnulib `getlocalename_l-unsafe.c` has a Redox case guarded by `HAVE_GETLOCALENAME_L`, which relibc doesn't define. The file hits an `#error` when neither the guard nor the platform detection matches. **Error:** ``` error: #error "Please port gnulib getlocalename_l-unsafe.c to your platform" ``` **Fix:** Changed the guard from `__redox__ && HAVE_GETLOCALENAME_L` to just `__redox__` and return `"C"` (Redox only supports the C/POSIX locale). **File:** `lib/getlocalename_l-unsafe.c` — line ~661: ```c #elif defined __redox__ return "C"; ``` **Applied via:** `local/recipes/dev/m4/source/` (local recipe with source bundled) ### Quirk 4: Configure Cache Variables **Problem:** m4's `configure` script tests for functions that don't exist or behave differently on Redox, causing false negatives. **Fix:** Pass cache variables to skip problematic tests: ```bash COOKBOOK_CONFIGURE_FLAGS+=( --disable-nls ac_cv_func___freadahead=yes ac_cv_have_decl___freadahead=yes gl_cv_header_wchar_h_correct_inline=yes gl_cv_func_btowc_nul=yes gl_cv_func_btowc_consistent=yes gl_cv_onwards_func___freadahead=yes ) ``` ### Quirk 5: wchar.h Circular Include (gnulib + relibc) **Problem:** m4's gnulib generates a `wchar.h` wrapper that does `#include_next `. relibc's `wchar.h` included `` at line 4, before defining `wint_t` (line 20) and `mbstate_t` (line 41). The circular chain: ``` wchar.h -> stdio.h -> stdint.h -> gnulib inttypes.h -> sysroot inttypes.h -> #include wchar.h -> BACK TO GNULIB wchar.h (guard not set!) ``` caused gnulib's `wchar.h` to process its replacements before `wint_t`/`mbstate_t` were defined, producing `unknown type name 'wint_t'` and `unknown type name 'mbstate_t'`. **Error:** ``` error: unknown type name 'wint_t' error: unknown type name 'mbstate_t' ``` **Root cause:** relibc's `wchar.h` included `` before defining `wint_t`/`mbstate_t`, AND relibc's `inttypes.h` included `` instead of `` + `` per POSIX spec. **Fix:** Two parts, both in the relibc fork (`local/sources/relibc/`): 1. `src/header/wchar/cbindgen.toml`: Moved `stdio.h`/`time.h` from `sys_includes` into `after_includes`, placed AFTER the `wchar_t`/`wint_t`/`mbstate_t` typedefs. Also defined `mbstate_t` in `after_includes` with a `_RELIBC_MBSTATE_T` guard and excluded it from cbindgen export via `[export] exclude = ["mbstate_t"]`. 2. `src/header/inttypes/cbindgen.toml`: Changed `sys_includes` from `["wchar.h"]` to `["stdint.h", "stddef.h"]` per POSIX spec (`inttypes.h` only needs `wchar_t` from `stddef.h`, not the full `wchar.h`). **Reference:** See the relibc section below (Quirk 3) for the same fix documented from the relibc side. Commits `d28963d` (wchar.h fix) and `a2e4cd2` (inttypes.h fix) in `local/sources/relibc/`. ### Quirk 6: `__freadahead` Prefix Staleness **Problem:** The recipe sets `ac_cv_func___freadahead=yes` to tell gnulib that relibc provides `__freadahead`. The function IS implemented in relibc's `src/header/stdio/ext.rs` (commit `047e7c0`). But the PREFIX `libc.a` was built before that commit, so the symbol was missing from the compiled library. **Error:** ``` undefined reference to `__freadahead' ``` **Fix:** Rebuild relibc in the prefix after adding new functions: ```bash touch relibc && make prefix ``` **Prevention:** The build script now warns when prefix artifacts are older than fork commits. ### Complete Recipe ```toml [source] tar = "https://ftp.gnu.org/gnu/m4/m4-1.14.21.tar.xz" patches = ["redox.patch"] [build] template = "custom" script = """ export CFLAGS="-fcommon ${CFLAGS}" DYNAMIC_INIT export LDFLAGS="-Wl,--allow-multiple-definition ${LDFLAGS}" COOKBOOK_CONFIGURE_FLAGS+=( --disable-nls ac_cv_func___freadahead=yes ac_cv_have_decl___freadahead=yes gl_cv_header_wchar_h_correct_inline=yes gl_cv_func_btowc_nul=yes gl_cv_func_btowc_consistent=yes gl_cv_onwards_func___freadahead=yes ) cookbook_configure """ [package] description = "GNU M4 macro processor" ``` **Note:** Quirks 5 and 6 do not require changes to `recipe.toml`. Quirk 5 is fixed in the relibc fork (header generation), and Quirk 6 is resolved by rebuilding the relibc prefix artifact (`touch relibc && make prefix`). --- ## Package: ninja-build (Ninja build system) **Recipe:** `local/recipes/dev/ninja-build/recipe.toml` **Source:** `https://github.com/ninja-build/ninja` (v1.13.1) **Template:** `cmake` ### Quirk: `BUILD_TESTING=OFF` Required (Cross-Compilation) **Problem:** ninja-build's `CMakeLists.txt` includes `CTest`, which enables `BUILD_TESTING=ON` by default. Tests require Google Test (gtest), which is found on the host system at `/usr/include/gtest/`. When the C++ compiler includes gtest headers, it transitively includes the host `/usr/include/stdlib.h`, which conflicts with the Redox sysroot `stdlib.h`. **Error:** ``` /usr/include/stdlib.h:67:5: error: conflicting declaration 'typedef struct div_t div_t' /usr/include/stdlib.h:75:5: error: conflicting declaration 'typedef struct ldiv_t ldiv_t' /usr/include/stdlib.h:85:5: error: conflicting declaration 'typedef struct lldiv_t lldiv_t' /usr/include/stdlib.h:761:18: error: conflicting declaration of 'int at_quick_exit(void (*)())' with 'C++' linkage ``` Additionally, the Redox sysroot `stdlib.h` uses `__noreturn` and `__deprecated` attributes that C++ doesn't recognize, causing further errors when test files are compiled. **Root cause:** This is NOT a Redox capability gap — it is a fundamental cross-compilation constraint. Test suites require a test framework (gtest) that exists on the host, and tests are designed to run on the target platform, not during cross-compilation. Building tests during cross-compile is incorrect. **Fix:** Disable test building via CMake flag: ```toml [build] template = "cmake" cmakeflags = ["-DBUILD_TESTING=OFF"] ``` This is standard practice for cross-compilation — the ninja binary itself is the deliverable, not its test suite. ### Complete Recipe ```toml [source] git = "https://github.com/ninja-build/ninja" rev = "v1.13.1" [build] template = "cmake" cmakeflags = ["-DBUILD_TESTING=OFF"] [package] description = "Ninja build system" ``` --- ## Package: kernel (Redox kernel — local fork) **Recipe:** `recipes/core/kernel/recipe.toml` (uses `path = "../../../local/sources/kernel"`) **Source:** `local/sources/kernel/` (local fork) ### Quirk: `-Zjson-target-spec` Cargo Flag **Problem:** The upstream kernel Makefile passes `-Zjson-target-spec` as a cargo CLI flag. The redoxer build environment's cargo accepts it in `cargo -Z help` but rejects it when invoked through `make` inside `redoxer env bash -ex`. **Fix:** Use the env-var form instead of the CLI flag in the kernel Makefile: ```makefile export CARGO_UNSTABLE_JSON_TARGET_SPEC=true # Remove -Zjson-target-spec from the cargo rustc command line ``` --- ## General Cross-Compilation Patterns ### Pattern: Disable Tests During Cross-Compilation When a package's build system includes test targets (cmake `BUILD_TESTING`, automake `TESTS`, meson `tests`), tests must be disabled during cross-compilation. **Why:** Tests require: 1. A test framework (gtest, catch2, etc.) that exists on the HOST, not the TARGET 2. The test framework headers transitively include HOST system headers 3. HOST system headers conflict with TARGET sysroot headers (type redefinition) 4. Tests are designed to RUN on the target, not during cross-compile **How (cmake):** ```toml cmakeflags = ["-DBUILD_TESTING=OFF"] ``` **How (meson):** ```toml mesonflags = ["-Dtests=false"] ``` **How (configure/automake):** ```bash COOKBOOK_CONFIGURE_FLAGS+=(--disable-tests) # or if no --disable-tests option: COOKBOOK_CONFIGURE_FLAGS+=(ac_cv_prog_have_gtest=no) ``` ### Pattern: Gnulib Compatibility (`-fcommon` and `--allow-multiple-definition`) Packages using gnulib (m4, bison, flex, autoconf, etc.) may need: 1. `-fcommon` in CFLAGS — gnulib provides replacement functions (`rpl_malloc`, `rpl_realloc`) that are defined as tentative symbols in multiple translation units. GCC 10+ defaults to `-fno-common`, causing multiple definition errors. 2. `-Wl,--allow-multiple-definition` in LDFLAGS — Even with `-fcommon`, the linker may encounter duplicate definitions from static library archives. **Note:** These flags should be added per-recipe only when needed, not globally. ### Pattern: Configure Cache Variables for Autoconf Packages Autoconf-based packages (m4, bison, flex, etc.) run `configure` tests that may fail on Redox because: - The function exists but behaves differently - The function doesn't exist (but the test expects it) - Cross-compilation can't run test programs Fix by passing cache variables to skip problematic tests: ```bash COOKBOOK_CONFIGURE_FLAGS+=( ac_cv_func___freadahead=yes ac_cv_have_decl___freadahead=yes gl_cv_header_wchar_h_correct_inline=yes ) ``` ### Pattern: NLS Disabled Most packages should disable NLS (Native Language Support) for Redox: ```bash COOKBOOK_CONFIGURE_FLAGS+=(--disable-nls) ``` Redox doesn't have a full gettext/libintl stack. The `--disable-nls` flag is standard for cross-compilation targets that don't need internationalization. ### Pattern: C++ Cross-Compilation Header Isolation When compiling C++ files for Redox, the compiler must use the Redox sysroot headers, not the host headers. The cookbook sets up `--sysroot` via the CMake toolchain file and `CFLAGS_x86_64_unknown_redox`/`CXXFLAGS_x86_64_unknown_redox`. If host headers leak in (usually through `#include ` or similar), the fix is to remove the dependency on host headers, not to try to isolate the include path further. This typically means disabling tests or the feature that requires the host-only dependency. --- ## Package: libiconv (GNU libiconv 1.17) ### Quirk: Libtool Version Mismatch (2.6.0.23-b08cb) #### Problem libiconv 1.17 ships a pre-generated `configure` script that embeds the libtool release identifier at configure-generation time. On Debian hosts where the system libtool has been updated to `2.6.0.23-b08cb`, the bundled configure declares `macro_version='2.6.0'`, but the running `libtool` binary advertises `VERSION=2.6.0.23-b08cb` and `package_revision=2.6.0.23`. At compile time, libtool aborts with: ``` libtool: Version mismatch error. This is libtool 2.6.0.23-b08cb, revision 2.6.0.23, libtool: but the definition of this LT_INIT comes from revision 2.6.0. libtool: You should recreate aclocal.m4 with macros from revision 2.6.0.23 libtool: of libtool 2.6.0.23-b08cb and run autoconf again. ``` This affects the nested `libcharset` subdir as well — both configure scripts need patching. #### Root Cause `ltmain.sh` (the libtool script template that ships with the host's `libtool` package) is the source of truth for the running libtool's `VERSION` and `package_revision`. The bundled `configure` script was generated against an older libtool and embeds the older `macro_version`/ `macro_revision` strings. When libtool runs at compile time it compares its own `VERSION`/`package_revision` against the values configure generated, and aborts when they differ. #### Fix Source the shared helper from the build script and rewrite both configure scripts' `macro_version` and `macro_revision` to match the host libtool: ```bash . "${COOKBOOK_ROOT}/local/scripts/lib/libtool-version-sync.sh" redbear_sync_libtool_version \ "${COOKBOOK_SOURCE}/configure" \ "${COOKBOOK_SOURCE}/libcharset/configure" ``` The helper detects the host libtool version (via `/usr/bin/libtool --version` — **must be the absolute path, not `command -v libtool`, because the cookbook prepends the Redox cross sysroot's `bin/` to PATH and `command -v libtool` would return the Redox-target libtool `2.5.4-redox-9510` instead of the host toolchain's libtool**) and rewrites the known bundled version strings (`2.4.7`, `2.5.4-redox-9510`, `2.6.0`) to the host values. Other recipes that bundle pre-generated configure scripts (older gettext, autotools packages from before the host libtool was updated) can use the same helper. #### Reference - `local/scripts/lib/libtool-version-sync.sh` — shared helper - `recipes/libs/libiconv/recipe.toml` — usage in the [build] script - `redoxer` host: `libtool (GNU libtool) 2.6.0.23-b08cb` - Upstream libtool version detection: - `libtool --version` (preferred) - `VERSION=` line in `/usr/share/libtool/build-aux/ltmain.sh` (fallback) --- ## Package: gnu-binutils (GNU Binutils 2.43.1) **Recipe:** `recipes/tools/gnu-binutils/recipe.toml` **Source:** `https://ftp.gnu.org/gnu/binutils/binutils-2.43.1.tar.xz` **Template:** `custom` (uses `cookbook_configure`) ### Quirk: Autoconf Version Lock — Skip Autoreconf **Problem:** Binutils 2.43.1 ships `config/override.m4` which enforces **exactly** Autoconf 2.69 via `_GCC_AUTOCONF_VERSION_CHECK`. If the host has Autoconf 2.73 (common on modern Debian/Ubuntu), autoreconf aborts: ``` configure.ac:21: error: Please use exactly Autoconf 2.69 instead of 2.73. ``` The original recipe used `COOKBOOK_AUTORECONF=autoreconf2.69` which requires the Debian-specific `autoconf2.69` package — not available on all distros. **Root cause:** GCC/Binutils lock their autoreconf to a specific Autoconf version. This is intentional upstream behavior, not a bug. **Fix:** Skip the autoreconf step entirely. The source tarball ships with pre-generated `configure` scripts that work correctly without regeneration. The `01_build_fix.patch` modifies `configure.ac`, but the pre-generated `configure` scripts remain functional for cross-compilation. **Recipe change:** Removed the `autotools_recursive_regenerate` call and the `COOKBOOK_AUTORECONF` override from the fetch script. Only the `config.sub`/`config.guess`/`install-sh` copy step remains. --- ## Package: gettext (GNU gettext 0.22.5) **Recipe:** `recipes/tools/gettext/recipe.toml` **Source:** `https://ftp.gnu.org/gnu/gettext/gettext-0.22.5.tar.gz` **Template:** `custom` (uses `cookbook_configure`) ### Quirk: `libintl.a` Not Installed by Default **Problem:** gettext's `make install` skips installing `libintl.a` and `libintl.h`. The Makefile has `install-exec-libintl` **commented out** with the note: "We must not install the libintl.h/libintl.la files." Downstream packages (glib, etc.) that depend on `intl` via meson's dependency lookup fail with: ``` Run-time dependency intl found: NO (tried builtin and system) ``` **Root cause:** gettext's configure detects the C library might already provide gettext functions and disables separate `libintl` installation. On Redox (relibc), gettext functions are NOT provided by the C library, so `libintl` must be installed separately. **Fix:** Two changes in the recipe build script: 1. Pass `--with-included-gettext` to configure to force building `libintl` from the included source. 2. Manually install `libintl.a` and `libintl.h` after `cookbook_configure`: ```bash cookbook_configure cp -f gettext-runtime/intl/.libs/libgnuintl.a "${COOKBOOK_STAGE}/usr/lib/libintl.a" cp -f gettext-runtime/intl/libgnuintl.h "${COOKBOOK_STAGE}/usr/include/libintl.h" ``` The library is built as `libgnuintl.a` (GNU internal name) and must be installed as `libintl.a` (POSIX name that meson/pkg-config expects). --- ## Package: glib (GLib 2.87.0) **Recipe:** `recipes/libs/glib/recipe.toml` **Source:** `https://download.gnome.org/sources/glib/2.87/glib-2.87.0.tar.xz` **Template:** `custom` (uses `cookbook_meson`) ### Quirk: Source Tarball Contains `.orig` Files (False Positive in Patch Checker) **Problem:** glib's source tarball legitimately includes `.orig` files as part of its test suite: ``` gio/tests/org.gtk.test.gschema.xml.orig gio/tests/org.gtk.test.gschema.override.orig ``` The cookbook's atomic patch checker (`src/cook/fetch.rs`) detects `.orig` files after patch application and treats them as evidence of failed hunks, rolling back the patch and aborting the build. **Root cause:** The patch checker did not distinguish between pre-existing `.orig` files (from the source tarball) and newly-created `.orig` files (from `patch` command failures). **Fix:** Updated the atomic patch checker in `src/cook/fetch.rs` to snapshot pre-existing `.orig` files before patch application, and only flag `.orig` files that were NOT in the pre-existing set. The `--no-backup-if-mismatch` flag (already used) prevents `patch` from creating new `.orig` files, so any `.orig` file found after patching that was in the pre-existing set is safe to ignore. --- ## Package: relibc (Redox C Library — local fork) **Recipe:** `recipes/core/relibc/recipe.toml` (uses `path = "../../../local/sources/relibc"`) **Source:** `local/sources/relibc/` (local fork) ### Quirk 1: cbindgen Generates `...` Instead of `va_list` **Problem:** cbindgen translates Rust's `VaList<'_>` type as `...` (variadic) in generated C headers. This affects all `v*printf` functions: ```c // Generated (WRONG): int vsnprintf(char *s, size_t n, const char *format, ...); // Expected (CORRECT): int vsnprintf(char *s, size_t n, const char *format, va_list ap); ``` This breaks C++ standard library code that takes the address of `vsnprintf` (e.g., `std::to_string(float)` → `__to_xstring(&std::vsnprintf, ...)`), causing compilation failures in Qt6 and other C++ packages. **Root cause:** cbindgen does not natively understand the `va_list` crate's `VaList` type and defaults to generating `...`. **Fix:** Post-generation `sed` in the relibc recipe build script: ```bash sed -i '/^int vfprintf(/s/\\.\\.\\./va_list ap/' "${COOKBOOK_STAGE}/usr/include/stdio.h" sed -i '/^int vsnprintf(/s/\\.\\.\\./va_list ap/' "${COOKBOOK_STAGE}/usr/include/stdio.h" sed -i '/^int vsprintf(/s/\\.\\.\\./va_list ap/' "${COOKBOOK_STAGE}/usr/include/stdio.h" ``` **Note:** The `\\.` sequences are required because TOML triple-quoted strings interpret `\\` as a literal `\`, producing the sed pattern `\.\.\.` ### Quirk 2: VaList API Version Mismatch Between Toolchains **Problem:** relibc HEAD uses `VaList<'a>` (1 lifetime) and `VaList::next_arg()` from Rust 1.98-dev. The PREFIX toolchain (nightly-2025-11-15) has the older `VaList<'a, 'f: 'a>` (2 lifetimes) with no `next_arg()` method. When building via `make live`, `mk/prefix.mk:24` exports `REDOXER_TOOLCHAIN=PREFIX`, forcing the older Rust. This causes compilation errors in relibc. **Fix:** Override `RUSTUP_TOOLCHAIN` in the relibc recipe build script to use the redoxer default toolchain (`~/.redoxer/${TARGET}/toolchain`, Rust 1.98-dev) when `REDOXER_TOOLCHAIN` is set and not pointing to a `.partial` directory (PREFIX bootstrap): ```bash REDOXER_TC="${REDOXER_TOOLCHAIN:-}" if [ -n "${REDOXER_TC}" ] && [[ "${REDOXER_TC}" != *".partial"* ]]; then REDOXER_DEFAULT="${HOME}/.redoxer/${TARGET}/toolchain" if [ -d "${REDOXER_DEFAULT}/lib/rustlib/src/rust/library/core" ]; then export RUSTUP_TOOLCHAIN="${REDOXER_DEFAULT}" export PATH="${REDOXER_DEFAULT}/bin:${PATH}" fi fi ``` ### Quirk 3: wchar.h / inttypes.h Circular Include Chain **Problem:** relibc's generated `wchar.h` included `` (via cbindgen `sys_includes`) before defining `wint_t` and `mbstate_t`. And `inttypes.h` included `` for `wchar_t` instead of using `stddef.h` per POSIX spec. This created a circular include chain that broke gnulib-based packages (m4, bison, flex, etc.). **Error:** ``` error: unknown type name 'wint_t' error: unknown type name 'mbstate_t' ``` **Root cause:** Two intertwined issues in relibc's cbindgen header generation: 1. `wchar/cbindgen.toml` listed `stdio.h` and `time.h` in `sys_includes`, which cbindgen emits at the top of the generated header, before the `wchar_t`/`wint_t`/`mbstate_t` typedefs. When a gnulib wrapper did `#include_next `, the preprocessor pulled in `stdio.h` -> `stdint.h` -> `inttypes.h` -> `wchar.h` again before any of those types were defined. 2. `inttypes/cbindgen.toml` listed `wchar.h` in `sys_includes`. Per POSIX, `inttypes.h` only needs `wchar_t` (from `stddef.h`), not the full `wchar.h`. Including `wchar.h` closed the circular chain. **Fix:** Both changes are in the relibc fork (`local/sources/relibc/`): 1. `src/header/wchar/cbindgen.toml`: `sys_includes = ["features.h"]` only. The type definitions (`wchar_t`, `wint_t`, `mbstate_t`) and the `stdio.h`/ `time.h` includes were moved to `after_includes`, so the typedefs land before any transitive include. `mbstate_t` is defined in `after_includes` under a `_RELIBC_MBSTATE_T` guard, and excluded from cbindgen export via `[export] exclude = ["mbstate_t"]`. 2. `src/header/inttypes/cbindgen.toml`: `sys_includes = ["stdint.h", "stddef.h"]` (POSIX spec compliant, breaks the circular chain by removing the `inttypes.h` -> `wchar.h` edge). **Commits:** `d28963d` (wchar.h fix), `a2e4cd2` (inttypes.h fix) in `local/sources/relibc/`. **Reference:** The m4 section (Quirk 5) documents the same fix from the consuming package's perspective. --- ## Package: qtbase (Qt 6 Base — local recipe) **Recipe:** `local/recipes/qt/qtbase/recipe.toml` **Source:** `local/recipes/qt/qtbase/source/` (local overlay with tar source) **Template:** `custom` ### Quirk 1: Source Tarball Not Extracted for Local Overlay Recipes **Problem:** The cookbook treats `local/recipes/qt/qtbase/source/` as a local overlay (immutable). The `is_local_overlay()` check prevents wiping and re-extracting the source from `source.tar`. As a result, only the Red Bear-specific files (`tests/`, `util/`) exist in `source/`, but the full Qt6 source code is missing. **Error:** ``` FileNotFoundError: No such file or directory: '.../source/src/tools/CMakeLists.txt' ``` **Fix:** Manually extract the source tarball into the `source/` directory: ```bash cd local/recipes/qt/qtbase tar xf source.tar --strip-components=1 -C source/ ``` This must be done once after updating the source tarball. Patches must also be applied manually since the cookbook won't apply them to local overlay sources: ```bash cd source patch -p1 --batch --no-backup-if-mismatch < ../redox.patch ``` **Affected recipes:** All Qt6 local overlay recipes with `tar` sources: `qtbase`, `qt5compat`, `qtdeclarative`, `qtsvg`, `qtshadertools`, `qtwayland`. ### Quirk 2: `forkfd.h` Missing `` Include **Problem:** `src/3rdparty/forkfd/forkfd.h` declares functions using `struct rusage` but only includes ``. On Linux glibc, `` transitively includes `` (which defines `struct rusage`). On Redox/relibc, this transitive include doesn't exist. **Error:** ``` forkfd.h:57: warning: 'struct rusage' declared inside parameter list forkfd.c:918: error: conflicting types for 'forkfd_wait4' ``` **Fix:** Add `#include ` to `forkfd.h`: ```c #include #include #include #include // Added for struct rusage ``` ### Quirk 3: `netinet/in6_pktinfo_compat.h` Not Available **Problem:** `src/network/socket/qnativesocketengine_unix.cpp` includes ``, which relibc does not provide. **Fix:** The `P0-fix-broken-include.patch` removes this include line. Ensure the patch is applied to the source (see Quirk 1 about manual patch application). --- ## Package: redox-driver-sys (Driver System Library) **Recipe:** `local/recipes/drivers/redox-driver-sys/` **Source:** `local/recipes/drivers/redox-driver-sys/source/` (local recipe) ### Quirk: Inline Assembly Register Constraints for I/O Port Access **Problem:** The `io.rs` module uses inline assembly for x86 `IN`/`OUT` instructions with generic register constraints (`in(reg)`, `out(reg_byte)`). The x86 `IN`/`OUT` instructions require specific registers: - Port: must be `DX` - Data: must be `AL` (byte), `AX` (word), or `EAX` (dword) Generic constraints let the compiler choose any register, producing invalid instructions like `outb ax, esi` instead of `outb dx, al`. **Error:** ``` error: invalid operand for instruction --> :2:2 | 2 | outb ax, esi | ^ ``` **Fix:** Use explicit register operands in `src/io.rs`: ```rust pub fn inb(port: u16) -> u8 { let val: u8; unsafe { core::arch::asm!("in al, dx", in("dx") port, out("al") val, options(nomem, nostack, preserves_flags)) }; val } pub fn outb(port: u16, val: u8) { unsafe { core::arch::asm!("out dx, al", in("dx") port, in("al") val, options(nomem, nostack, preserves_flags)) }; } ``` Apply the same pattern for `inw`/`outw` (`ax`/`dx`) and `inl`/`outl` (`eax`/`dx`). --- ## Package: mesa (Mesa 3D Graphics Library) **Recipe:** `recipes/libs/mesa/recipe.toml` **Source:** `https://gitlab.redox-os.org/redox-os/mesa.git` (branch `redox-24.0`) **Template:** `custom` (meson-based) ### Status (2026-06-20): ALL RED BEAR PATCHES WIRED Mesa builds successfully for the `x86_64-unknown-redox` target. The recipe's `patches=[...]` list contains all six Red Bear Mesa patches, in order: | # | Patch | Purpose | |---|-------|---------| | 01 | `local/patches/mesa/01-virgl-redox-disk-cache.patch` | Nullifies virgl's disk shader cache on Redox (no `XDG_CACHE_HOME` semantics on Redox) | | 02 | `local/patches/mesa/02-gbm-dumb-prime-export.patch` | Adds `gbm_dri_bo_get_fd` fallback path via DRM dumb buffers (PRIME export on Redox) | | 03 | `local/patches/mesa/03-platform-redox-gpu-probe.patch` | EGL platform probe: tells `platform_redox.c` to enumerate `/scheme/drm/cardN` and try virgl-capable GPUs | | 04 | `local/patches/mesa/04-sys-ioccom-stub-header.patch` | New file `include/sys/ioccom.h` (63 lines) implementing BSD-style `IOCPARM_LEN` / `_IO` / `_IOW` / `_IOR` macros for DRM UAPI ioctl encoding | | 05 | `local/patches/mesa/05-vk-sync-wchar-include.patch` | Adds `#include ` to `vk_sync.h` for `wchar_t` (relibc does not transitively pull in `` via `` the way glibc does) | | 06 | `local/patches/mesa/06-redox-surface-image-fields.patch` | Adds `__DRIimage *dri_image_back;` and `__DRIimage *dri_image_front;` to the `HAVE_REDOX_PLATFORM` section of `egl_dri2.h` (see quirk below) | Build artifacts (verified 2026-06-20 12:30): - `recipes/libs/mesa/target/x86_64-unknown-redox/stage/usr/lib/dri/virtio_gpu_dri.so` (17,474,080 bytes) - `stage.pkgar` published to `local/cache/pkgar/mesa/` ### Quirk: `dri_image_back` / `dri_image_front` fields missing from Redox platform section (RESOLVED) **Problem:** `src/egl/drivers/dri2/platform_redox.c` references `dri2_surf->dri_image_back` and `dri2_surf->dri_image_front`, but these fields are only defined inside `#ifdef HAVE_ANDROID_PLATFORM` in `egl_dri2.h`. The Redox platform section (`#ifdef HAVE_REDOX_PLATFORM`) only had `orb_window` and `orb_surface`. **Error (prior to fix):** ``` platform_redox.c:65:18: error: 'struct dri2_egl_surface' has no member named 'dri_image_back' ``` **Root cause:** When `03-platform-redox-gpu-probe.patch` was added to teach `platform_redox.c` to allocate GBM-backed images for the EGL surface (so the virgl probe can attach to a real GPU), it referenced `dri_image_back` and `dri_image_front`. These fields existed only for the Android platform because the original EGL/Redox port only used `orb_window` / `orb_surface` (the Orbital display server's native window/buffer types). A complete Redox EGL platform needs both the Orbital legacy fields AND the DRI image fields so a Wayland/DRM-style compositor can attach GBM-backed buffers. **Fix:** Patch `06-redox-surface-image-fields.patch` adds the missing fields to the `HAVE_REDOX_PLATFORM` section of `src/egl/drivers/dri2/egl_dri2.h`: ```c #ifdef HAVE_REDOX_PLATFORM void *orb_window; void *orb_surface; __DRIimage *dri_image_back; __DRIimage *dri_image_front; #endif ``` The patch is a 13-line unified diff. With this fix: - `platform_redox.c` (post-`03-platform-redox-gpu-probe.patch`) compiles cleanly - `redox_free_images()` can now free the GBM-backed images - The EGL platform probe can attach a virgl backend to a Redox DRM/KMS surface **Verification**: Mesa `cook` succeeds, `virtio_gpu_dri.so` links, `stage.pkgar` produced. **Runtime caveat**: The build is verified. The runtime EGL platform selection (`EGL_PLATFORM=redox`, choosing `virtio_gpu_dri.so` vs `swrast_dri.so`) is **not yet runtime-validated** in a QEMU `-device virtio-vga-gl` boot. This is the last remaining gap for hardware-accelerated SDDM greeter rendering. ### Why `-Dstatic_assert(...)=` Mesa's upstream `configure` (meson) emits `-Dstatic_assert=_Static_assert` when `_Static_assert` is unavailable on the target. On glibc/Linux, this collides with the `` `static_assert` macro. Redox's relibc doesn't define `static_assert` as a macro, so the explicit `=` nullifies the macro and lets Mesa use the C11 keyword `_Static_assert` instead. The recipe applies this via `-Dc_args="['-Wno-error=implicit-function-declaration','-Wno-error','-std=gnu11','-Dstatic_assert=_Static_assert']"`. --- ## Package: sddm (Simple Desktop Display Manager — Wayland-only build) **Recipe:** `local/recipes/kde/sddm/recipe.toml` **Source:** `https://github.com/sddm/sddm.git` (rev `63780fcd79f1dbf81a30eef48c28c699ab15aded`) **Template:** `custom` (cmake-based) **Status (2026-06-20): BUILDS — Wayland-only, all X11 excluded ### Build-Side Quirks SDDM's CMake build assumes X11/XCB/XAUTH are available. Red Bear OS has none of these. The recipe handles this in three coordinated ways: 1. **`wayland-patch.sh` (~245 lines, sed-based)** — applied at build time to: - Remove `find_package(XCB ...)` calls (CMakeLists) - Remove XCB include/library refs in `src/{daemon,helper,greeter}/CMakeLists.txt` - Replace `cmake/FindXKB.cmake`'s xcb-xkb lookups with xkbcommon - Remove `XcbKeyboardBackend.cpp` from the greeter - Rewrite `src/common/XAuth.cpp` as a Wayland-only stub - Replace `Qt5.15.0 CONFIG REQUIRED ...` with the components SDDM needs (Core, DBus, Gui, Network, Qml, Quick) + OPTIONAL_COMPONENTS LinguistTools/Test/QuickTest - Gate `qt_add_translation` on `Qt6LinguistTools_FOUND` - Guard the `test/CMakeLists.txt` block on `Qt6Test_FOUND AND Qt6QuickTest_FOUND` 2. **`remove-x11user-helper.py`** — strips the `sddm-helper-start-x11user` add_executable and `install(TARGETS sddm-helper-start-x11user ...)` from `src/helper/CMakeLists.txt`, plus the `target_link_libraries(sddm-helper-start-x11user ${JOURNALD_LIBRARIES})` line. 3. **`stubs/` directory** — provides fake headers for headers SDDM `#include`s but relibc does not yet provide: - `linux/kd.h`, `linux/vt.h` — VT ioctl definitions (SDDM only uses the ioctl numbers, not the kernel-side semantics) - `utmpx.h` — POSIX `utmpx` (SDDM only compiles against it; runtime stub) - `X11/Xauth.h` — minimal stub matching the rewritten `XAuth.cpp` (Wayland-only) **Note on stubs**: Per `local/AGENTS.md` zero-tolerance policy, the `X11/Xauth.h` stub is a temporary carrier because the corresponding `XAuth.cpp` is rewritten as a no-op for Wayland-only mode. When relibc gains a real `utmpx.h`, the `utmpx.h` stub here should be removed and SDDM should compile against the relibc version. The `linux/kd.h` and `linux/vt.h` stubs should be replaced by a proper `redbear-input-headers`-style header package in a future cleanup pass. ### CMake flags that make the build work The recipe passes these critical flags: ```bash cmake "${COOKBOOK_SOURCE}" \ -DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \ -DQT_HOST_PATH="${HOST_BUILD}" \ -DKF6_HOST_TOOLING="${HOST_BUILD}/lib/cmake" \ -DBUILD_TESTING=OFF \ -DBUILD_WITH_QT6=ON \ -DNO_SYSTEMD=ON \ -DENABLE_JOURNALD=OFF \ -DENABLE_PAM=ON \ -DUID_MIN=1000 \ -DUID_MAX=65000 \ -DLIBXKB_INCLUDE_DIR="${COOKBOOK_SYSROOT}/usr/include" \ -DLIBXKB_LIBRARIES="${COOKBOOK_SYSROOT}/usr/lib/libxkbcommon.so" \ -Wno-dev ``` Key flags: - `-DBUILD_WITH_QT6=ON` — required; SDDM defaults to Qt5 - `-DNO_SYSTEMD=ON` — required; no systemd on Redox - `-DENABLE_JOURNALD=OFF` — required; no journald on Redox - `-DENABLE_PAM=ON` — required; `pam-redbear` provides `libpam.so.0` - `-DUID_MIN=1000 -DUID_MAX=65000` — required at CMake time; SDDM hardcodes these via `add_definitions` if not provided - `-DQT_HOST_PATH` and `-DKF6_HOST_TOOLING` — required for `moc`/`qmltyperegistrar`/ECMs tools that must run on the host (they cannot cross-compile for the Redox target) ### Build verification (2026-06-20) - `cook sddm - successful` (0 errors) - Stage output: - `usr/bin/sddm` — main daemon - `usr/bin/sddm-greeter-qt6` — QML greeter binary - `usr/libexec/sddm-helper` — session spawn helper - `usr/libexec/sddm-helper-start-wayland` — Wayland session starter (forks compositor + greeter) - `usr/share/sddm/` — themes, scripts, translations - `etc/pam.d/{sddm,sddm-greeter,sddm-autologin}` — PAM service files - `patchelf --set-rpath "/usr/lib"` applied to all binaries so dynamic linking works against the sysroot at runtime ### Runtime caveat The QML greeter (`sddm-greeter-qt6`) is a Qt6 Wayland client and is therefore gated on the Qt6 Wayland `null+8` crash fix (see `CONSOLE-TO-KDE-DESKTOP-PLAN.md` §3.1). The daemon and helpers will start under init, but the greeter QML window will not appear until the null+8 fix is runtime-validated. --- ## General Pattern: Source Tarball Extraction for Local Overlay Recipes **Problem:** When a recipe lives under `local/recipes/` (local overlay), the cookbook's `is_local_overlay()` check prevents wiping and re-extracting the source from `source.tar`. If the `source/` directory only contains Red Bear-specific files (tests, utils, etc.), the main source code is missing. **Affected recipes:** Any local overlay recipe that uses `tar = "..."`: - `qtbase`, `qt5compat`, `qtdeclarative`, `qtsvg`, `qtshadertools`, `qtwayland` - Possibly others added in the future **Fix:** Extract the tarball manually before building: ```bash cd tar xf source.tar --strip-components=1 -C source/ # Apply patches manually: cd source for patch in ../*.patch; do patch -p1 --batch --no-backup-if-mismatch < "$patch" || true done # Clean up rejects: find . -name "*.rej" -delete ``` **Future improvement:** The cookbook could be enhanced to support "extract-and-overlay" mode for local recipes with `tar` sources — extracting the tarball first, then overlaying the local `source/` files. --- ## Adding a New Quirk When you encounter a new build quirk: 1. **Document it here** — add a section under the appropriate package, or create a new package section. 2. **Explain the root cause** — why does this happen? 3. **Show the fix** — what change resolves it? 4. **Note the placement** — should the fix go before or after `DYNAMIC_INIT`? 5. **Is it a cross-compilation pattern?** — if so, add it to the General Patterns section so future recipes can benefit. 6. **Update the recipe** — apply the fix to `recipe.toml`. 7. **Update UPSTREAM-SYNC-PROCEDURE.md** — if the quirk is related to upstream sync. --- ## Package: base-initfs (initfs image builder) ### pcid Must Start Before pcid-spawner **Problem:** `pcid-spawner` calls `fs::read_dir("/scheme/pci")` as its first action. `/scheme/pci` is created by `pcid` via `register_sync_scheme()`. If `pcid` is not started, `pcid-spawner` fails with `ENODEV`. **Root cause:** No init service started `pcid` in initfs. The upstream model assumed `pcid` would be started, but no `.service` file existed for it. This was masked in 0.2.3 because `driver-manager` (Red Bear's replacement) does its own PCI enumeration via `redox_driver_pci` and doesn't depend on `/scheme/pci`. **Fix:** Added `35_pcid.service` to `init.initfs.d/`: ```ini [unit] description = "PCI bus enumeration daemon (creates /scheme/pci)" requires_weak = ["30_acpid.service"] [service] cmd = "pcid" type = { scheme = "pci" } ``` The `type = { scheme = "pci" }` tells init to wait for `/scheme/pci` to be registered before marking the service as started. This ensures `pcid-spawner` (which depends on `35_pcid.service` via `requires_weak`) only runs after `/scheme/pci` exists. ### driver-manager Removed During Sync **Problem:** The 0.2.3 → 0.2.4 upstream sync silently removed `driver-manager` from `base-initfs` dependencies and `redbear-device-services.toml` packages. **Root cause:** Upstream doesn't have `driver-manager` — it's a Red Bear in-house project. The sync blindly overwrote tracked files with upstream versions. **Fix:** Restored: - `"driver-manager"` in `base-initfs/recipe.toml` dependencies - `cp driver-manager` binary copy to initfs/bin/ - `driver-manager = {}` in `redbear-device-services.toml` `[packages]` - `drivers.d/` config directory creation in build script - `set -eo pipefail` in build script **See also:** `UPSTREAM-SYNC-PROCEDURE.md` § "driver-manager: Upstream Not Ready" --- ## Redox Flag Values (Critical for Ported Code) Redox flag values differ from Linux. Code ported from Linux that assumes Linux conventions will be WRONG on Redox: | Flag | Linux | Redox | |------|-------|-------| | `O_RDONLY` | `0` | `0x0001_0000` | | `O_WRONLY` | `1` | `0x0002_0000` | | `O_RDWR` | `2` | `0x0003_0000` | | `O_ACCMODE` | `3` | `0x0003_0000` | | `O_CLOEXEC` | `0x80000` | `0x0100_0000` | **Common bug:** `flags & O_ACCMODE == O_RDONLY` evaluates to `0 == 0` on Linux (read-only check passes), but on Redox `O_CLOEXEC & O_ACCMODE = 0 ≠ O_RDONLY`, so a file opened with `O_CLOEXEC` without explicit `O_RDONLY` fails with `EROFS`. **Fix:** Always pass `O_RDONLY` explicitly when opening for read: ```rust let fd = Fd::open(path, O_RDONLY | O_CLOEXEC, 0)?; ``` ### Kernel kcall on ACPI Scheme **Problem:** Upstream-synced `acpid` uses `AcpiVerb` call API (`ReadRxsdt=1`, `CheckShutdown=2`) but the kernel's `AcpiScheme` didn't implement `kcall`, returning `EOPNOTSUPP`. **Fix:** Added `kcall` method to `impl KernelScheme for AcpiScheme` in `src/scheme/acpi.rs`. The kernel fork commit is `c7af6c43`. **Signature:** ```rust fn kcall(&self, fds: &[usize], payload: UserSliceRw, flags: CallFlags, metadata: &[u64], token: &mut CleanLockToken) -> Result ``` - `fds[0]` = handle id - `metadata[0]` = verb (u64): `1` = ReadRxsdt, `2` = CheckShutdown - `payload` = UserSliceRw (read/write buffer) --- ## Cross-Reference | Document | Relevance | |----------|-----------| | `local/docs/UPSTREAM-SYNC-PROCEDURE.md` | Per-package sync notes include build quirks | | `AGENTS.md` | Project-level build commands and conventions | | `local/AGENTS.md` | Local fork model, recipe structure | | `src/cook/script.rs` | `DYNAMIC_INIT`, `reexport_flags`, `cookbook_cmake` definitions | | `src/recipe.rs` | Recipe TOML schema (`cmakeflags`, `configureflags`, etc.) |