Skip to content

Make & Build Systems — Trivia & Interesting Facts

Surprising, historical, and little-known facts about Make and build systems.


Make was created in 1976 because its author forgot to recompile

Stuart Feldman at Bell Labs created Make in 1976 after spending a morning debugging a program that was actually fine — he had just forgotten to recompile after a change. Make's dependency-driven rebuild model ensured that only modified files and their dependents would be recompiled. Feldman received the ACM Software System Award for Make in 2003.


The tab-vs-spaces requirement was an unintended accident

Make requires tab characters (not spaces) to indent recipe lines. Feldman himself called this a design flaw, but by the time he realized the problem, Make already had about a dozen users and he felt he could not change it. This accidental syntax requirement has confused developers for nearly 50 years and remains one of the most infamous quirks in programming tools.


GNU Make added 80% of the features people actually use

The original Make was quite simple. GNU Make (first released by Richard Stallman and Roland McGrath in 1988) added pattern rules, automatic variables ($@, $<, $^), conditional directives, functions for string manipulation, and the include directive. Most Makefiles written today use GNU Make extensions and are not portable to other Make implementations.


Make's dependency graph is a DAG — directed acyclic graph

Make builds a directed acyclic graph of targets and prerequisites, then performs a topological sort to determine build order. If a cycle is detected (A depends on B, B depends on A), Make reports an error. This graph-based approach was revolutionary in 1976 and directly influenced every build system that followed, from Ant to Bazel.


Recursive Make is considered harmful

In 1998, Peter Miller published the influential paper "Recursive Make Considered Harmful," arguing that the common pattern of calling Make from within Make (submakes for subdirectories) breaks dependency tracking across boundaries. A change in one directory might not trigger rebuilds in another. Non-recursive Make (a single Makefile with includes) provides correct dependencies at the cost of complexity.


Make is Turing-complete

GNU Make's macro system, combined with its $(eval), $(call), and recursive variable features, makes it theoretically Turing-complete. People have implemented Conway's Game of Life, Fibonacci calculators, and even a working Tetris game entirely in Makefile syntax. This computational power is also why complex Makefiles can be nearly impossible to debug.


Bazel, the Google build system, evolved from a 2006 internal tool called Blaze

Google built Blaze in 2006 to handle builds across their monolithic multi-billion-line codebase. Bazel (an anagram of Blaze) was open-sourced in 2015. It introduced hermetic builds (every input explicitly declared), remote caching, and distributed build execution. Build rules are written in Starlark, a Python dialect designed to be deterministic and side-effect free.


Ninja was built because Make was too slow for Chrome

Evan Martin created Ninja in 2010 while working on Google Chrome because Make spent more time parsing Makefiles and checking file timestamps than actually building. Ninja is intentionally minimal — it has no conditionals, no functions, and no built-in rules. It is designed to be generated by a higher-level build system (like CMake or Meson) and then execute builds as fast as possible.


CMake does not actually build anything

CMake (Cross-platform Make) is a meta-build system: it generates native build files for other tools. CMake can output Makefiles, Ninja files, Visual Studio projects, or Xcode projects. This design lets a single CMakeLists.txt support any platform. CMake was created by Kitware in 2000 for the Insight Segmentation and Registration Toolkit (ITK) medical imaging project.


Phony targets in Make predate CI/CD pipelines by decades

The .PHONY declaration tells Make that a target does not represent a real file. Common phony targets like make clean, make test, make install, and make deploy effectively created a standardized task runner interface decades before task runners like Grunt, Gulp, or npm scripts existed. Many DevOps teams today use Makefiles purely as task runners, never invoking actual compilation.