Containerizing Make with Cake

Building Stuff the Easy Way

Uros Perisic
4 min readJun 4, 2021

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.

Chilled out GNU

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:

  1. 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
  2. Implementing complex logic in the Makefile to do the same in a container — complex, error prone, and inconvenient when integrating with CI/CD pipelines
  3. 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
  4. 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
  5. 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
  6. 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 of make), you can even keep your old Makefile with no modifications
  • It doesn’t touch your beloved dev rig
  • You can use whatever esoteric development environment you like
“Real Programmers” xkcd.com/378
  • 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 the Makefile can now remain environment agnostic. You’ll probably reuse your Dockerfile 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 or cake 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.

--

--