KISS Package Manager
Some extra information which isn't available on https://kisslinux.github.io/wiki/package-manager.
[0.0] Index
[1.0] Interesting Features
[1.1] Runtime dependency detector built around 'ldd'
Dynamic dependencies brought in by build systems (which are missing from the package's dependency list) are fixed on-the-fly by checking which libraries link to the package's files.
This prevents an incomplete dependency list from causing system breakage as the package manager is able to complete the list.
A lot of packages make use of this "implicit" to "explicit" dependency list "conversion" to provide optional dependencies.
Example output:
-> libXmu Checking for missing dependencies
--- /home/dylan/conf/cache/kiss/build-4477/depends
+++ depends
@@ -1,3 +1,8 @@
+libX11
+libXau
libXext
libXt
+libxcb
xorg-util-macros make
[1.2] Fully dynamic (and automatic) alternatives system
Any file conflicts between two packages automatically become choices in the alternatives system.
This allows one to swap providers of files without needing to explicitly tell the package manager that two packages conflict, provide the same utilities, etc.
In other words, no changes need to be made to packages. In fact, nothing needs to be done at all. It's entirely automatic.
List available alternatives ('a' is an alias to 'alternatives'):
$ kiss a
gnugrep /usr/bin/grep
ncurses /usr/bin/clear
Swap to GNU grep:
$ kiss a gnugrep /usr/bin/grep
-> Swapping '/usr/bin/grep' from busybox to gnugrep
Swap back to busybox grep:
$ kiss a busybox /usr/bin/grep
-> Swapping '/usr/bin/grep' from gnugrep to busybox
Swap to all alternatives for a given package (sbase for example):
$ kiss a | grep ^sbase | kiss a -
-> Swapping '/usr/bin/cat' from busybox to sbase
-> Swapping '/usr/bin/cut' from busybox to sbase
-> Swapping '/usr/bin/yes' from busybox to sbase
...
The above command works as the output of the alternatives listing is directly usable as input to 'kiss a'.
[1.3] 3-way handshake for files in /etc/
Files in /etc/ are handled differently to those elsewhere on the system. A reinstallation or update to a package will not always overwrite these files.
Instead, a 3-way handshake happens during installation to determine how the new /etc/ file should be handled.
If the user has made modifications to the file and those modifications differ to the to-be-installed package's file, the file is installed with the suffix '.new'.
If the user hasn't touched the file, it will be automatically overwritten by the package manager as it will contain updated/new contents..
If the user has touched the file but the file has not changed between package versions, it will simply be skipped over.
[2.0] Repositories
Repository management in KISS is very simple. Repositories are configurable via an environment variable. This environment variable can be set system-wide, per-user, conditionally (via a script or program), for a single invocation, etc.
The environment variable is called KISS_PATH
and is functionally identical to
the $PATH
variable. A colon separated list of paths, in other words.
For example,
$ KISS_PATH=/var/db/kiss/repo/core:/var/db/kiss/repo/extra
In the above example, two repositories are enabled (Core and Extra). The package manager will search this list for packages in the order it is written.
Repositories can live anywhere on the system. In your $HOME
directory,
somewhere system-wide in '/', etc. The only requirement is that a full path be
used.
[2.1] What is a repository?
A KISS repository is simply a directory of directories. The quickest way to get started is as follows.
Create the repository:
$ mkdir -p repo
$ cd repo
Let's "fork" a few packages into our new repository.
$ kiss fork curl
$ kiss fork xz
$ kiss fork zlib
This is now a fully usable repository and it can be added to your KISS_PATH
.
[2.2] Enabling a remote repository
Let's assume that our KISS_PATH
matches the above example (Core and Extra).
As an example, we'll be enabling the Community repository. First, clone it:
# This can live anywhere on the system.
$ git clone https://codeberg.org/kiss-community/community
Now, enable the repository:
$ export KISS_PATH=$KISS_PATH:/path/to/community/community
[2.3] Preventing a package from receiving updates
Preventing a package from receiving updates can be accomplished in a myriad of different ways. The easiest method is to leverage a user repository.
Create a new repository:
$ mkdir -p no_updates
$ cd no_updates
Copy the package to the new repository:
$ cp -r /var/db/kiss/installed/PKG_NAME /path/to/no_updates
Add the new repository to the START of your KISS_PATH
:
$ export KISS_PATH=/path/to/no_updates:$KISS_PATH
The package manager will search KISS_PATH
in order. It will see that the
no_updates repository provides PKG_NAME and the version matches that which
is installed. No updates will again happen for the package.
[2.4] Package fallbacks
If you would like to package something in your own repository but would
like the package manager to prefer other repositories before yours, simply
add your repository to the end of KISS_PATH
.
The moment that your package is available elsewhere, the package manager will prefer the new location to yours. The list is searched (in order) and the first match is picked.
[2.5] Bypassing KISS_PATH
There is a special case where one can bypass the regular KISS_PATH for a single invocation of the package manager. This has been called "CRUX-like usage" by users.
$ cd /path/to/myrepo/firefox
$ kiss b
$ kiss i
As seen above, various package manager commands will work without arguments, so long as you are in a package's repository directory. This will prepend the current directory to '$KISS_PATH' only for this invocation.
[3.0] Hook examples
[3.1] Logging build duration
This hook adds a new message post-build with the total build duration in a human readable format (00h 00m). Similar code is used in the boot process of the system to calculate boot time.
PKG="$2"
case $1 in
pre-build)
cat /proc/uptime > "${KISS_TMPDIR:-/tmp}/_kiss_start"
;;
post-build)
IFS=. read -r _start _ < "${KISS_TMPDIR:-/tmp}/_kiss_start"
IFS=. read -r _end _ < /proc/uptime
rm -f "${KISS_TMPDIR:-/tmp}/_kiss_start"
(
_s=$((_end - _start))
_h=$((_s / 60 / 60 % 24))
_m=$((_s / 60 % 60))
[ "$_h" = 0 ] || _u="${_u}${_h}h "
[ "$_m" = 0 ] || _u="${_u}${_m}m "
printf "-> %s %s\n" "$PKG" "Build finished in ${_u:-${_s}s}"
)
;;
esac
[3.2] Setting the terminal title
This hook uses the Xterm escape sequence to change the terminal title, which is supported by most modern terminal emulators. The title will include the name of the package currently being build, the position in the batch and the total amount of packages to be built. That way it's easy to keep track of build progress, without having to scroll past a wall of compiler output.
case $1 in
queue-status|queue)
[ -t 2 ] && {
printf '\033]0;kiss: %s (%d/%d)\a' \
"$2" "${3:-?}" "${4:-?}" >&2
}
;;
esac
[3.3] Drop into shell on build fail
case $1 in
build-fail)
printf "-> %s %s\a\n" "$2" "Dropped into shell"
sh >/dev/tty
;;
esac
[4.0] Tips and Tricks
A lot of the package manager's features are hard to discover or otherwise non-obvious to its users. This section will document these features, how to use them and the benefits they bring.
[4.1] Swap grep implementations for a major speed up
The default grep implementation in KISS is busybox grep. This version of grep works very well and supports a large number of features. The one issue is that it is painfully slow when compared to other popular implementations.
A fairly major speedup can be attained by swapping to a different grep via the alternatives system. The fastest grep implementation around is GNU grep which is available in Community as 'gnugrep'.
$ kiss b gnugrep && kiss i gnugrep
$ kiss a gnugrep /usr/bin/grep
[4.2] Shallow cloning
By default, the package manager performs a shallow clone of the remote
repository, branch, or commit whenever possible. This is to speedup cloning,
and for most packages does not alter the package building process. This
shallow cloning is done in a way which still allows git describe
.
For packages which require submodules, add git submodule update --init
to
pull in the required submodules.