initial commit.
This commit is contained in:
commit
81b330c30e
|
@ -0,0 +1,4 @@
|
|||
lower
|
||||
system
|
||||
built-with
|
||||
build-conf
|
|
@ -0,0 +1,20 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright 2020 "yafox"
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,206 @@
|
|||
lix
|
||||
===
|
||||
|
||||
lix is a source-based package manager written entirely in POSIX-compliant shell
|
||||
script. it was developed to serve as the core component of lix os.
|
||||
|
||||
it uses chroots and package-and-version-specific overlayfs layers to avoid
|
||||
package conflicts during the build process. aside from anything in the "system"
|
||||
bootstrap directory, the only tools and libraries available to a package are
|
||||
those it explicitly lists as a dependency. all package files are kept in
|
||||
separate directories and soft-linked in to the system root. this makes it easy
|
||||
to see what packages have supplied each file in one's system root.
|
||||
|
||||
lix is composed of the following small shell script utilities, all of which can
|
||||
be used independently of lix and each other (with the exception of `how`, which
|
||||
uses `vercmp` to allow defining instructions for ranges of package versions and
|
||||
`shsort` to quicksort them):
|
||||
|
||||
1) `lmr` can merge one directory into another using soft or hard links.
|
||||
2) `lyr` provides directories and commands for managing overlayfs overlays.
|
||||
3) `src` pulls and verifies source code and provides default version numbers.
|
||||
4) `how` provides scripts for patching, configuring, building, and installing.
|
||||
5) `chin` sets up common system paths and chroots in to a target directory.
|
||||
6) `vercmp` allows comparison of version strings in package-specific formats.
|
||||
7) `shsort` is a POSIX shell quicksort implementation which takes a user
|
||||
supplied comparison command.
|
||||
|
||||
because of its small size and modularity, lix and its utilities can provide a
|
||||
foundation for building a simple source-based distribution of one's own. the
|
||||
lix os project serves as an example of how one might do this.
|
||||
|
||||
## manual intervention
|
||||
|
||||
lix favors an interactive approach to problem solving. patching, configuring,
|
||||
building, and installing are all separate commands. the results of commands
|
||||
executed on a package up to any given point may be examined via:
|
||||
|
||||
- mounting the package's chroot contents using `lix mount <path> <package>`
|
||||
- obtaining a shell into the package's chroot using `lix do sh <package>`
|
||||
- examining the package's layers at `lyr upperdir lix/<package>/<version>`
|
||||
|
||||
## dependency handling
|
||||
|
||||
lix does not solve dependency graphs. dependency graph resolution is a
|
||||
non-trivial problem, and chrooted builds make solving it largely unnecessary
|
||||
anyway. each package can have whatever dependencies it needs without causing
|
||||
conflicts with other packages. if conflicts between packages arise, they can be
|
||||
quickly resolved by swapping around softlinks using `lix up` and `lix down`.
|
||||
|
||||
`lix` records the versions of each dependency used when building a package in
|
||||
the `built-with` directory in LIXROOT after each successful build. this makes
|
||||
it possible to determine which packages need to be rebuilt when changes are made
|
||||
to a dependency.
|
||||
|
||||
lix also does not recursively build or install dependencies. only code the user
|
||||
has explicitly asked for should ever run on a user's machine. however, assuming
|
||||
package dependency lists are exhaustive and kept sorted by their height in the
|
||||
(acyclic) dependency graph in a similar way to the linux kernel's module
|
||||
dependencies list, implementing this behavior should require little more than a
|
||||
loop and some list deduplication.
|
||||
|
||||
## dynamic dependencies
|
||||
|
||||
any line in a package's list of dependencies that matches the regex
|
||||
"^\$[A-Za-z0-9]+$" will get evaluated before being parsed. this allows some
|
||||
dependencies to be provided via environment variables. this functionality is
|
||||
provided in order to support the user's ability to choose a text editor during
|
||||
the configuration of certain packages. by convention, lix packages use the
|
||||
"EDITORDEP" variable to reference the package providing the command named in the
|
||||
"EDITOR" variable. because of its general nature, this technique can also be
|
||||
extended to provide build flexibility as needed.
|
||||
|
||||
for example, the line:
|
||||
|
||||
$EDITORDEP
|
||||
|
||||
in combination with the command:
|
||||
|
||||
export EDITORDEP="elvis > 0"
|
||||
|
||||
results in the line:
|
||||
|
||||
elvis > 0
|
||||
|
||||
which selects the most recent version of the elvis package, assuming its version
|
||||
number is greater than zero.
|
||||
|
||||
## --bootstrap
|
||||
|
||||
every package chroot needs, at minimum, a userland of some kind. (e.g., a shell
|
||||
and utilities like `ls` and `cat`.) this userland should be included in the
|
||||
dependencies of the `how` packages one has chosen. however, when bootstrapping
|
||||
a lix system, no dependencies have been compiled yet and compilation must depend
|
||||
on the host system's userland. the `--bootstrap` option allows one to specify a
|
||||
directory to use as the lowest layer in lix's overlayfs chroot. this directory
|
||||
must be constructed; `--bootstrap=/` will NOT work because overlayfs does not
|
||||
allow mounting overlays inside of lower directories. `lmr` may come in handy
|
||||
here. `lix-os-utilities` also provides a script for constructing such a
|
||||
directory, `mkbootstrap.sh`.
|
||||
|
||||
## the `build-conf` directory
|
||||
|
||||
when a package's build chroot is being constructed, versions for the target
|
||||
package and each of its dependencies must be chosen somehow. if a version for
|
||||
the target package is not passed on the command line, the `build-conf` directory
|
||||
is examined for a file with the same name as the target package. this file
|
||||
should contain a whitespace delimited list of package names and version numbers,
|
||||
with the first line containing the name of the target package and the version to
|
||||
build by default when no version is specified on the command line.
|
||||
|
||||
if there is no such file, the versions are taken from the `defaults.sh` file in
|
||||
the `src` package roots for the target package and its dependencies.
|
||||
|
||||
each version specified is validated against the dependency constraints supplied
|
||||
by `how`'s `deps` command.
|
||||
|
||||
if no version can be determined for a dependency, or if the dependency's layer
|
||||
does not exist yet, a warning is emitted but the build attempts to continue.
|
||||
this is because there is no guarantee that the dependency has not simply been
|
||||
included via the `system` directory. during a bootstrap build, this will be the
|
||||
case more often than not. in this situation, the dependency's name is still
|
||||
written to a `built-with` file, but no version number is included.
|
||||
|
||||
## the `built-with` directory
|
||||
|
||||
every time a package is built successfully, a tab delimited list of the
|
||||
dependencies included in its build chroot and their versions is written to the
|
||||
`built-with` directory using the pattern `<package>-<version>` for the filename.
|
||||
|
||||
dependencies whose versions could not be determined (likely because they were
|
||||
included via the `system` directory) or whose layers don't exist yet are listed
|
||||
without a version number.
|
||||
|
||||
## layers
|
||||
|
||||
in the process of building and installing a package, the following `lyr` layers
|
||||
are created:
|
||||
|
||||
1. lix/<package>/<version>/src
|
||||
used as the top layer for /mnt/src in the package chroot. overlaid on top
|
||||
of the package's source code. holds all changes that would have been made
|
||||
to the package's source code directory during the patching, configuring, and
|
||||
building process.
|
||||
|
||||
2. lix/<package>/<version>/build
|
||||
bind mounted to /mnt/build in the package chroot. not a true layer as there
|
||||
is nothing to overlay. gives packages a place to build out-of-source without
|
||||
having to mess up the package's root file system.
|
||||
|
||||
3. lix/<package>/<version>/how
|
||||
provided as the upper layer for the package's `how` instructions. this layer
|
||||
gets read-only bind mounted to /mnt/how in the package chroot. mostly exists
|
||||
to give `how` a place to mount its layers. (see lib/ch.sh for details.)
|
||||
|
||||
4. lix/<package>/<version>/fs
|
||||
contains the root filesystem for the package. this is the "upper directory"
|
||||
in the overlayfs for the package's chroot. gets used as a "lower directory"
|
||||
in the overlayfs mounts of packages that depend on it. gets `lmr`ed in to the
|
||||
system root directory by `lix up`.
|
||||
|
||||
## usage
|
||||
|
||||
lix <command> <package> [<version>]
|
||||
|
||||
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
|
||||
symlink the package overlay into the system root.
|
||||
|
||||
dn, down
|
||||
remove the package symlinks from the system root.
|
||||
|
||||
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 version inferred for the given package.
|
||||
|
||||
dp, deps, dependencies
|
||||
list package dependencies and their inferred versions.
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
. "$LIXROOT/lib/versionformat.sh"
|
||||
|
||||
built() {
|
||||
ls "$(lyr upperdir lix/$1)" | while read ver; do
|
||||
[ "ls $(lyr upperdir lix/$1/$ver/fs/)" = "" ] || echo "$ver"
|
||||
done | shsort -r "vercmp -f $(versionformat "$1")" 2> /dev/null
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
#!/bin/sh -e
|
||||
|
||||
clearopaques() {
|
||||
# opaque directories are created when a layer creates a directory that does
|
||||
# not exist in one of its currently mounted lower layers. preserving the
|
||||
# layer's illusion of being the progenitor of a directory when mounted with
|
||||
# different lower layers is incompatible with lix's operation, and blocking
|
||||
# another layer's files in general is unsupported behavior as far as lix is
|
||||
# concerned. this strips all directories of the "opaque" attribute.
|
||||
|
||||
find "$1" -type d -exec setfattr -x trusted.overlay.opaque {} \; 2>/dev/null \
|
||||
|| true # find returns nonzero if no 'opaque' files were found.
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
. "$LIXROOT/lib/built.sh"
|
||||
. "$LIXROOT/lib/reverse.sh"
|
||||
. "$LIXROOT/lib/versionpasses.sh"
|
||||
|
||||
dependencies() { # <contents of a 'deps' file>
|
||||
seen=""
|
||||
deps="$(echo "$1" | while read line; do
|
||||
eval "printf '%s\n' "\"$line\"""
|
||||
done)"
|
||||
|
||||
echo "$deps" | while read line; do
|
||||
[ "$line" ] || continue;
|
||||
|
||||
# eval inline expressions and convert all whitespace to single spaces.
|
||||
line="$(echo "$line" | tr -s '[:space:]' ' ')"
|
||||
dep="$(echo "$line" | cut -d' ' -f1)"
|
||||
depreq="$(echo "$line" | cut -d' ' -f2-)"
|
||||
depver="$(built "$dep" | reverse | while read ver; do
|
||||
[ "$ver" ] || continue;
|
||||
if [ "$(versionpasses "$dep" "$ver" "$depreq")" = "yes" ]; then
|
||||
echo "$ver"
|
||||
break
|
||||
fi
|
||||
done)"
|
||||
|
||||
resolved="$(printf '%s\t%s\n' "$dep" "$depver")"
|
||||
echo "$seen" | grep -q "^$resolved\$" || echo "$resolved"
|
||||
|
||||
seen="$resolved\n$seen"
|
||||
done
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#!/bin/sh
|
||||
|
||||
. "$LIXROOT/lib/versionformat.sh"
|
||||
|
||||
guessver() {
|
||||
pkg="$1"
|
||||
tgt="${2:-$pkg}"
|
||||
|
||||
if [ -f "$LIXROOT/build-conf/$pkg" ]; then
|
||||
awk '$1 == "'$tgt'" { print $2 }' "$LIXROOT/build-conf/$tgt"
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf '%s\n%s\n%s' \
|
||||
"$(version=''; eval "$(src -d $tgt 2> /dev/null)"; echo "$version")" \
|
||||
"$(ls "$HOWROOT/pkg/$tgt" 2>/dev/null)" \
|
||||
"$(built "$tgt" 2> /dev/null)" \
|
||||
| grep -vE '^default$|^$' \
|
||||
| shsort -r "vercmp -f $(versionformat "$tgt")" 2> /dev/null \
|
||||
| tail -n1
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
#. "$LIXROOT/libs/reverse.sh"
|
||||
|
||||
# errors out if a dependency was not resolved or if the LAYERMAX is exceeded.
|
||||
lowerlayers() { # <resolved dependencies> [<bootstrap directory>]
|
||||
layers="$2"
|
||||
layercount="0"
|
||||
maxlayers=${LAYERMAX:-100}
|
||||
|
||||
layers="$(echo "$1" | reverse | while read line; do
|
||||
[ "$line" ] || continue;
|
||||
|
||||
dep="$(echo "$line" | awk '{ print $1 }')"
|
||||
ver="$(echo "$line" | awk '{ print $2 }')"
|
||||
dir="$(lyr upperdir lix/$dep/$ver/fs)"
|
||||
|
||||
# only warn on missing dependencies if a bootstrap directory was
|
||||
# provided. it's possible the dependency is in the bootstrap.
|
||||
[ "$2" ] && logfn="wrn possible" || logfn="err"
|
||||
|
||||
[ "$ver" ] && [ -d "$dir" ] \
|
||||
&& printf "$dir:" \
|
||||
|| $logfn "missing dependency: $dep $ver"
|
||||
|
||||
layercount="$(expr $layercount + 1)"
|
||||
[ "$layercount" -le "$maxlayers" ] \
|
||||
|| err "overlayfs can't handle more than $maxlayers lower directories!"
|
||||
|
||||
done)$layers"
|
||||
|
||||
[ "$?" -eq 0 ] || exit $? # error codes weren't bubbling up... :/
|
||||
|
||||
# strip possible trailing colon and echo.
|
||||
echo "${layers%:}"
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
. "$LIXROOT/lib/reverse.sh"
|
||||
|
||||
registermount() {
|
||||
MOUNTS="$1\n$MOUNTS"
|
||||
}
|
||||
|
||||
unmountall() {
|
||||
echo "$MOUNTS" | while read path; do
|
||||
umount "$path"
|
||||
done
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
. "$LIXROOT/lib/built.sh"
|
||||
. "$LIXROOT/lib/dependencies.sh"
|
||||
. "$LIXROOT/lib/clearopaques.sh"
|
||||
. "$LIXROOT/lib/lowerlayers.sh"
|
||||
. "$LIXROOT/lib/versionformat.sh"
|
||||
|
||||
export PKGHOW=""
|
||||
export PKGDEPS=""
|
||||
|
||||
# sets up the package chroot, runs the given command in it, then tears it down.
|
||||
pkgdo() {
|
||||
if [ -z "$FAKEROOT" ]; then
|
||||
FAKEROOT="$(mktemp -d)"
|
||||
CLEANUPFAKEROOT="yes"
|
||||
|
||||
log "loading $name version $version overlay"
|
||||
|
||||
mountpkg "$FAKEROOT" "$2" \
|
||||
|| err "failed to mount $name $version overlay."
|
||||
fi
|
||||
|
||||
chin "$FAKEROOT" "cd /mnt/src; $1"
|
||||
}
|
||||
|
||||
mountpkghow() {
|
||||
# mount the 'how' overlay for the given package and version if not mounted.
|
||||
PKGHOW="$(lyr mountdir "lix/$name/$version/how")"
|
||||
if ! (mountpoint -q "$PKGHOW"); then
|
||||
lyr mk "lix/$name/$version/how"
|
||||
lyr up "$(how "$name" "$version")" "lix/$name/$version/how" > /dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
loadpkgdeps() {
|
||||
[ "$PKGHOW" ] || mountpkghow
|
||||
|
||||
if [ -z "$PKGDEPS" ]; then
|
||||
[ -f "$PKGHOW/deps" ] \
|
||||
|| err "could not find a 'deps' file in the package's 'how' overlay!"
|
||||
|
||||
PKGDEPS="$(dependencies "$(cat "$PKGHOW/deps")")"
|
||||
fi
|
||||
}
|
||||
|
||||
# sets up the package chroot overlay. optionally takes a path at which the
|
||||
# overlay should be created.
|
||||
mountpkg() {
|
||||
loadpkgdeps
|
||||
|
||||
tgt="$1"
|
||||
pkglower="$(lowerlayers "$PKGDEPS" "$BSLAYER")"
|
||||
|
||||
# make and mount package root filesystem overlay
|
||||
lyr mk "lix/$name/$version/fs"
|
||||
|
||||
fs="$(lyr up "$pkglower" "lix/$name/$version/fs")"
|
||||
|
||||
[ -n "$fs" ] || err "could not bring up chroot layer for $name $version."
|
||||
|
||||
# bind mount overlay and override $fs value for remainder of function if a
|
||||
# target path was specified.
|
||||
mount -o bind "$fs" "$tgt"
|
||||
|
||||
# bind the how overlay to /mnt/how in the chroot.
|
||||
mkdir -p "$tgt/mnt/how"
|
||||
mount -o bind "$PKGHOW" "$tgt/mnt/how"
|
||||
|
||||
# mount source code overlay and bind to /mnt/src in the chroot.
|
||||
lyr mk "lix/$name/$version/src"
|
||||
srcmnt="$(lyr up "$SRCREPO/$name/$version" "lix/$name/$version/src")"
|
||||
|
||||
mkdir -p "$tgt/mnt/src"
|
||||
mount -o bind "$srcmnt" "$tgt/mnt/src"
|
||||
|
||||
# create build layer and bind to /mnt/build in the chroot.
|
||||
# this layer exists to support packages that need an out-of-source build.
|
||||
# doesn't really *need* to be a layer, but it's a handy place to stash any
|
||||
# writes we make to it.
|
||||
lyr mk "lix/$name/$version/build"
|
||||
mkdir -p "$tgt/mnt/build"
|
||||
mount -o bind "$(lyr upperdir "lix/$name/$version/build")" "$tgt/mnt/build"
|
||||
}
|
||||
|
||||
unmountpkg() {
|
||||
_dn() { mountpoint -q "$1" && umount "$1"; };
|
||||
_dnandrm() { _dn "$1" && rmdir "$1" 2> /dev/null; }
|
||||
_dnandrm "$1/mnt/build" || true
|
||||
_dnandrm "$1/mnt/src" || true
|
||||
_dnandrm "$1/mnt/how" || true
|
||||
_dn "$1" || true
|
||||
|
||||
lyr dn "lix/$name/$version/src" || true
|
||||
lyr dn "lix/$name/$version/fs" || true
|
||||
_dn "$PKGHOW"
|
||||
|
||||
rmdir "$(lyr upperdir lix/$name/$version)/fs/mnt" || true
|
||||
clearopaques "$(lyr upperdir lix/$name/$version)/fs" || true
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
alias reverse="sed '1!x;H;1h;\$!d;g'"
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
versionformat() {
|
||||
(vercmp formats | grep -q "^$1\$") \
|
||||
&& echo "$1" \
|
||||
|| echo "default"
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/sh
|
||||
|
||||
. "$LIXROOT/lib/versionformat.sh"
|
||||
|
||||
versionpasses() { # <package> <version> <constraints expression>
|
||||
|
||||
index() { echo "$1" | tr -s '[:space:]' ' ' | cut -d' ' -f$2; }
|
||||
|
||||
format="$(versionformat "$1")"
|
||||
version="$2"
|
||||
constraints="$3"
|
||||
value="yes"
|
||||
|
||||
while [ "$constraints" != "" ]; do
|
||||
|
||||
# handle 'or' operator
|
||||
if [ "$(index "$constraints" 1)" = "||" ]; then
|
||||
constraints="$(index "$constraints" 2-)"
|
||||
|
||||
[ "$value" = "yes" ] \
|
||||
&& break \
|
||||
|| { value="yes" && continue; }
|
||||
fi
|
||||
|
||||
# handle implicit 'and' operator
|
||||
if [ "$value" = "yes" ]; then
|
||||
value="$(vercmp -f "$format" "$version $(index "$constraints" -2)")"
|
||||
fi
|
||||
|
||||
constraints="$(index "$constraints" 3-)"
|
||||
done
|
||||
|
||||
echo "$value"
|
||||
}
|
|
@ -0,0 +1,363 @@
|
|||
#!/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"
|
|
@ -0,0 +1,26 @@
|
|||
SRCDIR := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
PREFIX ?= /usr
|
||||
DESTDIR ?= $(PREFIX)/bin
|
||||
LIXROOT ?= $(PREFIX)/share/lix
|
||||
|
||||
.PHONY: default install uninstall
|
||||
|
||||
default: $(SRCDIR)built-with $(SRCDIR)build-conf
|
||||
|
||||
$(SRCDIR)built-with:
|
||||
mkdir -p $(SRCDIR)built-with
|
||||
|
||||
$(SRCDIR)build-conf:
|
||||
mkdir -p $(SRCDIR)build-conf
|
||||
|
||||
$(LIXROOT):
|
||||
ln -sf $(SRCDIR) $(LIXROOT)
|
||||
|
||||
$(DESTDIR)/lix:
|
||||
ln -sf $(LIXROOT)/lix.sh $(DESTDIR)/lix
|
||||
|
||||
install: $(LIXROOT) $(DESTDIR)/lix
|
||||
|
||||
uninstall:
|
||||
rm $(DESTDIR)/lix
|
||||
rm $(LIXROOT)
|
|
@ -0,0 +1,12 @@
|
|||
#!/bin/sh
|
||||
|
||||
# find all *.sh files not under `pkg` that are not symbolic links, strip all
|
||||
# trailing whitespace, then all leading whitespace, then all lines starting
|
||||
# with '#', then all empty lines. then count the remaining lines.
|
||||
|
||||
find . -name "*.sh" ! -type l \
|
||||
| xargs sed 's/[[:space:]]*$//g; s/^[[:space:]]*//g; s/^#.*$//g; /^$/d' \
|
||||
| wc -l - \
|
||||
| cut -d' ' -f1
|
||||
|
||||
# note that this script's ELOC is also included in the count.
|
Loading…
Reference in New Issue