Makefiles for website generation
August 2025
Over time, I've been accumulating various small websites that I want to keep online on the same domain as this blog. This can be everything from an old project, the documentation for a file format, to a random patch file that needs to be accessible over HTTP. Most of these things don't deserve their own domain or subdomain, but I still want to keep them online. They feel just about right to stuff in a subdirectory.
I use simple static files for yurix. Other parts, like this blog article, use a script to generate static pages. The pages generated by scripts also use all kinds of scripting languages and frameworks. In short: not only the content is diverse, the way that content is built is diverse too. In the end, I have to assemble it all into one directory before uploading it to my hosting provider via FTP.
This assembly step has been manual and error-prone until now. I've always wanted to automate it, but the diversity in the way the content is being generated means the automation framework needs to be very flexible.
I've considered using bash scripts. The issue with that is that it takes quite long to assemble everything. You don't have any parallelism and everything has to be redone all the times. Full rebuilds are not always necessary, and adding a simple missing comma shouldn't take what feels like ages before I see the change in the browser. I want incremental rebuilds.
The solution I found is a Makefile. While Makefiles are mostly known for being used for building C applications, they actually just specify shell commands and their dependencies. That can be a C compiler or a linker, but you can also call a python script or simply cp
to copy a file. The shell gives me the flexibility of calling any possible tool I could use for the website. It also feels easy to use because I usually invoke these tools on the CLI anyway.
On top of that, you can specify dependencies of files for incremental rebuilds. After fixing a typo, I can rebuild and reload the page in 100ms. It is sometimes a bit hard to figure out and specify all dependencies of an output, but I've yet to encounter a situation where it wasn't possible.
You can even use other build systems from a Makefile. Just call the build system from your Makefile. It is all shell. If your build system supports incremental compilation, you can also instruct Make to always call the build system so it can check if it has to do incremental updates. That way, you can avoid two incremental systems fighting each other because one thinks the other has to update, while the second thinks it doesn't have to.
Makefiles might sound a bit old-fashioned, but they are actually really useful to wrap incremental compilation around basically everything.