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