cub: add assessment plan + 12-PKGBUILD integration test harness (v6.0 2026)

The cub AUR→RBPKGBUILD→recipe.toml conversion pipeline (located at
local/recipes/system/cub/source/) was assessed end-to-end against
12 representative real-world PKGBUILDs:

  - libevdev (simple meson)
  - fd-find (cargo)
  - libpciaccess 0.18.1 (meson)
  - fmt (cmake)
  - wlroots-git (git source, complex deps)
  - libpciaccess 0.19 (extra/-style, meson + ninja)
  - ffmpeg (configure + options)
  - mesa 24.3 (git+url + multi-source + pkgver())
  - gzip (configure + git source + check)
  - zlib (simple C, configure)
  - openssl (pkgbase split package)
  - glib2 (complex deps, real-world)

The assessment found 8 critical bugs that would prevent cub from
producing working Red Bear recipes for any real Arch package. 7 of
the 8 bugs were fixed in the previous commit (7c5b1f36e); the 8th
(custom-template recipes lack DYNAMIC_INIT and cookbook_apply_patches
boilerplate) is deferred as a cookbook-integration concern.

This commit adds two artifacts of the assessment:

  1. local/docs/cub-assessment-and-improvement-plan.md (508 lines,
     ~28KB): the complete assessment document. Sections:
       - Executive summary (architecture decision + 8-bug verdict)
       - What cub does well (10+ working cases)
       - The 8 bugs (location, severity, root cause, fix)
       - Test methodology
       - Test cases by category (A: conversion success, B: dep mapping,
         C: source URL, D: build template, E: edge cases, F: validation)
       - Forward improvement plan (16 items in 4 tiers)
       - Appendix A: cub architecture map (CLI + 17 modules)
       - Appendix B: RBPKGBUILD format spec
       - Appendix C: Generated recipe format vs. real Red Bear recipe

  2. local/recipes/system/cub/source/cub-assessment/:
     a 12-PKGBUILD integration test harness. A standalone binary that
     exercises the conversion pipeline on each PKGBUILD and reports
     status, warnings, action_items, recipe validity (TOML), and the
     first 30 lines of the generated recipe. Used to verify the bug
     fixes in 7c5b1f36e — all 12 cases convert successfully
     post-fix, including the previously-erroring mesa 24.3 (which
     now produces a valid recipe with a multi-source warning).

The test harness lives next to the cub source (cub-assessment/) and
has its own Cargo.toml with [workspace] empty so it doesn't join the
cub workspace. Build/run with:

  cd local/recipes/system/cub/source
  cargo run --manifest-path cub-assessment/Cargo.toml

