With simplicity as a primary goal, sabotage adopted from the start a new old
directory layout - the same one as used on early UNIX.
That corresponds to what you think of as a "prefix"
defined as ""
or
alternatively "/"
. Meaning that there's a /bin
, but not /usr/bin
.
Same for /include
, /lib
, and so on.
Not only is it shorter to type, it's also easier to navigate a filesystem
that only has a single bin directory, instead of 4 or more.
I refer to /usr/bin
, /usr/sbin
, /bin/
and /sbin
that exist on popular
linux distributions and even on BSDs.
The practice of having a separate /usr
stems from historic limitations on
multi-user systems, whereas the typical linux user today has a dedicated
home computer at his disposal where he is the only user.
Yet most or all popular modern linux distros use /usr
as the prefix for their
packages.
The other common choice for the prefix option in software build systems
is /usr/local
, set as default in GNU autoconf.
Its purpose is to allow the user to build custom packages from source
without having them interfere with other libraries installed via
the official package manager, typically as binary builds.
Since I anticipated from the start of my work on sabotage that some users
might want or need a different "prefix"
, all packages were written using
an overridable butch_prefix
variable. Nobody ever used it though, so there
were a couple of minor bugs that I fixed recently when I first needed it.
In order to properly integrate our packages into a foreign distro environment, this is the key ingredient.
My task was to set up a lightweight desktop environment on a raspberry pi using the Raspberry Pi OS (installed from "lite" version). There's only very limited amount of space on the SD card it uses, so I opted for using my favorite window manager openbox, combined with lxpanel. That's enough to get 90% of a desktop environment's functionality at a minimal resource cost.
At first I installed openbox from the distro repos, which had far too many,
but still an acceptable amount of dependencies.
Then i typed apt install lxpanel
and was confronted with 50MB of dependencies,
including gtk3 AND gtk4 in addition to the already installed gtk2, but more
importantly, it would pull in and install pulseaudio!
In case you missed it, pulseaudio is one of the first successes in Lennart Poettering's quest to ruin the Linux Desktop Experience. A Red Hat employee at the time, now on Microsoft's payroll, he developed a new sound system for Linux that works by injecting a pre-loaded DLL into processes and overriding library functions to use his pulesaudio daemon. What could possibly go wrong? The trojan horse was eagerly adopted by fedora in its infancy and then widely propagated to other distributions.
At that time, I happened to upgrade my fedora install to the version that first shipped it (fedora 9), and it took me almost a week to get sound working again!
I spare you the details of the hoops i had to jump, though the solution was to get rid of pulseaudio entirely.
Whenever you have issues with audio on Linux, look no further than pulseaudio. It came right in time as the Linux desktop gained considerable traction.
Other well known Poettering projects are dbus and systemd; rammed down our throats in a similar manner. Whenever Poettering announces a new project, you can be sure it will not take long to find its way into your distro. Unless you use sabotage linux.
The fundamental problem with binary distros like debian is that they need to provide a single source of truth for every library and application. If there's an editor with optional support to detect a hot-plugged printer, then that optional support will be built in, including all 20 dependencies needed for this feature. The feature wouldn't exist if there was nobody in the whole world wanting to use it.
The result of that "all-options-enabled" policy is that these distros are horribly bloated and will fill up your hard disk at break-neck speed.
"Not a problem, just buy more RAM and a bigger SSD!", you may say, to which I reply "Thanks for the offer. Here's my bank account number."
Build-from-source distros like Gentoo let you toggle optional dependencies on and off to your liking. Such a mechanism also exists for a select few packages in sabotage, even though we generally go for the "min deps, max usefulness" aka "best bang for the buck" configuration.
Anyway, it was clear to me that I will NOT install pulseaudio and let it fuck up the audio subsystem! And I certainly don't need 3 GTK versions installed just to orchestrate a couple of xterm windows.
What are the options to rectify the situation and get lxpanel without those dependencies?
Here's what I could think of:
It should come as no surprise that I went for the third option.
The first thing we need is obviously the sabotage package recipe repository
and the build manager butch
.
Since butch is written in portable shell and lives in the sabotage repo,
both are only a single git clone away.
git clone https://github.com/sabotage-linux/sabotage
After I cloned the repo, and since we will need root powers anyway,
I moved it to /src - basically mv sabotage /src
.
butch lives in KEEP/bin and can be readily executed from there. The only thing it needs is a config file. The repo has 2 templates one could use, KEEP/config.stage1 and KEEP/config.cross. I chose the former because half of the latter is boilerplate for cross-compiler setup. Since we compile natively on and for debian, we can use the existing system toolchain.
cp KEEP/config.stage1 config
after a few edited lines, my config looks like this:
export A=aarch64
export K=/src/KEEP
export S=/src
export R=/
export C=/src/tarballs
export LOGPATH=/src/logs
export MAKE_THREADS=4
[ -z "$CC" ] && export CC="gcc -I/usr/local"
export HOSTCC="$CC"
[ -z "$CXX" ] && export CXX="g++ -I/usr/local"
export HOSTCXX="$CXX"
export BUTCH_BUILD_TEMPLATE="$K"/butch_build_template.txt
export BUTCH_DOWNLOAD_TEMPLATE="$K"/butch_download_template.txt
export STAGE=1
export butch_prefix=/usr/local
That's it. the STAGE=1 thing is needed so butch is aware that we're past
the bootstrapping stage. The C and C++ compiler's env variables are exported
with -I/usr/local/lib
added, so they will find the headers of things
we compile in addition to those in the system header directory.
The butch_prefix is set to /usr/local
so stuff we compile gets installed there
and doesn't interfere with system libraries.
Actually things will be installed in /opt/packagename
and then linked into
the root directory.
For lxpanel, the binary will live in /opt/lxpanel/usr/local/bin/lxpanel
and
be available as a symlink /usr/local/bin/lxpanel
pointing to the former.
As I don't like to type out the full path to the butch script, every time I use it, i will install it into a directory in $PATH:
cp KEEP/bin/butch* /usr/bin
If we would execute butch at this point, it would assume nothing is installed
and build the entire dependency tree for lxpanel, including a C compiler.
The reason for that is that its package installation database
/var/lib/butch.db
is still empty. In fact it doesn't even exist yet.
Let's fix that.
butch deps lxpanel | awk '{print $0 " 1"}' > /var/lib/butch.db
The strategy here is that we will make butch believe everything is already installed and then chip away dependency after dependency until the db mirrors adequately what's already installed by means of distro packages, and what's yet needed to be built.
/var/lib/butch.db
is a plain text file consisting of
"packagename packageversion" tuples on each line.
The command above created the file populated with all dependencies of lxpanel,
including itself, and the respective package version of 1.
Not having the latest version here is fine as long as you don't call
butch update
which would naturally try to update all installed to packages
to the latest package version.
Next step is to remove the line with lxpanel in butch.db, so we can try to build it and see which dependencies aren't there.
sed '/^lxpanel .*/d' -i /var/lib/butch.db
With that done,
butch install lxpanel
the output looks roughly like:
...
2025-01-26 22:43:37 building lxpanel (/src/build/build_lxpanel.sh) -> /src/logs/build_lxpanel.log
2025-01-26 22:43:38 WARNING: lxpanel failed to build! wait for other jobs to finish.
2025-01-26 22:43:38 done.
got 0 download errors and 1 build errors.
failed to build: lxpanel
time spent: 00:00:01
As expected the build fails and the log file contains the hint that libfm is missing. So we remove the libfm line in butch.db (so butch will try to build it) and try again.
sed '/^libfm .*/d' -i /var/lib/butch.db
butch install lxpanel
this time, libfm fails to find GTK+2. Since GTK+2 is already installed on the host debian, this means it's lacking the development header files we need for source builds. So let's install those headers.
apt install libgtk2.0-dev
You need to draw the line somewhere which packages you use from the distro repo or which ones you build yourself. For me, that's:
Continuing with our build:
butch install lxpanel
libfm package fails to build, because it wants libfm-extra installed first and we only pretended it's there. So we remove the libfm-extra line from butch.db. Rinse and repeat.
At some point it can't find libxml2, and as I mentioned earlier, this one we're gonna take from upstream.
apt install libxml2-dev
After half a dozen build failures, log checks to figure out which dependencies we have yet to build and removing their corresponding lines from butch.db, the lxpanel build finally succeeded.
And works; except that on first start the GLIBC dynlinker complained that it
couldn't find the libraries we built.
Since we're in GNU land, the solution is to run ldconfig
.
Optionally, or on a non-glibc system, one could export
LD_LIBRARY_PATH=/usr/local/lib
before running the binary, or by making a
wrapper that looks like
#!/bin/sh
LD_LIBRARY_PATH=/usr/local/lib /usr/local/bin/real-lxpanel "$@"
The only thing left to do is to wire up lxpanel in the openbox config so it's
started automatically when you startx
.