lix/lix.sh

364 lines
9.4 KiB
Bash
Executable File

#!/bin/sh -e
[ -n "$2" ] || { \
echo "
usage: $(basename $0) [--bootstrap=<path>] <command> <package> [<version>]
options:
--bootstrap=<path>
use <path> as the lowest layer in the overlay stack.
commands:
pl, pull
acquire and patch package source code.
cf, config, configure
configure the package.
mk, make
build the package.
in, inst, install
install the package to its overlay.
un, uninst, uninstall
remove the package overlay.
ad, add
pull, configure, make, and install the package to its overlay.
rm, remove
delete the package version's overlay, source code, and signature.
up [additional lmr flags]
symlink the package into the system root with lmr, passing along flags.
dn, down
remove the package symlinks from the system root with lmr.
do <cmd>
chroot into the package filesystem overlay and run <cmd>.
mt, mount <path>
mounts the package filesystem overlay at the given path.
um, umount <path>
unmounts the package filesystem overlay at the given path.
vr, ver, version
print the default version for the given package.
bl, built
list all built versions for the given package.
dp, deps, dependencies
list package dependencies and their inferred versions.
" && exit 0; }
# if this is running on an interactive terminal (as opposed to in a cron job,
# for example), if tput is installed, and if tput knows some color codes, then
# set the color values.
if [ -t 1 ] && [ "$(tput colors 2>/dev/null || echo 0)" -ge 8 ]; then
_clr="$(tput sgr0)"
_blu="$(tput setaf 6)"
_ylw="$(tput setaf 3)"
_red="$(tput setaf 1)"
fi
log() { echo "$_blu[LOG]$_clr $@"; }
wrn() { echo "$_ylw[WRN]$_clr $@" >&2; }
err() { echo "$_red[ERR]$_clr $@" >&2; exit 1; }
export LIXPREFIX="${LIXPREFIX:-${0%/bin/lix}}"
export LIXROOT="$(dirname "$(readlink -f "$0")")"
export HOWROOT="$(dirname "$(readlink -f "$(which how)")")"
export SRCREPO="${SRCREPO:-$LIXPREFIX/var/src}"
export SRCROOT="${SRCROOT:-${LIXROOT%/lix}/src}"
export cmd="$1"
export OP="$cmd"
export FAKEROOT=""
export CLEANUPFAKEROOT=""
export SUCCESS=""
# these are lowercase because they will be used in 'how' scripts.
export name
export version
[ -d "$SRCROOT" ] || err "$SRCROOT is not a directory!"
[ -d "$SRCREPO" ] || err "$SRCREPO is not a directory!"
[ -d "$LIXROOT" ] || err "$LIXROOT is not a directory!"
. "$LIXROOT/lib/pkg.sh"
. "$LIXROOT/lib/built.sh"
. "$LIXROOT/lib/dependencies.sh"
. "$LIXROOT/lib/lowerlayers.sh"
. "$LIXROOT/lib/guessver.sh"
# process command options
if [ "${cmd#--bootstrap}" != "$cmd" ]; then
if [ "$cmd" = "--bootstrap" ]; then
shift
export BSLAYER="$1"
else
export BSLAYER="$(echo "$cmd" | cut -d'=' -f2)"
fi
[ -d "$BSLAYER" ] || err "$BSLAYER is not a directory!"
shift
cmd="$1"
fi
case "$cmd" in
do)
docmd="$2"
shift
;;
mt|mnt|mount|um|umt|umnt|umount)
FAKEROOT="$2"
shift
;;
up)
while [ "${2#-}" != "$2" ]; do
upflags="$upflags $2"
shift
done
;;
sh|shell)
# maybe this should be its own utility.
[ "$1" ] || err "missing 'layer name' argument!"
[ -e "$2" ] || err "no such file as '$2'."
[ "$1" = "${1#lix/}" ] \
|| wrn "you are using the 'lix' namespace! this can be dangerous..."
layers="$(lowerlayers "$(dependencies "$(cat "$2")")" "$BSLAYER")"
lyr mk "$1"
chin "$(lyr up "$layers" "$1")" "${3:-sh}"
exit 0
;;
esac
# get required positional arguments
name="$2"
[ -n "$3" ] \
&& version="$3" \
|| version="$(guessver "$name")"
[ -n "$version" ] || err "could not determine version for '$name'."
cleanup() {
[ -z "$PKGHOW" ] || umount "$PKGHOW" || true
[ -z "$FAKEROOT" ] || unmountpkg "$FAKEROOT" || true
[ -z "$CLEANUPFAKEROOT" ] || rmdir "$FAKEROOT" || true
[ -n "$SUCCESS" ] || err "$name $version '$OP' failed"
}
trap cleanup EXIT INT HUP
beginmsg() {
OP="$1"
log "starting '$OP' for $name $version"
}
successmsg() { log "completed '$OP' for $name $version"; }
expectsrc() {
[ -d "$(lyr upperdir lix/$name/$version/src)" ] \
|| err "'src' layer missing! did you run 'lix pull $name $version' first?"
}
pullcmd() {
beginmsg "pull"
# if `how` has specific instructions for this package but there is no
# `src` package, then this must be a "bundle" package or something.
# just create an empty source directory and call it a day. (but please
# consider finding a more elegant way to handle bundles. there must
# be one, but it will probably require a rewrite. design problems
# usually arise out of fundamentally flawed models. with the right
# model, a _clean_ solution becomes trivial.)
if { how $name $version | grep -q ':' ; } \
&& [ ! -d "$SRCROOT/pkg/$name" ]
then
wrn "no 'src' entry. assuming this is a 'sourceless' package..."
mkdir -p "$SRCREPO/$name/$version" # ew...
elif [ -e "$SRCREPO/$name/$version" ]; then
wrn "$SRCREPO/$name/$version already exists. skipping remote pull."
else
src "$name" "$version";
fi
if [ -d "$(lyr upperdir lix/$name/$version/src)" ]; then
wrn "source code upper layer already exists! skipping patch.sh."
else
loadpkgdeps
log "looking for a patch.sh from 'how' to run in $name's overlay."
pkgdo ". ../how/env.sh; ../how/patch.sh"
fi
successmsg
}
confcmd() {
beginmsg "configure"
pkgdo ". ../how/env.sh; ../how/conf.sh"
successmsg
}
makecmd() {
beginmsg "make"
pkgdo ". ../how/env.sh; ../how/make.sh"
# compile "built-with" list
mkdir -p -- "$LIXROOT/built-with/${name}"
echo "$deps" > "$LIXROOT/built-with/${name}/$version"
successmsg
}
instcmd() {
beginmsg "install"
upperdir="$(lyr upperdir lix/$name/$version)"
# clean out previous installation and make backup
if [ -d "$upperdir/fs" ]; then
log "making backup of package's existing 'fs' layer..."
fsbak="$upperdir/bak/$(basename "$(mktemp -u)")"
mkdir -p "$fsbak"
cp -a "$upperdir/fs/." "$fsbak/"
fi
# install. restore backup on failure. delete backup on success.
if { pkgdo ". ../how/env.sh; ../how/inst.sh"; }; then
log "removing backup of package's previous 'fs' layer..."
rm -fr "$fsbak"
rmdir "$(dirname "$fsbak")" 2> /dev/null || true
else
log "restoring backup of package's previous 'fs' layer..."
rm -fr "$upperdir/fs"
mv "$fsbak" "$upperdir/fs"
rmdir "$(dirname "$fsbak")" 2> /dev/null || true
exit 1
fi
successmsg
}
# commands can be invoked with their full name, with a two-letter short form
# composed of the first letter and the last consonant (e.g., 'mk' for 'make'),
# and with an assortment of one-off abbreviations that just make sense for each
# command. e.g., 'rm' for 'remove'.
case "$cmd" in
pl|pull) pullcmd ;;
cf|cr|cg|conf|config|configure)
expectsrc
confcmd
;;
mk|make)
expectsrc
makecmd
;;
in|il|inst|install)
expectsrc
instcmd
;;
ad|add)
pullcmd
expectsrc
confcmd
makecmd
instcmd
;;
cl|cn|clean)
rm -fr "$(lyr upperdir lix/$name/$version)/src" 2> /dev/null || true
rm -fr "$(lyr upperdir lix/$name/$version)/build" 2> /dev/null || true
;;
rm|rv|remove) lyr rm "lix/$name/$version" ;;
pg|purge)
rm -fr $SRCREPO/$name/$version
lyr rm "lix/$name/$version"
;;
up) lmr -s $upflags "$(lyr upperdir lix/$name/$version/fs)" / ;;
dn|down) lmr -s -u "$(lyr upperdir lix/$name/$version/fs)" / ;;
do)
expectsrc
beginmsg "do"
pkgdo ". ../how/env.sh; $docmd"
successmsg
;;
mt|mnt|mount)
beginmsg "mount"
[ -d "$mntpth" ] || err "no such directory: $mntpth"
expectsrc
mountpkg "$mntpth" >/dev/null \
|| err "failed to mount $name $version overlay."
successmsg
;;
um|ut|umt|umnt|umount|unmount)
unmountpkg "$mntpth" \
&& log "unmounted $name $version overlay at $mntpth" \
|| err "error unmounting $name $version overlay at $mntpth"
;;
vr|vn|ver|version) echo "$version" ;;
bl|built) built "$name" ;;
dp|deps|dependencies)
loadpkgdeps
cols=0
cols="$(echo "$PKGDEPS" | while read line; do
newcols="$(echo "$line" | awk '{ print $1 }' | wc -c)"
[ "$newcols" -ge "$cols" ] || continue
cols="$newcols"
echo "$cols"
done | tail -n1)"
# get a list of dependencies with resolved version numbers. if no
# version number could be resolved, the "version" column will be empty
# and a warning will be printed.
echo "$PKGDEPS" | while read line; do
[ "$line" ] || continue;
dep="$(echo "$line" | awk '{ print $1 }')"
depver="$(echo "$line" | awk '{ print $2 }')"
[ "$depver" ] || wrn "could not resolve dependency '$dep'!"
# pad-space each dependency name so the versions align in a column.
printf "%-${cols}s %s\n" "$dep" "$depver"
done
;;
*) err "unrecognized command '$cmd'";;
esac
SUCCESS="yes"