The harness is intended for use by future cub maintainers to catch
regressions. It's not wired into CI yet — that would be a separate
task.
This commit is contained in:
2026-06-10 10:01:44 +03:00
parent 7c5b1f36eb
commit 0080fac138
4 changed files with 894 additions and 0 deletions
@@ -0,0 +1,508 @@
# cub Assessment and Improvement Plan (v6.0 2026)
**Date:** 2026-06
**Status:** Assessment complete, critical fixes in progress
**Scope:** Comprehensive empirical review of cub's AUR → RBPKGBUILD → Redox recipe.toml conversion pipeline, with concrete bug fixes and a forward improvement plan.
---
## Executive Summary
cub (Red Bear OS package manager, located at `local/recipes/system/cub/source/`) is a substantial 17-module Rust workspace with a real, working AUR → RBPKGBUILD → recipe.toml conversion pipeline. The architecture is sound: a 937-line `pkgbuild` parser, a 408-line `rbpkgbuild` serializer, a 465-line `cookbook` recipe generator, plus AUR fetching, dependency mapping, sandbox, cook, storage, and resolver modules. All 70 unit tests pass and the source compiles cleanly.
However, the conversion pipeline has **8 known bugs** that would prevent any real-world Arch package from converting to a working Red Bear recipe. This assessment is based on running the pipeline against 12 representative real-world PKGBUILDs and observing the output.
**Verdict:** cub is the right tool for the job. The architecture is correct, the code is mostly right, and the bugs are surgical fixes — not architectural rewrites. The first 7 bugs are being fixed in commit `<hash>` (this PR). The 8th is deferred as a cookbook-integration concern.
---
## What cub does well
The 12-PKGBUILD assessment found that cub's conversion pipeline handles these cases correctly:
| Case | Status |
|---|---|
| Simple meson package (libevdev) | ✅ clean conversion |
| Cargo package (fd-find) | ✅ clean conversion |
| CMake package (fmt) | ✅ clean conversion |
| Configure package (libpciaccess 0.19) | ✅ clean conversion |
| Git source with `git+url#tag=` (gzip, mesa 24.3 single-source) | ✅ clean conversion |
| Split package with `pkgbase` + `package_<name>()` (openssl) | ⚠️ converts primary only, warns about split |
| Custom build template (raw shell script) | ✅ extracts build/package bodies |
| `pkgver()` function (dynamic version) | ⚠️ ignored with warning |
| `sha256sums=('SKIP')` for git sources | ✅ correct handling |
| TOML validity of generated recipe | ✅ always valid |
| Detection of Linux-only deps (libx11, libxcb, libinput, alsa) | ✅ correctly flags as unmapped |
| Linuxism detection (glibc, x11, gtk, etc.) | ✅ warns + actions_required |
The unit tests (70 in total) cover the rbpkgbuild, rbsrcinfo, sandbox, storage, recipe, resolver modules. The test coverage is solid for the serializer/parser side.
---
## The 8 bugs
### Bug 1 — `glibc` mapped to `relibc` (CRITICAL)
**Location:** `cub-lib/src/deps.rs:13`
**Severity:** Critical — affects every Linux C library consumer
**Symptom:** `depends=('glibc')` produces `[package] dependencies = ["relibc"]` in the generated recipe
**Root cause:** The dependency mapping hard-codes:
```rust
"glibc" => ("relibc".to_string(), false),
```
**Why wrong:** glibc is the Linux C library; relibc is the Redox C library, which is part of the OS itself (not a package). Putting relibc as a runtime dependency in a Redox recipe is a tautology — every Redox program already links against relibc implicitly. Worse, it suggests relibc is a separately-installable package, which it isn't.
**Fix:** Drop the mapping. Use the standard "empty string" pattern that cub already uses for other Linux-only dependencies (systemd, xorgproto, libcap, libpulse, etc.):
```rust
"glibc" => (String::new(), false),
```
The `map_dep_list` function already filters out empty mappings.
**Status:** Fixed in this commit.
### Bug 2 — `gcc-libs` mapped to `gcc` (CRITICAL)
**Location:** `cub-lib/src/deps.rs:15`
**Severity:** Critical — affects every Arch package that links libstdc++/libgcc
**Symptom:** `depends=('gcc-libs')` produces `[package] dependencies = ["gcc"]` in the generated recipe
**Root cause:** The mapping hard-codes:
```rust
"gcc-libs" => ("gcc".to_string(), false),
```
**Why wrong:** gcc-libs is the **runtime** libgcc + libstdc++ (analogous to Arch's `gcc-libs`). gcc is the **compiler**. They're entirely different artifacts. Mapping runtime C++ libraries to a compiler creates broken cross-compilation graphs (the cookbook would try to build gcc as a runtime dep).
**Fix:** Drop the mapping, same as Bug 1. relibc provides the runtime; the compiler gcc is a host-only build tool (covered by Bug 3):
```rust
"gcc-libs" => (String::new(), false),
```
**Status:** Fixed in this commit.
### Bug 3 — Build tools not prefixed with `host:` (CRITICAL)
**Location:** `cub-lib/src/deps.rs` (entire `map_dependency` function)
**Severity:** Critical — affects every package that uses standard build tools
**Symptom:** Generated recipes list `meson`, `ninja`, `cmake`, `make`, `pkg-config`, `git`, `perl`, `python`, `rust`, `cargo`, `autoconf`, `automake`, `libtool`, etc. as bare `[build] dependencies`, without the `host:` prefix
**Root cause:** The hard-coded mapping table returns bare names. The Redox cookbook convention is to mark build tools as `host:<tool>` so the cookbook knows they're host-only and not part of the cross-compiled Redox target.
The current `prefix_host_deps` function in `cookbook.rs` already handles `host:` prefixing for `dev-dependencies` (the `check` deps) but not for `dependencies` (the `makedepends`).
**Why wrong:** Without `host:`, the Redox cookbook would try to *cook* meson, ninja, and cmake as Redox packages, which is wrong — they're host tools that run on the build host, not target tools that get cross-compiled.
**Fix:** Add a `BUILD_TOOLS: &[&str]` constant containing all known build tools. In `map_dependency`, when the name matches a build tool, return `MappedDep { mapped: "host:<name>", is_exact: true }` instead of the bare name.
The set of build tools (~30 entries): `make`, `cmake`, `ninja`, `meson`, `pkg-config`, `pkgconf`, `pkgconfig`, `autoconf`, `automake`, `libtool`, `m4`, `git`, `svn`, `mercurial`, `perl`, `python`, `python2`, `python3`, `rust`, `cargo`, `rustc`, `go`, `golang`, `bison`, `flex`, `yacc`, `gperf`, `gettext`, `intltool`, `help2man`, `gengetopt`, `xmlto`, `asciidoc`, `doxygen`, `graphviz`, `swig`.
**Status:** Fixed in this commit.
### Bug 4 — AUR `git+url#tag=branch` source syntax not parsed (CRITICAL)
**Location:** `cub-lib/src/pkgbuild.rs` (the `source_from_arch` function or equivalent)
**Severity:** Critical — affects every AUR git package
**Symptom:** Generated recipes contain literal `git+https://...git#tag=mesa-24.3.0` as the source URL, which is invalid Redox cookbook format
**Root cause:** AUR PKGBUILDs use Arch-style source notation. `source_from_arch` does not strip the `git+` prefix or extract the `#tag=branch` fragment.
**Real example:** `mesa 24.3` PKGBUILD has:
```bash
source=("git+https://gitlab.freedesktop.org/mesa/mesa.git#tag=mesa-${pkgver}")
```
cub produces: `git = "git+https://gitlab.freedesktop.org/mesa/mesa.git#tag=mesa-${pkgver}"` in the recipe.
**Fix:** Modify `source_from_arch` (or wherever source URLs are normalized) to:
1. Check if the source starts with `git+` — if so, set `source_type = SourceType::Git` and strip the prefix
2. Check for a `#` fragment — if present, split off and parse the fragment. Common fragment keys:
- `tag=...``SourceEntry::branch` (since the Redox cookbook uses `branch` for tags)
- `branch=...``SourceEntry::branch`
- `commit=...` or `revision=...``SourceEntry::rev`
3. For git sources without a fragment, set `branch = "HEAD"` as a reasonable default
The Redox cookbook's git source format is:
```toml
[source]
git = "https://..."
branch = "v1.0" # or rev = "..."
```
**Status:** Fixed in this commit.
### Bug 5 — Multi-source PKGBUILDs not supported (HIGH)
**Location:** `cub-lib/src/cookbook.rs:80-100`
**Severity:** High — blocks mesa, ffmpeg, and many real packages
**Symptom:** Multi-source PKGBUILDs (e.g., mesa with 2 sources: git repo + llvmpipe_generated.h.tar.xz) error with:
```
Error: multiple sources not yet supported (found 2: https://..., https://...).
Use single-source packages or manually create recipe.toml
```
**Root cause:** The `generate_recipe` function explicitly errors on multi-source. The `convert_pkgbuild` function preserves all sources in the `RbPkgBuild` but `generate_recipe` only handles 1.
**Why wrong:** Many real packages have multiple sources (a primary git repo + an auxiliary file). Forcing the user to manually create the recipe for these defeats the purpose of automated conversion.
**Fix:** In `convert_pkgbuild` (in `pkgbuild.rs`), after the full source array is parsed, **truncate to the first source only** and add a warning to the `ConversionReport`:
```rust
if sources.len() > 1 {
warnings.push(format!(
"PKGBUILD has {} sources; using the first as primary ({}). Auxiliary sources dropped: {}",
sources.len(),
sources[0],
sources[1..].join(", ")
));
actions_required.push(
"review multi-source PKGBUILD and re-add auxiliary sources manually".to_string()
);
sources.truncate(1);
}
```
This way `generate_recipe` doesn't need to know about multi-source, and the user gets a clear warning + action item.
**Status:** Fixed in this commit.
### Bug 6 — `options=()` flags silently dropped (MEDIUM)
**Location:** `cub-lib/src/pkgbuild.rs` (no extraction at all)
**Severity:** Medium — silent data loss
**Symptom:** PKGBUILDs with `options=('!lto' '!emptydirs' '!strip')` lose all option flags. The generated recipe has no trace of them.
**Root cause:** The current `convert_pkgbuild` doesn't extract `options=()`.
**Why wrong:** Options like `!lto` (disable link-time optimization) and `!strip` (don't strip binaries) can be required for a package to build correctly. Silently dropping them creates recipes that fail in ways the user can't predict.
**Fix:** Extend the `CompatSection` struct in `rbpkgbuild.rs` to include an `options: Vec<String>` field. Extract `options=()` in `convert_pkgbuild` and store it. The recipe generator doesn't need to consume it — the user reads the `compat.options` field in the RBPKGBUILD and re-adds the relevant flags to their Redox recipe.
**Status:** Fixed in this commit.
### Bug 7 — `${pkgver}` literal in source URL (HIGH)
**Location:** `cub-lib/src/pkgbuild.rs` (source array not passed through `resolve_shell_vars`)
**Severity:** High — affects every AUR package that uses `${pkgver}` in its source URL
**Symptom:** Generated recipes have `tar = ".../libevdev-${pkgver}.tar.xz"` with the literal `${pkgver}` string. The Redox cookbook doesn't do shell variable substitution at source-URL time, so the resulting tar URL is invalid.
**Root cause:** The `convert_pkgbuild` function calls `resolve_shell_vars` on `pkgname`, `pkgver`, `pkgdesc`, `url` but NOT on the `source` array.
**Why wrong:** The recipe is broken as soon as it's generated. The user would have to manually fix every source URL.
**Fix:** After extracting the `source` array, apply `resolve_shell_vars` to each entry. Since `pkgver` is already resolved (it's a top-level scalar in the PKGBUILD), the substitution should be straightforward. The existing `resolve_shell_vars` function is the right tool.
**Status:** Fixed in this commit.
### Bug 8 — Recipes lack cookbook boilerplate (DEFERRED)
**Location:** `cub-lib/src/cookbook.rs` (the `custom_script` function)
**Severity:** Medium — affects every Custom-template package
**Symptom:** Custom-template recipes generate raw shell scripts without the Redox cookbook's `DYNAMIC_INIT` prelude or the `cookbook_apply_patches` helper that the Rule 2 migration recipes use.
**Root cause:** `custom_script` (in `cookbook.rs:244-282`) joins `prepare`, `build_script`, `check`, `install_script`, and install lines into a single script. It does not prepend `DYNAMIC_INIT` (which is required for any Redox cookbook shell script to find the cookbook helpers like `cookbook_meson`, `cookbook_apply_patches`, etc.).
**Why wrong:** A real Redox recipe that doesn't start with `DYNAMIC_INIT` will fail at build time because the shell helpers aren't loaded.
**Fix:** Prepend `DYNAMIC_INIT\n` to the generated `custom_script`. Add an optional `redox_compat/` shim dir or `cookbook_apply_patches "${REDBEAR_PATCHES_DIR}"` call if the package has patches (this requires the user to declare patches in the RBPKGBUILD).
**Status:** Deferred. This is a cookbook-integration concern that intersects with the project's Rule 2 patch policy. A separate task should:
1. Decide whether the cub-generated recipe's `patches` field is treated as the source-of-truth (per Rule 2) or as auxiliary patches
2. Add a `local/patches/<component>/` directory generation step to cub
3. Update the `custom_script` to include `DYNAMIC_INIT` and `cookbook_apply_patches`
---
## Test methodology
The `cub-assessment` test binary at `local/recipes/system/cub/source/cub-assessment/src/main.rs` is the integration test that exercises 12 representative real-world PKGBUILDs:
1. `libevdev` — simple meson (reference case)
2. `fd-find` — cargo
3. `libpciaccess 0.18.1` — meson
4. `fmt` — cmake
5. `wlroots-git` — git source, complex deps
6. `libpciaccess 0.19` (extra/-style) — meson + ninja
7. `ffmpeg` — configure + options
8. `mesa 24.3` — git+url + multi-source + pkgver() (used to fail pre-fix)
9. `gzip` — configure + git source + check
10. `zlib` — simple C, configure
11. `openssl` — pkgbase split package
12. `glib2` — complex deps, real-world
**Pre-fix results (before this commit):**
- 11 of 12 converted successfully
- 1 of 12 errored (mesa 24.3, multi-source)
- All recipes valid TOML
- Several recipes had wrong deps (glibc→relibc, gcc-libs→gcc)
- All git+url sources kept literal prefix
- All multi-source attempts failed
**Post-fix results (after this commit):**
- All 12 convert successfully
- mesa 24.3 produces a valid recipe with a multi-source warning
- glibc, gcc-libs, x11, alsa, libinput are correctly dropped/flagged
- Build tools are correctly `host:`-prefixed
- git+url sources parse correctly into branch/rev fields
- 70 existing unit tests still pass
---
## Test cases by category
### A. Conversion path success (verifies the core pipeline)
-`convert_pkgbuild` returns Ok for all 12 cases
-`generate_recipe` returns Ok for all 12 cases post-fix
- ✅ All generated recipes parse as valid TOML
### B. Dependency mapping correctness
-`glibc` → dropped (empty string)
-`gcc-libs` → dropped (empty string)
-`glib2``glib` (exact)
-`openssl``openssl3` (inexact, warning)
-`zlib``zlib` (exact)
-`meson`, `ninja`, `cmake``host:meson`, etc. (post-fix)
-`libinput` → dropped with action_required
-`libx11`, `libxcb`, `libxrandr` → dropped (no Redox mapping)
### C. Source URL handling
- ✅ Tar source with `${pkgver}` → resolved to actual version (post-fix)
- ✅ Git source with `git+url#tag=v1.0` → git+ stripped, branch=v1.0 (post-fix)
- ✅ Git source without fragment → branch=HEAD (post-fix)
- ✅ Multi-source → first kept, others warned (post-fix)
-`sha256sums=('SKIP')` → ignored, source_type=Git
### D. Build template detection
- ✅ meson template (cookbook_meson path)
- ✅ cmake template (cookbook_cmake path)
- ✅ cargo template (cookbook_cargo path)
- ✅ configure template (cookbook_configure path)
- ✅ Custom template (raw script path)
### E. Edge cases
- ✅ Split package (pkgbase + pkgname array + package_* functions)
- ✅ pkgver() function (ignored with warning)
- ✅ options=() (preserved in compat section, post-fix)
- ✅ check() function (extracted when allow_tests=true)
### F. Validation
- ✅ RbPkgBuild.validate() passes for all
- ✅ Generated RBSRCINFO is valid
- ✅ All arch values are "x86_64-unknown-redox"
- ✅ All licenses are preserved
---
## Forward improvement plan (post-fix)
After the 7 critical bug fixes, the conversion pipeline produces recipes that are *valid TOML with correct deps* but still need work to be *buildable in the Redox cookbook*. Here's the prioritized follow-up list:
### Tier 1: Cookbook integration (Bugs 8 + others)
1. **Custom template boilerplate** — prepend `DYNAMIC_INIT` to all `custom_script` outputs. Without this, the recipe will fail at runtime because cookbook helpers like `cookbook_meson` aren't loaded.
2. **Patch application hook** — for recipes with `patches.files` non-empty, automatically emit `cookbook_apply_patches "${REDBEAR_PATCHES_DIR}"` in the custom script (matching the Rule 2 migration pattern established in commits `1a291fbb9` and `b0f440c47`).
3. **Systematic dep audit** — the 80-entry hard-coded dep map in `deps.rs` is a maintenance burden. Migrate to a data-driven table that lives in `local/docs/dependency-mapping.md` (or a similar canonical source) and is loaded at runtime. This makes the mapping auditable and easy to extend.
4. **Per-package overrides** — sometimes a single package needs a non-standard dep. For example, `mesa` needs `llvm21` not `llvm`. Add a `compat.dependency_overrides: HashMap<String, String>` field in `RBPKGBUILD` that overrides the hard-coded mapping.
### Tier 2: Conversion quality
5. **Better Linuxism detection** — currently flags `glibc`, `x11`, `alsa`, `libpulse` as Linux-only. Many AUR packages have implicit Linux-only paths (e.g., `/proc/cpuinfo`, `/sys/class/...`, `/dev/...`) that the heuristic doesn't catch.
6. **Multi-source preservation** — when truncating to 1 source, the user loses access to auxiliary files (e.g., a gitignore or systemd unit). Generate a `redox/aux-sources.txt` next to the recipe listing the dropped sources, so the user can manually re-add them.
7. **`pkgver()` execution** — for git sources, the Arch convention is to have a `pkgver()` function that calls `git describe` to compute the version. This is impossible to execute in cub (we don't have a git clone at conversion time). Generate a static `version = "GIT"` and add a `compat.notes = "git source — version detected dynamically"` annotation.
8. **License mapping** — Arch licenses like `('GPL3' 'Apache-2.0')` are SPDX-like but not exact. Map to SPDX identifiers and warn when an Arch license doesn't have a Red Bear SPDX equivalent.
### Tier 3: UX
9. **`cub -S <pkg>` should run end-to-end** — currently the AUR → recipe → cook flow is fragmented (separate `get-aur`, `build` subcommands). The user should be able to say `cub -S mesa` and get a built Redox package.
10. **Conversion report** — the `ConversionReport` already exists but is only printed by `get-aur` and `import-aur`. The `cook` and `install` subcommands should print the report after the recipe is generated so the user can see warnings and action items.
11. **TUI integration** — the cub TUI at `cub-tui/` should show a "Convert from AUR" action that drives the full conversion and shows the report.
12. **BUR (Redox User Repo) sync**`cub -Sy` syncs from BUR at `https://gitlab.redox-os.org/redox-os/bur.git`. This works but the BUR is sparse. A "publish" subcommand would let users contribute their converted recipes back to BUR.
### Tier 4: Infrastructure
13. **Network access policy** — AUR fetching requires network access. The project's `REPO_OFFLINE=1` default blocks this. Add a `cub config set aur-fetch off` knob to disable AUR fetches in offline mode while keeping BUR sync (which uses git's local cache) working.
14. **Cache management** — cub caches AUR metadata in `/tmp/cub_cache/`. On Redox this maps to ramfs and is volatile. A persistent cache in `~/.cub/cache/` (or in the `var/lib/packages` directory) would survive reboots.
15. **Test coverage** — 70 unit tests is good, but the integration tests (the 12-PKGBUILD assessment) should be moved into `cargo test` so they run in CI. Currently they're in a separate `cub-assessment` sub-crate that nobody runs automatically.
16. **Sparse AUR metadata** — instead of cloning the full AUR git repo for every package, use the AUR RPC API (`https://aur.archlinux.org/rpc/?v=5&type=info&arg=<pkg>`) to get just the metadata, then clone only the source. This is already partially implemented in `aur.rs` but the full flow isn't exercised in tests.
---
## Files involved
- `local/recipes/system/cub/source/cub-lib/src/deps.rs` — bug 1, 2, 3
- `local/recipes/system/cub/source/cub-lib/src/pkgbuild.rs` — bugs 4, 5, 6, 7
- `local/recipes/system/cub/source/cub-lib/src/cookbook.rs` — bug 5, 8
- `local/recipes/system/cub/source/cub-lib/src/rbpkgbuild.rs` — bug 6 (CompatSection extension)
- `local/recipes/system/cub/source/cub-lib/src/converter.rs` — bug 4 (also has its own `convert_pkgbuild`)
- `local/recipes/system/cub/source/cub-assessment/src/main.rs` — integration test (no changes)
---
## Conclusion
cub is the right tool for the job. The architecture is correct (PKGBUILD → RbPkgBuild → recipe.toml is a clean three-stage pipeline), the existing code is mostly right (70 unit tests pass, all 12 real-world PKGBUILDs convert to valid TOML), and the bugs are surgical fixes that don't require architectural changes.
After the 7 critical fixes in this commit, cub will be a useful Red Bear package tool. The remaining 16 follow-up items in the forward plan address cookbook integration, conversion quality, UX, and infrastructure — they're substantial but well-scoped.
The recommendation is to ship the 7 fixes, then use cub to bootstrap the next 5-10 missing recipes (zlib, libffi, pcre2, freetype, libpng, etc.) as a real-world validation. If the generated recipes cook successfully, we proceed to scale up cub-driven recipe generation. If they fail, the next round of fixes will be more targeted because we'll have concrete error messages.
---
## Appendix A: cub architecture map
```
cub-cli (binary)
├── main.rs
│ ├── Cli (clap derive)
│ ├── Commands: Install, Search, Info, Sync, SystemUpgrade, Build,
│ │ Get, GetAur, GetPkgbuild, Inspect, ImportAur, UpdateAll,
│ │ Remove, QueryLocal, QueryInfo, QueryList, CleanCache, CleanAll
│ ├── Aliases: -S, -Ss, -Si, -Sy, -Syu, -B, -G, -R, -Q, -Qi, -Ql,
│ │ -Pi, --import-aur, -Sua, -Sc, -Scc, -Gp
│ └── launch_tui_or_help() / run_command()
├── CookbookAdapter → calls cub::cookbook::generate_recipe()
├── PkgbuildConverter → calls pkgbuild::convert_pkgbuild()
└── PackageCreator → calls cub::package (gated on `full` feature)
cub-lib (library)
├── lib.rs → re-exports all modules
├── aur.rs → AUR RPC client + AurPackage
├── converter.rs → simpler convert_pkgbuild (used by tests)
├── cook.rs → invokes `repo cook` on the generated recipe
├── cookbook.rs → RbPkgBuild → cookbook recipe.toml
│ (THE PIPELINE ENDPOINT)
├── deps.rs → Arch → Red Bear dep mapping
│ (Bugs 1, 2, 3 live here)
├── depresolve.rs → dependency resolution across packages
├── error.rs → CubError enum
├── package.rs → pkgar creation (gated on `full` feature)
├── pkgbuild.rs → PKGBUILD → RbPkgBuild (937 lines)
│ (Bugs 4, 5, 6, 7 live here)
├── rbpkgbuild.rs → RbPkgBuild TOML serialization (408 lines)
├── rbpkgbuild tests
├── rbsrcinfo.rs → .SRCINFO format reader/writer
├── recipe.rs → save_recipe_to_store, etc.
├── resolver.rs → check_missing_header, library, pkgconfig
├── sandbox.rs → build sandboxing for repo cook
└── storage.rs → CubStore: ~/.cub/ directory management
cub-tui (Ratatui TUI)
├── app.rs → TUI state machine
├── lib.rs → public API: cub_tui::run()
└── theme.rs → colors and styles
```
## Appendix B: RBPKGBUILD format
cub's intermediate format (between PKGBUILD and recipe.toml):
```toml
format = 1
[package]
name = "mesa"
version = "24.3.0"
release = 1
description = "Open-source implementation of the OpenGL specification"
homepage = "https://mesa.freedesktop.org"
license = ["MIT"]
architectures = ["x86_64-unknown-redox"]
maintainers = []
[[source]]
type = "git"
url = "https://gitlab.freedesktop.org/mesa/mesa.git"
sha256 = ""
rev = ""
branch = "mesa-24.3.0"
[dependencies]
build = ["host:meson", "host:ninja", "llvm"]
runtime = ["libdrm", "wayland"]
check = []
optional = []
provides = []
conflicts = []
[build]
template = "meson"
release = true
features = []
args = []
build_dir = ""
prepare = []
build_script = []
check = []
install_script = []
[install]
bins = []
libs = []
headers = []
docs = []
man = []
[compat]
imported_from = "aur"
original_pkgbuild = "..." # full PKGBUILD text
conversion_status = "Full" # or "Partial"
target = "x86_64-unknown-redox"
split_packages = []
options = [] # added in this commit
[policy]
allow_network = false
allow_tests = false
review_required = false
```
## Appendix C: Generated recipe.toml format
What cub produces (post-fix):
```toml
[source]
git = "https://gitlab.freedesktop.org/mesa/mesa.git"
shallow_clone = true
branch = "mesa-24.3.0"
[build]
template = "meson"
dependencies = [
"host:meson",
"host:ninja",
"llvm",
]
[package]
dependencies = [
"libdrm",
"wayland",
]
version = "24.3.0-1"
description = "Open-source implementation of the OpenGL specification"
```
What a real Red Bear recipe looks like (e.g., `local/recipes/libs/mesa/recipe.toml`):
```toml
# Comprehensive header comment block explaining Rule 2 + migration rationale
[source]
git = "https://gitlab.redox-os.org/redox-os/mesa"
branch = "redox-24.0"
[build]
template = "custom"
dependencies = [
"expat",
"libdrm",
"libwayland",
"llvm21",
"wayland-protocols",
"zlib",
]
dev-dependencies = [
"llvm21.dev",
]
script = """
DYNAMIC_INIT
# Apply Red Bear OS external patches (per local/AGENTS.md Rule 2)
cookbook_apply_patches "${REDBEAR_PATCHES_DIR}"
# TODO: Should be CPPFLAGS but cookbook_meson isn't reading it
export CFLAGS+=" -DHAVE_PTHREAD=1 -I${COOKBOOK_SYSROOT}/include/libdrm"
...
cookbook_meson -Dplatforms=wayland -Dvirgl=enabled ...
"""
[package]
version = "0.2.3"
description = "mesa 0.2.3 (v6.0 2026) — Mesa 3-D graphics library, Red Bear OS fork..."
```
The cub-generated recipe is **structurally correct** but **stylistically and contextually different** from a real Red Bear recipe. The forward plan addresses this gap.
@@ -0,0 +1 @@
/target
@@ -0,0 +1,10 @@
[package]
name = "cub-assessment"
version = "0.1.0"
edition = "2021"
[workspace]
[dependencies]
cub-lib = { path = "../cub-lib" }
toml = "0.8"
@@ -0,0 +1,375 @@
// cub_assessment.rs - Exercise cub's PKGBUILD→RBPKGBUILD→recipe pipeline
// on representative real-world PKGBUILDs to identify failures.
use cub::pkgbuild::convert_pkgbuild;
use cub::cookbook::generate_recipe;
fn assess(label: &str, pkgbuild: &str) {
println!("\n=== {label} ===");
match convert_pkgbuild(pkgbuild) {
Ok(conversion) => {
println!("conversion: ok, status={:?}", conversion.report.status);
for w in &conversion.report.warnings {
println!(" WARN: {w}");
}
for a in &conversion.report.actions_required {
println!(" ACTION: {a}");
}
match generate_recipe(&conversion.rbpkg) {
Ok(recipe) => {
println!("recipe: ok, {} bytes", recipe.len());
// Verify the recipe is valid TOML
match toml::from_str::<toml::Value>(&recipe) {
Ok(_) => println!("recipe TOML: valid"),
Err(e) => println!("recipe TOML: INVALID - {e}"),
}
// Print first 30 lines of recipe
for (i, line) in recipe.lines().take(30).enumerate() {
println!(" {i:3}: {line}");
}
if recipe.lines().count() > 30 {
println!(" ... ({} more lines)", recipe.lines().count() - 30);
}
}
Err(e) => println!("recipe: ERR - {e:?}"),
}
}
Err(e) => println!("conversion: ERR - {e:?}"),
}
}
fn main() {
// 1. Simple meson package (like libepoxy, libevdev)
let p1 = r#"
pkgname=libevdev
pkgver=1.13.3
pkgrel=1
pkgdesc="Wrapper library for evdev kernel devices"
arch=('x86_64')
license=('MIT')
depends=()
makedepends=('meson' 'ninja' 'python' 'check')
source=("https://www.freedesktop.org/software/libevdev/libevdev-${pkgver}.tar.xz")
sha256sums=('aaaa')
build() {
meson setup build --prefix=/usr
meson compile -C build
}
package() {
meson install -C build --destdir="$pkgdir"
}
"#;
assess("libevdev (simple meson)", p1);
// 2. Cargo package
let p2 = r#"
pkgname=fd-find
pkgver=10.2.0
pkgrel=1
pkgdesc="Simple, fast and user-friendly alternative to find"
arch=('x86_64')
license=('MIT' 'Apache-2.0')
depends=('gcc-libs' 'xz')
makedepends=('rust' 'git')
source=("https://github.com/sharkdp/fd/archive/v${pkgver}.tar.gz")
sha256sums=('bbbb')
build() {
cargo build --release --locked
}
package() {
install -Dm755 target/release/fd "$pkgdir/usr/bin/fd"
}
"#;
assess("fd-find (cargo)", p2);
// 3. Configure-based (libpciaccess)
let p3 = r#"
pkgname=libpciaccess
pkgver=0.18.1
pkgrel=1
pkgdesc="X11 PCI access library"
arch=('x86_64')
license=('MIT')
depends=()
makedepends=('meson' 'ninja')
source=("https://gitlab.freedesktop.org/xorg/lib/libpciaccess/-/archive/libpciaccess-${pkgver}/libpciaccess-libpciaccess-${pkgver}.tar.gz")
sha256sums=('cccc')
build() {
meson setup build --prefix=/usr
meson compile -C build
}
package() {
meson install -C build --destdir="$pkgdir"
}
"#;
assess("libpciaccess (configure-ish meson)", p3);
// 4. CMake package
let p4 = r#"
pkgname=fmt
pkgver=11.0.2
pkgrel=1
pkgdesc="Modern formatting library"
arch=('x86_64')
license=('MIT')
makedepends=('cmake' 'ninja')
source=("https://github.com/fmtlib/fmt/releases/download/${pkgver}/fmt-${pkgver}.zip")
sha256sums=('dddd')
build() {
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
cmake --build build
}
package() {
DESTDIR="$pkgdir" cmake --install build
}
"#;
assess("fmt (cmake)", p4);
// 5. Git-based source
let p5 = r#"
pkgname=wlroots-git
_pkgname=wlroots
pkgver=0.18.0.r123.gabcdef
pkgrel=1
pkgdesc="Modular Wayland compositor library (git)"
arch=('x86_64')
license=('MIT')
depends=('wayland' 'libdrm' 'libinput' 'pixman')
makedepends=('meson' 'ninja' 'git')
provides=("wlroots=${pkgver%%.r*}")
conflicts=('wlroots')
source=("git+https://gitlab.freedesktop.org/wlroots/wlroots.git")
sha256sums=('SKIP')
pkgver() {
cd "$_pkgname"
git describe --long --tags | sed 's/\([^-]*-g\)/r\1/;s/-/./g'
}
build() {
meson setup build --prefix=/usr
meson compile -C build
}
package() {
meson install -C build --destdir="$pkgdir"
}
"#;
assess("wlroots-git (git source)", p5);
// 6. Real libpciaccess PKGBUILD-style (extra/) - the one user mentioned
let p6 = r#"
pkgname=libpciaccess
pkgver=0.19
pkgrel=1
pkgdesc="X11 PCI access library"
arch=(x86_64)
url="https://gitlab.freedesktop.org/xorg/lib/libpciaccess"
license=('MIT')
depends=()
makedepends=('meson' 'ninja')
source=("https://gitlab.freedesktop.org/xorg/lib/libpciaccess/-/archive/libpciaccess-${pkgver}/libpciaccess-libpciaccess-${pkgver}.tar.bz2")
sha256sums=('eeee')
build() {
meson setup build --prefix=/usr -Ddocs=false
ninja -C build
}
package() {
DESTDIR="$pkgdir" meson install -C build
}
"#;
assess("libpciaccess 0.19 (extra/, meson+ninja)", p6);
// 7. With options() and variables
let p7 = r#"
pkgname=ffmpeg
pkgver=7.0
pkgrel=1
pkgdesc="Complete solution to record/convert/stream audio and video"
arch=('x86_64')
license=('LGPL2.1' 'GPL2')
depends=('glibc' 'bzip2' 'zlib')
makedepends=('yasm' 'nasm' 'libass' 'openssl')
options=('!lto')
source=("https://ffmpeg.org/releases/ffmpeg-${pkgver}.tar.xz")
sha256sums=('ffff')
build() {
./configure \
--prefix=/usr \
--enable-shared \
--disable-static
make
}
package() {
make DESTDIR="$pkgdir" install
}
"#;
assess("ffmpeg (configure + options)", p7);
// 8. AUR-style git+url source
let p8 = r#"
pkgname=mesa
pkgver=24.3.0
pkgrel=1
pkgdesc="Open-source implementation of the OpenGL specification"
arch=('x86_64')
license=('MIT')
depends=('libdrm' 'wayland' 'libxshmfence')
makedepends=('meson' 'ninja' 'git' 'llvm' 'glslang' 'libxrandr' 'libxvmc' 'python-mako')
source=("git+https://gitlab.freedesktop.org/mesa/mesa.git#tag=mesa-${pkgver}"
"https://gitlab.freedesktop.org/mesa/mesa/-/raw/mesa-${pkgver}/scons/llvmpipe_generated.h.tar.xz")
sha256sums=('SKIP' 'aaaa')
pkgver() {
cd "$_pkgname"
git describe --tags | sed 's/mesa-//;s/-/./g'
}
build() {
meson setup build \
--prefix=/usr \
-Dplatforms=x11,wayland \
-Dgallium-drivers=iris,radeonsi \
-Dllvm=enabled
meson compile -C build
}
package() {
meson install -C build --destdir="$pkgdir"
}
"#;
assess("mesa 24.3 (git+url + multi-source + pkgver())", p8);
// 9. Real-world extra package: gzip
let p9 = r#"
pkgname=gzip
pkgver=1.13
pkgrel=2
pkgdesc="GNU compression utility"
arch=('x86_64')
url="https://www.gnu.org/software/gzip/"
license=('GPL3')
depends=('glibc' 'bash')
makedepends=('git')
source=("git+https://git.savannah.gnu.org/git/gzip.git#tag=v${pkgver}")
sha256sums=('SKIP')
pkgver() {
cd "$pkgname"
git describe --tags | sed 's/^v//;s/-.*//'
}
build() {
./configure --prefix=/usr
make
}
check() {
make check
}
package() {
make DESTDIR="$pkgdir" install
}
"#;
assess("gzip (configure + git source + check)", p9);
// 10. Extra package: zlib (very simple, C)
let p10 = r#"
pkgname=zlib
pkgver=1.3.1
pkgrel=1
pkgdesc="Compression library implementing the deflate compression method found in gzip and PKZIP"
arch=('x86_64')
license=('custom')
depends=('glibc')
source=("https://zlib.net/fossils/zlib-${pkgver}.tar.gz")
sha256sums=('9a93b2b7dfdac77ce2905a414a9eec29cb6e6b41b4d3eef3b0c0d51c4feaa9c3')
build() {
./configure --prefix=/usr
make
}
package() {
make install DESTDIR="$pkgdir"
}
"#;
assess("zlib (simple C, configure)", p10);
// 11. Package with pkgbase (split)
let p11 = r#"
pkgbase=openssl
pkgname=('openssl' 'openssl-doc')
pkgver=3.3.2
pkgrel=1
pkgdesc="The open source SSL/TLS toolkit (libraries + docs)"
arch=('x86_64')
license=('Apache-2.0')
depends=('glibc')
makedepends=('perl')
source=("https://www.openssl.org/source/openssl-${pkgver}.tar.gz")
sha256sums=('bbbb')
build() {
cd "$pkgbase"
./Configure --prefix=/usr
make
}
package_openssl() {
cd "$pkgbase"
make install DESTDIR="$pkgdir"
}
package_openssl-doc() {
pkgdesc="The open source SSL/TLS toolkit (documentation)"
cd "$pkgbase"
install -d "$pkgdir/usr/share/doc/"
cp -r doc/* "$pkgdir/usr/share/doc/"
}
"#;
assess("openssl (pkgbase split package)", p11);
// 12. Real-world: glib2 (a complex package with many deps)
let p12 = r#"
pkgname=glib2
pkgver=2.82.1
pkgrel=1
pkgdesc="Low level core library"
arch=('x86_64')
url="https://wiki.gnome.org/Projects/GLib"
license=('LGPL2.1')
depends=('glibc' 'libffi' 'pcre2' 'util-linux-libs' 'zlib')
makedepends=('gettext' 'meson' 'ninja' 'pkgconf' 'python-docutils' 'gi-docgen')
options=('!lto' '!emptydirs')
source=("https://download.gnome.org/sources/glib/${pkgver%.*}/glib-${pkgver}.tar.xz")
sha256sums=('cccc')
build() {
meson setup build --prefix=/usr --libdir=lib -Ddefault_library=shared
meson compile -C build
}
check() {
meson test -C build
}
package() {
meson install -C build --destdir="$pkgdir"
}
"#;
assess("glib2 (complex deps, real-world)", p12);
}