Minimal build system for small C projects. Written in Bourne shell (/bin/sh) for maximum portability and minimum dependencies.
f494542f — Kartik Agaram 16 days ago
work around
749ae780 — Kartik K. Agaram 1 year, 6 months ago
don't make readers think of Prolog the language
6241e9a4 — Kartik K. Agaram 1 year, 8 months ago
quote more variables


browse log



A minimal build system using Bourne sh

Build systems have a tendency to create languages, and their languages have a way of growing increasingly complex over time.

They work well once you get them working in your environment, but if they don't work out of the box for some reason, they can be daunting for your users to debug.

A common source of build errors is incompatibilities between build languages on different platforms. GNU make isn't the same as BSD make. autotools can autogenerate Makefiles for endless obsolete flavors of Unix, but it only serves to hinder you on the platform you're on right now. cmake can autogenerate build files for m build tools in n platforms, which means it needs to understand mXn mappings between DSLs.

An alternative approach is to sidestep existing tools (i.e. make) rather than build atop them. This repo illustrates a way to manage a project using just plain Bourne sh, available on all Unix systems for 30 years. No place for incompatibilities to rear their heads!


Say you have a command to run:

cc input1.c input2.c -o output

To only do this work when necessary, wrap this command in an older_than block:

older_than output input1.c input2.c && {
  cc input1 input2 -o output

If the output is newer than all inputs, nothing happens.

If any of the inputs is newer than the output, the command runs and updates the output.

A sequence of such blocks can mimic any makefile, and the extra verbosity can help someone debug your project if it doesn't work for them. Every command is explicit, as is the order to try them in.

Try it out

To see older_than at work and a more complete example of its use, try running the build script in this repo.

$ ./build
updating a.out
$ ./a.out
hello, world!
$ ./build
# no change to a.out

The build script is easy to repurpose to your needs. The older_than function in it is 16 lines of code.