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