Files
kama/lib/kama.sh
T
2026-09-01 08:00:05 +02:00

186 lines
6.8 KiB
Bash

# 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"