Build systems are a scourge

The villagers cried as the warlord Cabal and his troops burned their homes.

Some of them were trying to quickly rebuild a small shelter to protect themselves from the bugs outside. It was such a simple piece of work, Cabal shouldn't need to know about it, they thought. But Cabal's troops found them and promptly knocked it down.

"Nothing can be built except by Cabal!" the warlord shouted. "Burn them all!"


"Build systems" shouldn't exist. A compiler should be able to compile the damn code.

There are three problems that need to be solved to build code: compiling the source, finding the source files, and installing the source files. The first is clearly a compiler's job. The last is clearly a package manager's job. The middle problem is small and does not call for a separate tool with its own config files.

Here's how it ought to work:

But many languages don't handle dependencies so elegantly. The C ecosystem revolves around make, by which I mean the different GNU, BSD, and Microsoft makes, each of which is basically a separate language, and pkg-config, because what did you think you could just import an installed package and use it?

GNU make vs. BSD make - a practical problem experience

And you can't get far without running into the surreal mess called GNU autotools. Who doesn't like a bunch of 3000-line files with weird names involved in their build process? Who doesn't want to debug that?


And what's this CMake thing? Wait, it's *not* a dialect of make, and it's also not an alternative to make, you need both?

When I submitted my patch to GTK to fix a GObject Introspection annotation, I wanted to compile GTK after my change first, because I was terrified of how I'd feel if I submitted a patch that broke the build, as sure as I was that that was impossible. I spent about 12 hours trying to do so before giving up. The quest led me through installing Meson, Ninja, hunting down dependencies manually after each failed compilation informed me of another one, googling cryptic error messages and reading old mailing list archives, trying on an Ubuntu system after giving up on the FreeBSD one, and finally deciding I'd just submit the merge request without having seen a successful build.

My GTK patch

While GTK was the worst, *most* C projects I've worked with have given me huge trouble building them.

The Javascript ecosystem has a similar situation drowned in webpack, babel, rollup, and several config files, but it's more defensible because those tools solve problems that aren't created by the Javascript interpreters (minifying and bundling to save bandwidth, package imports, polyfills...).

Javascript review

I think Crystal is the best example I've seen. Imports work the way I prescribe above, and dependencies are named in shard.yml and installed in lib/ in your project root. I've never had any problem building or installing Crystal packages. Rust is similar. Go is another language that handles building well; `go build` builds your project, end of story (third-party dependencies are actually imported by URL, but filesystem path overrides can be specified in go.mod).

Crystal review

Rust review

Go review

Haskell and OCaml are even worse than C. With these ML family languages, it seems like the compiler was never meant to be directly usable. So people wrote a dozen different tools to wrap the compiler so you can actually use it. All of said tools introduce their own problems, so you still can't actually build anything with them, and the solution is always to install and use another tool on top of it.

With OCaml, `ocamlopt` is the native code compiler (`ocamlc` is a bytecode compiler and `ocaml` is an interpreter). `dune` is the "build system". `opam` is the "package manager". Then what are `ocamlfind` and `ocamlbuild`, you ask? I don't know, but they're involved in some tangled way. All the documentation is out of date and nothing they say works. I literally gave up on installing the de-facto standard library Batteries.


The built-in `Str` module apparently requires explicit linking. If you're using `ocaml`, you can load it with `#load "str.cma";;`. *But that directive is invalid syntax* to `ocamlopt` and `ocamlc`, which want command-line arguments instead - *different* ones.


This is madness! Compilers should Just Work!

Proxied content from gemini://yujiri.xyz/software/build-systems.gmi

Gemini request details:

Original URL
Status code
text/gemini; lang=en
Proxied by

Be advised that no attempt was made to verify the remote SSL certificate.

What is Gemini?