Containerizing Make with Cake
Building Stuff the Easy Way
There is a plethora of build systems and tools out there, each with it’s own advantages and shiny new features, but let’s face it, the O.G. Make (with all its flaws) still reigns supreme.
According to Godwin’s law of Software Development:
As a project grows, the probability of a Makefile
being written for it slowly approaches 1, irrespective of the build system it predominantly uses.
The Problem
Great as Make is, it has one major limitation. (A) Makefie(s)
(± some Autotools magic) can only codify build logic, not the build environment — an issue that is typically addressed using one of the following (IMHO less-than-ideal) approaches:
- Implementing complex logic in the
Makefile
to pull down and install all relevant (and often cross-language) dependencies on the developer’s host — complex, error prone, and kind of invasive - Implementing complex logic in the
Makefile
to do the same in a container — complex, error prone, and inconvenient when integrating with CI/CD pipelines - Leaving build environment setup to each developer, with varying levels of help from documentation — tedious, and results in a bunch of bug reports due to misconfigured environments
- Heavy-duty solutions like Vagrant — feature-complete and battle-tested, but a massive additional dependency, rarely found on your average computer, not as easy to set up as you might think, not familiar to most developers, and it requires standardization of the development environment as well as the build environment
- Trying to replace make with a language specific tool and re-implementing make in the process as your project inevitably grows past fitting tidily into a single ecosystem — works really well as long as you’re an exception to the rule (you do fit tidily into a single ecosystem), gets kind of cumbersome otherwise
- Using local implementations of forge-specific build pipelines as a replacement for make — not portable, foreign to most devs, but very, very cool
A Solution
I often found myself going for option №2 listed above, but became increasingly annoyed by the hack-y, copy-past-y nature of it. Fueled by my frustrations and informed by my past experiences with some of the other approaches I came up with a loose specification of what I would like in a solution to this problem. A tool that:
- Unambiguously describes the development environment
- Is easy to use/does not require any additional knowledge from the developer
- Does not directly modify the developer’s machine
- Does not force a specific development environment (hey, devs are picky, and you’re paying us to be productive, not miserable)
- Is fully automated
- Smoothly integrates with CI/CD pipelines, without the need to re-implement all of the build logic in 2 places/tie into a specific CI/CD solution
- Uses software already found on most dev’s machines
Cake is the result of my efforts to do so. It’s a really thin, drop-in replacement/wrapper around Make that runs all of your targets inside of a development Docker/Podman container. It’s nothing fancy, but it makes my life significantly easier and as far as I can tell it ticks all of the boxes above:
- It fully specifies the build environment (with some rounding errors, such as the kernel)
- If you know how to use Make you know how to use Cake (literally, just type
cake
instead ofmake
), you can even keep your oldMakefile
with no modifications - It doesn’t touch your beloved dev rig
- You can use whatever esoteric development environment you like
- It’s fully automated
- You can completely reuse the logic from your
Makefile
in your build pipeline (this time type ‘make’ instead of ‘cake’) since since theMakefile
can now remain environment agnostic. You’ll probably reuse yourDockerfile
in the pipeline too. Nice and DRY. - In this day and age, who doesn’t have Docker/Podman installed?
The Vision
Though Cake supports more complex workflows, most projects that currently have a Makefile
at their root should also place a developer-focused Dockerfile
there for convenience and portability.
- The
Makefile
is the single source of truth for the build process - The
Dockerifle
is the single source of truth for the build environment - A container runtime should not be a hard dependency to build the project
- Choosing between containerized and “naked” builds should be as easy as typing
make
orcake
interchangeably
The Naming Dilemma
After writing Cake, I became aware of the other Cake, a Make replacement in the C# ecosystem. I considered leaving things as they are— odds are my Cake will never become big enough to cause any confusion, as well as renaming the project to one of the following:
- Bake — it’s a verb and it still alludes to the process of creation (of cake)
- Fake — in some sense you are using Make in a “fake” environment
- Jake — an homage to BSD jails
- Kake — kinda weird, but it works out phonetically
- Lake — my girlfriend says it’s cute
- Rake — going with the last letter of Container instead of the first
- Sake — yes, like the booze
Let me know what you think about this in the comments — this is one of those things where it’s best to submit to the will of the people.