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 ` - obtaining a shell into the package's chroot using `lix do sh ` - examining the package's layers at `lyr upperdir lix//` ## 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 `-` 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///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///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///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///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 [] 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 chroot into the package filesystem overlay and run . mt, mount mounts the package filesystem overlay at the given path. um, umount 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.