Everyone hates the best build system.
ecause they're stupid and their brains are not webscale.
And everyone complains about the bad ones.
Background
A few years ago, I got so mad at nix and nixos that I had the thought: is this really the best it gets?*1
But first, I have to*2 zoom out even further: why am I even using this tragic improvised mess?
Debian
Debian appeared in my life at an opportune moment: I had just convinced my parents to get me my first broadband connection to the internet and Debian just worked.
It didn't ask me to enter the refresh rate for my CRT monitor*3 into XFree86.conf under threat of blowing up my computer screen, lest I get that wrong. It also packaged all kinds of useful software and allowed me to install the rest from third party repositories.
It also had a few flaws. I will not attempt to justify whether ~20-year-old me's reasons for switching to Arch were objectively correct, but I will list them:
- Even debian sid/unstable packages could be a bit old, OTOH switching to debian experimental was a great way to break your system
- Maintaining a custom package overriding official ones was and still is a bit of a nightmare. I didn't know about epochs*4, but those do also introduce their own problems
- Most importantly: it wasn't all new and shiny *5
Arch
Arch was good. Arch delivered everything I had wanted from debian and also let me install the latest doodads from source control trunks easily and update them with minimal effort. It had only two flaws:
- Its package repository is so heavily intertwined with clang/llvm that it was impossible to install the bleeding edge version of clang. The one distributed officially with Arch was, at that time, consistently over a full year behind stable*6
- It wasn't hipster enough any more.*7
The search for a newer compiler lead me straight into the arms of
Gentoo
And what a short and disappointing romance that was.
While they do offer "profiles" for a bleeding edge clang-based system, the sad reality is that most of them have only been tested on one maintainer's computer and are not reproducible. The people are nice and helpful, but it still doesn't work.
Their preferred solution? Use a flatpak. Yes - use a flatpak for every program you use 99% of your time on the computer. Why even use Gentoo, then?
In fact, I would say the operating system and distribution is living proof for why we do need something like nix.
NixOS
A Gentoo that works on more than one computer. What a promise! And it delivered. Sort of.
Nix*8 introduces concepts orthogonal to the main focuses of other build systems: reproducible builds and build isolation. These prove surprisingly powerful in avoiding exactly the failure modes of Gentoo.
The price of it is 2-3 days of rebuilds on each update and all of your disk space.
But wait, content-addressed derivations*9! Dead-on-arrival. There's a good chance if you're using custom CFLAGS and CA-derivations, you might be literally the only person testing most package builds this way. Oh, and often they don't work even if they do build.
I have since given up on the Gentoo-that-works dream and mostly have been using the NixOS binary caches with standard CFLAGS and no ca-derivations.
Build systems
Alright then, is there anything better than nix? What are the options? What are the categories and criteria? Let's dive into an admittedly abridged and selective history of the literature.
Recursive make considered harmful*10
I would highly recommend reading the paper itself, as it is written in plain language and requires only a modicum of familiarity with unix-like systems and Makefiles to understand. Unfortunately, it is also wrong in one of its core assumptions: "Make is good enough and you're using it wrong."
The main takeaways are:
- Recursive make leads to incomplete dependency graphs
- Several techniques are identified in the paper to flatten the
Makefilestructure, as replacements for the related recursive practices
Non-recursive make considered harmful*11
Here's where it gets interesting. We are introduced to shake*12 - a build system designed for the Glasgow Haskell Compiler. The authors note that they had already been following all of the best practices from the AUUG97 paper. They were, however, able to identify deficiencies in make and point out additional desirable features of a build system. Some of it can be seen in the attached Table 1, which I've captured here in full:
Most notably:
- Additional criteria other than
mtimecan be used to avoid rebuilding a file that has merely beentouched - A non-semantic change to a dependency causes only the dependency to rebuild. The output artefact is identified as identical to the previous version and the chain of rebuilds is correctly short-circuited.
- A real change to a nested dependency propagates the rebuild only to its dependents
- A configuration change in the build system scoped to a dependency automatically causes a rebuild of said dependency regardless of freshness status
On top of the above, several Makefile rituals and hacks are also promoted to first class abstractions:
- Additional dependencies specified dynamically at build time
- Order-only dependencies (e.g. lazily ensure `config.h` is present, but do not force rebuild if it is modified)
- Concurrency control based on resources
- Rules with multiple outputs
Build Systems à la Carte*13
The final stop of our pseudo-academic journey. If you wish to read only one paper, let it be this one.
Here, the property of avoiding unnecessary rebuilds is finally defined as minimality. It is also, however, tortured beyond recognition to the point where even base make satisfies it, under very specific conditions.
The property of aborting/short-circuiting unnecessary rebuilds is split into [early] cutoff.
An additional axis is identified when it comes to the task-scheduling algorithm of the build system:
- A restarting algorithm is one that runs unconditionally, but aborts the task if an out-of-date dependency is encountered. It then jumps to building that dependency and restarts the parent task when done.
- Suspending schedulers will suspend the parent task, instead, then resume it when the dependency is ready. Similar, but much more efficient.
- A topological task-scheduler means simply static dependencies walked a in arbitrary order calculated based on the dependency graph of dependencies known in advance, like
make. You might actually have to call it twice, or three times.*14
Most importantly, we are first introduced to the idea of distributed binary caches, called "cloud builds" in the article.*15
These definitions allow the authors to create the following feature matrix:
Verdict
And the best build system is... [drumroll] BAZEL?*16
Well, in that case... I suppose I'm going to keep rolling my /etc/nixos dung-ball up the hill, at least until someone tries to make an operating system based fully on shake*17 and GHC*18.
It does seem like this particular field of computer science is still almost as underserved as it was in 1997, and this despite actual build systems having multiplied like locusts.*19
This post has been written fully without any AI assistance. Not because I don't like AI. In fact, I love AI. However, I wanted to demonstrate what my writing looks like with no involvement from a LLM. I may at some point write a follow-up post without this restriction, where I throw all of the known build systems into a tumbler and have the robot make a wider feature matrix.
- This actually happens every 3-4 weeks to anyone using NixOS in non-trivial ways. ↩
- I didn't have to, skip to the next section if you only want to read about build systems ↩
- I was 13, I didn't fully understand what that even meant ↩
- https://www.debian.org/doc/debian-policy/ch-controlfields.html#version ↩
- though they made a good effort being one of the first to adopt systemd ↩
- This has since been "fixed", in that it's still intertwined but someone actually updates it regularly ↩
- That may sound like a frivolous complaint, but I do genuinely believe it is important that your linux system at home should break regularly so that you are forced to confront how it works. The Valve partnership and the mainstreaming of Arch made the maintainers too cautious. It works too well. Additionally, I have always been a hobbyist first. I don't want a linux on the desktop. I don't believe the interests of a typical Mac OS or Windows user align with mine when it comes to the design of an operating system. I want to be forced to tinker with it, I want to do all this stuff they think is bad UX. ↩
- https://edolstra.github.io/pubs/phd-thesis.pdf ↩
- https://wiki.nixos.org/wiki/Ca-derivations ↩
- https://aegis.sourceforge.net/auug97.pdf Miller, Peter. (2002). Recursive Make Considered Harmful. AUUGN Journal of AUUG Inc.. 19. https://accu.org/journals/overload/14/71/miller_2004/ ↩
- https://simonmar.github.io/bib/papers/shake.pdf Mokhov, Andrey & Mitchell, Neil & Jones, Simon & Marlow, Simon. (2016). Non-recursive make considered harmful: build systems at scale. 170-181. 10.1145/2976002.2976011. https://simon.peytonjones.org/non-recursive-make/ ↩
- https://ndmitchell.com/downloads/paper-shake_before_building-10_sep_2012.pdf ↩
- https://www.microsoft.com/en-us/research/wp-content/uploads/2018/03/build-systems.pdf Andrey Mokhov, Neil Mitchell, and Simon Peyton Jones. 2018. Build Systems à la Carte. Proc. ACM Program. https://doi.org/10.1145/3236774 ↩
- Dang, that sounds so minimal ↩
- Presumably because, in 2018, you had to be cloud-brained to publish anything at Microsoft. ↩
- I have come to interpret the results this way because the way in which
bazelis not minimal is massively irrelevant when minimal is defined in such a way thatmakesatisfies it, butbazelwouldn't. ↩ - Or rather the mythical Cloud Shake or Distributed Shake ↩
- An attempt was made at the latter https://github.com/hsyl20/haskus-system, unfortunately without shake, though. ↩
- I suppose it is also worth noting that only those 4 are present in the feature matrix because they are representative for demonstrating several points made in the paper. Many other build tools and systems satisfy some subset of the measured features. ↩
Did you enjoy this article?
Recommend it — Standard Reader surfaces well-loved writing to more readers across the network.