Initial scaffold
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
# Kama (窯)
|
||||
|
||||
**Kama** — the kiln — is the package manager for [Keru OS](https://github.com/AstralZX/KeruOS).
|
||||
|
||||
It's a single shell script (plus a small library) that builds packages from
|
||||
source by executing their recipes. It is deliberately minimal: there is no
|
||||
dependency resolver, no package database, and no binary format. Kama reads a
|
||||
`make.conf`, and it runs whatever each recipe tells it to.
|
||||
|
||||
## Why shell?
|
||||
|
||||
Kama is the whole point of Keru stated plainly: the package manager shouldn't
|
||||
be more complex than the packages it builds. It's not a program that manages
|
||||
packages as much as a *runner* for recipe scripts — the clever part lives in
|
||||
the recipes, not the mangement machinery.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
kama fetch <pkg> download + extract source into the cache
|
||||
kama build <pkg> stage-build one package
|
||||
kama install <pkg> build + install into $ROOT
|
||||
kama make <pkg> fetch deps, build, install, purge temp deps (AUR-style)
|
||||
kama purge remove temp build-time deps
|
||||
kama info <pkg> show recipe metadata
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Kama reads a `make.conf` (default `/etc/kama/make.conf`) for global build
|
||||
flags and profile choices. Key variables:
|
||||
|
||||
- `ROOT` — target rootfs the packages install into
|
||||
- `CACHEDIR` — persistent source + build cache
|
||||
- `CFLAGS` / `MAKEFLAGS` — passed to every build
|
||||
- `PKGDIR` — where the recipe repository lives (default: sibling `kama-packages`)
|
||||
|
||||
## Recipes
|
||||
|
||||
Packages are plain POSIX shell scripts. The syntax splits into two tiers:
|
||||
|
||||
- **Easy tier** — declare `name`, `version`, `url`, then a `build()` and
|
||||
`install()`. Kama handles fetch + extract automatically.
|
||||
- **Advanced tier** — override `pkg_fetch`, add `pkg_post`, `pkg_split`,
|
||||
`provides`, `conflicts`, `arch`, etc.
|
||||
|
||||
See [the recipe format](KeruOS/blob/main/docs/recipe-format.md) in the KeruOS
|
||||
repo for the full spec.
|
||||
|
||||
## Dependencies
|
||||
|
||||
Dependencies are handled AUR-style: build-time deps are fetched, compiled, and
|
||||
stripped away once the real package is built, leaving only what the system
|
||||
needs. This is the foundation of Keru's "nothing you didn't ask for" ethos.
|
||||
|
||||
## License
|
||||
|
||||
AGPL-3.0
|
||||
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env sh
|
||||
# kama — the Keru OS package manager
|
||||
#
|
||||
# Kama (窯, "kiln") builds packages from source by executing shell recipes.
|
||||
# It is deliberately minimal: it reads make.conf and runs whatever each
|
||||
# recipe tells it to. No dependency resolver, no package database.
|
||||
#
|
||||
# Usage:
|
||||
# kama <command> [package ...]
|
||||
#
|
||||
# Commands:
|
||||
# fetch <pkg> download + extract source into the cache
|
||||
# build <pkg> stage-build one package
|
||||
# install <pkg> build + install into $ROOT (staging-aware)
|
||||
# cmake <pkg> high-level: fetch deps, build, install, purge temp deps
|
||||
# purge remove temp build-time deps after a successful install
|
||||
# info <pkg> show a recipe's metadata
|
||||
|
||||
set -eu
|
||||
|
||||
SELF_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
. "$SELF_DIR/lib/kama.sh"
|
||||
|
||||
cmd="${1:-help}"
|
||||
shift || true
|
||||
|
||||
case "$cmd" in
|
||||
fetch) kama_fetch "$@" ;;
|
||||
build) kama_build "$@" ;;
|
||||
install) kama_install "$@" ;;
|
||||
make) kama_make "$@" ;;
|
||||
purge) kama_purge ;;
|
||||
info) kama_info "$@" ;;
|
||||
help|--help|-h)
|
||||
msg "Keru OS — Kama (窯) package manager"
|
||||
cycle
|
||||
;;
|
||||
*)
|
||||
die "unknown command: $cmd (try: kama help)"
|
||||
;;
|
||||
esac
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
# kama.sh — shared core library for the Kama package manager
|
||||
#
|
||||
# Sources make.conf, provides helpers, and implements the fetch/build/
|
||||
# install/temp-dep logic. All the messy bits live here so recipes stay clean.
|
||||
|
||||
PREFIX="\033[1;36m[ kama ]\033[0m"
|
||||
|
||||
# --- config ---------------------------------------------------------
|
||||
# Where the package recipe repository lives (kama-packages repo).
|
||||
PKGDIR="${PKGDIR:-$SELF_DIR/../kama-packages}"
|
||||
# merge a make.conf if one is provided
|
||||
MAKE_CONF="${MAKE_CONF:-/etc/kama/make.conf}"
|
||||
[ -f "$MAKE_CONF" ] && . "$MAKE_CONF"
|
||||
|
||||
ROOT="${ROOT:-/tmp/keru/root}"
|
||||
CACHEDIR="${CACHEDIR:-/var/cache/kama}"
|
||||
SRCDIR="$CACHEDIR/src"
|
||||
DLDIR="$CACHEDIR/dl"
|
||||
STAGEDIR="$CACHEDIR/stage"
|
||||
|
||||
# --- message helpers -------------------------------------------------
|
||||
msg() { printf '%b %s\n' "$PREFIX" "$*"; }
|
||||
warn() { printf '%b \033[1;33mwarning:\033[0m %s\n' "$PREFIX" "$*"; }
|
||||
die() { printf '%b \033[1;31merror:\033[0m %s\n' "$PREFIX" "$*" >&2; exit 1; }
|
||||
|
||||
# --- easy/advanced tier mapping --------------------------------------
|
||||
# If a recipe defines the easy-tier `build`/`install` sugar (and doesn't
|
||||
# define the advanced pkg_* forms), alias them. This keeps simple recipes
|
||||
# simple and gives advanced recipes full control.
|
||||
map_tiers() {
|
||||
# map build -> pkg_build unless pkg_build is overridden
|
||||
if declare -f build >/dev/null 2>&1 && ! declare -f pkg_build >/dev/null 2>&1; then
|
||||
pkg_build() { build; }
|
||||
fi
|
||||
if declare -f install >/dev/null 2>&1 && ! declare -f pkg_install >/dev/null 2>&1; then
|
||||
pkg_install() { install; }
|
||||
fi
|
||||
# advanced recipes may define pkg_build/pkg_install directly — leave as-is
|
||||
}
|
||||
|
||||
# --- default fetch: download + auto-extract -----------------------------
|
||||
default_fetch() {
|
||||
local tarball="$DLDIR/$(basename "$url")"
|
||||
mkdir -p "$DLDIR" "$SRCDIR"
|
||||
if [ ! -f "$tarball" ]; then
|
||||
msg "downloading $url"
|
||||
wget -q --show-progress "$SOURCE_MIRROR/$(basename "$url")" -O "$tarball" \
|
||||
|| wget -q --show-progress "$url" -O "$tarball" \
|
||||
|| die "download failed for $name"
|
||||
else
|
||||
msg "using cached source for $name"
|
||||
fi
|
||||
|
||||
local s="$SRCDIR/$name"
|
||||
rm -rf "$s"; mkdir -p "$s"
|
||||
msg "extracting $name"
|
||||
case "$tarball" in
|
||||
*.tar.gz|*.tgz) tar xf "$tarball" -C "$s" --strip-components=1 ;;
|
||||
*.tar.xz|*.txz) tar xJf "$tarball" -C "$s" --strip-components=1 ;;
|
||||
*.tar.bz2|*.tbz2) tar xjf "$tarball" -C "$s" --strip-components=1 ;;
|
||||
*.tar) tar xf "$tarball" -C "$s" --strip-components=1 ;;
|
||||
*.zip) unzip -q "$tarball" -d "$s.tmp" && \
|
||||
mv "$s.tmp"/*/* "$s/" ; rm -rf "$s.tmp" ;;
|
||||
*) die "unsupported archive type: $tarball" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# --- locate a recipe --------------------------------------------------
|
||||
find_recipe() {
|
||||
local pkg="$1"
|
||||
for rec in \
|
||||
"$PKGDIR/$pkg/PKGBUILD" \
|
||||
"$PKGDIR/$pkg.sh" \
|
||||
"$PKGDIR/$pkg"/*.sh ; do
|
||||
[ -f "$rec" ] && { printf '%s' "$rec"; return 0; }
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# --- fetch + extract source -------------------------------------------
|
||||
# --- fetch + extract source -------------------------------------------
|
||||
kama_fetch() {
|
||||
[ $# -ge 1 ] || die "fetch: missing package name"
|
||||
for pkg in "$@"; do
|
||||
local recipe; recipe="$(find_recipe "$pkg")" || die "no recipe for '$pkg'"
|
||||
msg "fetching source for $pkg"
|
||||
. "$recipe"
|
||||
map_tiers
|
||||
|
||||
local src="$SRCDIR/$name"
|
||||
if [ -d "$src" ] && [ -n "$(ls -A "$src" 2>/dev/null)" ]; then
|
||||
msg "using cached source for $pkg"
|
||||
continue
|
||||
fi
|
||||
|
||||
# advanced recipe overrides pkg_fetch -> use it; otherwise default_fetch
|
||||
if declare -f pkg_fetch >/dev/null 2>&1; then
|
||||
pkg_fetch
|
||||
else
|
||||
default_fetch
|
||||
fi
|
||||
[ -d "$src" ] && [ -n "$(ls -A "$src" 2>/dev/null)" ] \
|
||||
|| die "fetch produced no source for $pkg"
|
||||
done
|
||||
}
|
||||
|
||||
# --- stage-build a single recipe ---------------------------------------
|
||||
kama_build() {
|
||||
[ $# -ge 1 ] || die "build: missing package name"
|
||||
for pkg in "$@"; do
|
||||
local recipe; recipe="$(find_recipe "$pkg")" || die "no recipe for '$pkg'"
|
||||
msg "building $pkg"
|
||||
. "$recipe"
|
||||
map_tiers
|
||||
|
||||
local src="$SRCDIR/$name"
|
||||
local pkgdir="$STAGEDIR/$name"
|
||||
[ -d "$src" ] && [ -n "$(ls -A "$src" 2>/dev/null)" ] || kama_fetch "$pkg"
|
||||
|
||||
rm -rf "$pkgdir"; mkdir -p "$pkgdir"
|
||||
( cd "$src" && pkg_build ) || die "build failed for $pkg"
|
||||
msg "staging install for $pkg"
|
||||
( cd "$src" && pkg_install ) || die "install failed for $pkg"
|
||||
( cd "$src" && pkg_post ) || die "post-install failed for $pkg"
|
||||
pkg_split || true
|
||||
done
|
||||
}
|
||||
|
||||
# --- install staging dir into ROOT --------------------------------------
|
||||
install_staged() {
|
||||
local pkg="$1"
|
||||
local pkgdir="$STAGEDIR/$pkg"
|
||||
[ -d "$pkgdir" ] || die "nothing staged for '$pkg'"
|
||||
msg "installing $pkg into \$ROOT"
|
||||
( cd "$pkgdir" && cp -a . "$ROOT" )
|
||||
}
|
||||
|
||||
# --- high-level: make a package (deps + build + install + purge) --------
|
||||
kama_make() {
|
||||
[ $# -ge 1 ] || die "make: missing package name"
|
||||
for pkg in "$@"; do
|
||||
local recipe; recipe="$(find_recipe "$pkg")" || die "no recipe for '$pkg'"
|
||||
. "$recipe"
|
||||
|
||||
# temp deps: build, stage, install, then purge after
|
||||
if [ -n "${deps+x}" ] && [ ${#deps[@]} -gt 0 ]; then
|
||||
msg "building temp deps for $pkg: ${deps[*]}"
|
||||
kama_make "${deps[@]}"
|
||||
fi
|
||||
|
||||
kama_build "$pkg"
|
||||
install_staged "$pkg"
|
||||
|
||||
if [ "${PURGE_TEMP_DEPS:-1}" = "1" ] && [ -n "${deps+x}" ] && [ ${#deps[@]} -gt 0 ]; then
|
||||
msg "purging temp deps for $pkg: ${deps[*]}"
|
||||
kama_purge "${deps[@]}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# --- show recipe metadata ------------------------------------------------
|
||||
kama_info() {
|
||||
[ $# -ge 1 ] || die "info: missing package name"
|
||||
local recipe; recipe="$(find_recipe "$1")" || die "no recipe for '$1'"
|
||||
. "$recipe"
|
||||
printf 'name : %s\n' "$name"
|
||||
printf 'version : %s\n' "$version"
|
||||
printf 'url : %s\n' "$url"
|
||||
printf 'deps : %s\n' "${deps[*]:-}"
|
||||
printf 'runtime_deps: %s\n' "${runtime_deps[*]:-}"
|
||||
}
|
||||
|
||||
# --- purge temp deps from ROOT --------------------------------------------
|
||||
# NOTE: This is intentionally naive for the scaffold. A real implementation
|
||||
# tracks what each recipe installed and removes exactly that, plus clears the
|
||||
# staged dir. For now it clears the stage so a re-make re-pulls.
|
||||
kama_purge() {
|
||||
for pkg in "$@"; do
|
||||
msg "purging temp dep '$pkg' (stage)"
|
||||
rm -rf "$STAGEDIR/$pkg"
|
||||
done
|
||||
}
|
||||
|
||||
# --- init ----------------------------------------------------------------
|
||||
mkdir -p "$ROOT" "$CACHEDIR" "$SRCDIR" "$DLDIR" "$STAGEDIR"
|
||||
@@ -0,0 +1,7 @@
|
||||
# Tests will live here.
|
||||
# Planned:
|
||||
# - recipe.sh unit tests for the easy/advanced tier mapping
|
||||
# - fetch.sh fake download + extract
|
||||
# - deps.sh AUR-style temp dep build + purge
|
||||
# - make.conf.sh profile variable expansion
|
||||
# - integration.sh end-to-end: build a tiny package into a throwaway ROOT
|
||||
Reference in New Issue
Block a user