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.

"No program is allowed to exist unless it was built 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 package management. 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 this isn't how it works. Few or no languages 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?

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?

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 and hope it worked (it did).

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...).

Python and Go are pretty straightforward; neither really has a "build system" (Python does have setuptools, but you don't need it just to run a project). go build builds your project, even if it's complicated, or you just run the Python file (after fiddling with some and crap for a multi-directory project).

Haskell and OCaml are even worse than C. With these ML family languages, it seems like the compiler was never meant to be usable on its own. 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.

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. We shouldn't need this much complexity just to write some code. Compilers should just work.

This page was last modified (UTC)