GNU Make
The GNU implementation of the Unix make utility — a declarative, rule-based language for describing dependency graphs and the commands that produce files from their prerequisites.
Created by Richard Stallman and Roland McGrath (later maintained by Paul D. Smith)
GNU Make is the GNU Project’s implementation of the Unix make(1) utility — a declarative, rule-based language for describing how files are produced from other files and the commands required to do so. A makefile is essentially a directed acyclic graph of dependencies annotated with shell recipes; make walks the graph, compares modification times, and runs the recipes whose outputs are older than their inputs.
The
makelanguage — its rule/prerequisite/recipe structure, its tab-indented commands, and its macro syntax — was defined by Stuart Feldman’s original 1976 implementation at Bell Labs and later codified by POSIX. This page is about the GNU implementation of that language: its history, the substantial extensions it adds, and why it has become the de-facto referencemakefor free Unix systems.
Origins
The original make(1) was written by Stuart Feldman at Bell Labs in 1976 and shipped in Version 7 Unix. Feldman’s paper, “Make — A Program for Maintaining Computer Programs,” introduced the core idea that has remained essentially unchanged for half a century: describe each output file as a target, list the input files it depends on, and write a recipe of shell commands that produces the target from the inputs. The tool decides what to rebuild by comparing file modification timestamps.
The GNU Project needed a freely-licensed implementation of make, and the first GNU Make appeared in 1988. The early implementation was due to Richard Stallman, with substantial contributions and the long-term redesign work coming from Roland McGrath, who served as the primary maintainer for many years. Paul D. Smith took over maintenance in the early 2000s and has shepherded the 3.8x and 4.x series since.
From the very beginning, GNU Make was more than a clone: it added pattern rules, conditional directives, a much richer macro/text-manipulation function library, and the convention of recursive $(MAKE) invocations that became fundamental to building large GNU projects.
The Make Language
A makefile is a sequence of rules, variable assignments, and directives. The canonical rule has the form:
| |
The infamous requirement that recipe lines begin with a literal tab character has tripped up generations of new users; GNU Make 3.82 introduced the .RECIPEPREFIX special variable, which allows the prefix to be redefined, but tabs remain the default and the de-facto convention.
The major language constructs are:
| Construct | Examples | Purpose |
|---|---|---|
| Explicit rules | foo.o: foo.c foo.h | Declare a specific target’s dependencies and recipe |
| Pattern rules | %.o: %.c | A template applied to any target/prereq pair matching the pattern |
| Variables | CC = gcc, CFLAGS := -O2, OBJ ?= foo.o | Recursive (=), simple (:=), conditional (?=), append (+=) |
| Automatic variables | $@, $<, $^, $*, $? | Refer to the current target, first prereq, all prereqs, stem, out-of-date prereqs |
| Functions | $(patsubst %.c,%.o,$(SRCS)), $(shell ...), $(foreach ...), $(if ...), $(call ...) | Text manipulation and computation |
| Conditionals | ifeq, ifneq, ifdef, ifndef, else, endif | Branch on variable values at parse time |
| Includes | include other.mk, -include optional.mk | Compose makefiles from fragments |
| Special targets | .PHONY, .PRECIOUS, .SECONDARY, .DELETE_ON_ERROR, .SUFFIXES, .POSIX | Modify make’s behavior for specific targets or globally |
The language has two distinct evaluation phases. First, the entire makefile is read and parsed: variable assignments are processed, includes are followed, conditionals are evaluated, and the rule graph is built. Only after the parse phase does make update targets by walking the graph and running recipes. This two-phase model is the source of much of make’s subtlety — for example, := variables are expanded at parse time while = variables are re-expanded each time they are used, and $(eval ...) lets you generate makefile syntax that is then fed back through the parser.
GNU Extensions Beyond POSIX
GNU Make is a superset of POSIX make, and the extensions are extensive enough that “writing a portable makefile” and “writing a GNU makefile” are essentially different disciplines. Notable GNU-only features include:
- Pattern rules with
%:%.o: %.cmatches any.otarget with a corresponding.cprerequisite. POSIX make has only the older, more limited suffix rules (.c.o:). $(eval ...)and$(call ...): dynamic makefile generation and parameterized macros, used heavily by Autotools, Kbuild, and Buildroot to construct rules programmatically..SECONDEXPANSION:: when set, prerequisites are expanded a second time after rules have been parsed, allowing prereqs to refer to automatic variables like$$@.- A large function library:
$(patsubst ...),$(filter ...),$(filter-out ...),$(sort ...),$(strip ...),$(wildcard ...),$(shell ...),$(foreach ...),$(if ...),$(or ...),$(and ...),$(word ...),$(words ...),$(dir ...),$(notdir ...),$(abspath ...),$(realpath ...), and many more — POSIX make has essentially none of these. - Order-only prerequisites:
target: normal-prereqs | order-only-prereqsdistinguishes prerequisites that must exist but whose timestamps should not trigger a rebuild. - Conditional directives:
ifeq,ifneq,ifdef,ifndefare GNU extensions, not POSIX. .PHONY,.DELETE_ON_ERROR,.ONESHELL,.NOTPARALLEL: special targets that modify make’s behavior..ONESHELLin particular changes the long-standing convention that each recipe line is a separate shell invocation.- The jobserver protocol: when GNU Make runs in parallel (
-jN), recursive sub-makes coordinate their concurrency by reading and writing tokens through a shared pipe (or, since 4.4, named pipes). This allows a build tree to honor a global-jlimit even across recursive$(MAKE)invocations. - Guile integration (since 4.0): GNU Make can be built with embedded GNU Guile, allowing Scheme code to be called from
$(guile ...). - Loadable objects (since 4.0):
load foo.sodynamically loads shared objects that register new make functions written in C.
These extensions are why a great deal of free software’s build infrastructure quietly assumes GNU Make even when the documentation says “make” — and why gmake is often installed as the GNU implementation on systems whose default make is BSD or Solaris flavored.
Parallelism and the Jobserver
GNU Make supports parallel builds via -jN, where N is the maximum number of recipes that may run concurrently. A naive parallel implementation would oversubscribe the system when one make recursively invokes another, because each sub-make would independently spawn N jobs.
GNU Make solves this with the jobserver protocol: the top-level make creates a pipe pre-loaded with N-1 tokens; each recursive sub-make inherits the pipe’s file descriptors and reads a token before starting a recipe, returning the token when the recipe completes. This way, the total number of concurrent jobs across the entire recursive build tree never exceeds the original -j value. As of GNU Make 4.4, a named-pipe variant of the jobserver was introduced to improve portability and make the protocol usable on systems where inheriting raw file descriptors is awkward.
Standardization and Portability
POSIX standardizes a conservative core of make features:
- The rule/prerequisite/recipe structure and tab-indented recipes.
- Suffix rules (
.c.o:style). - A small set of built-in variables and inference rules.
- Macros (variables) with simple substitution.
- A handful of special targets:
.SUFFIXES,.DEFAULT,.PRECIOUS,.IGNORE,.SILENT,.POSIX.
GNU Make can be invoked with --posix, or a makefile may begin with the special target .POSIX:, to suppress GNU-specific behavior. In practice, however, most makefiles depend on at least one GNU extension — pattern rules in particular — and portability is achieved either by restricting to a small POSIX subset (as Autotools/Automake do, by generating portable makefiles from higher-level descriptions) or by simply requiring GNU Make and saying so explicitly.
Comparison with Other make Implementations
Several actively maintained make implementations coexist:
- BSD make (
bmake, the NetBSD/FreeBSD/OpenBSD lineage): a different superset of POSIX make, with its own non-overlapping extensions —.forloops, modifier syntax like${VAR:M*.c}, and a distinct conditional syntax. BSD make and GNU Make makefiles are generally not interchangeable beyond their POSIX-conformant subset. - System V make / Solaris make: closer to POSIX, with few extensions; largely of historical interest now.
- NMAKE (Microsoft): a make-like tool for Windows, syntactically similar but semantically divergent and not compatible with GNU Make.
- Ninja: not a make-compatible tool, but a deliberately minimal build executor designed to be generated by higher-level systems (CMake, Meson, gn). Ninja was created in reaction to make’s slow incremental-rebuild times on very large projects, and trades make’s macro language and human-authorability for raw speed.
Current Status
GNU Make is actively maintained under Paul D. Smith, with version 4.4.1 released in 2023 as the most recent stable version as of 2026. The project has a relatively slow release cadence appropriate to a piece of infrastructure on which essentially every free-software build depends; backwards compatibility is taken seriously, and behavior changes — such as the macro-expansion tightening in 4.3 — are documented carefully because long-lived makefiles in the wild rely on subtle corner cases.
In day-to-day use, GNU Make is one of the most universally-deployed pieces of free software. It is the default make on GNU/Linux, available under the name gmake on most BSDs, macOS (where Apple reportedly ships an older 3.81 as /usr/bin/make, commonly attributed to GPLv3 licensing concerns), and Solaris derivatives, and it is the orchestration substrate of the Linux kernel, the GNU toolchain, Buildroot, Yocto’s lower layers, and a vast amount of legacy and contemporary infrastructure.
Why GNU Make Matters
- It is the reference
makefor the free Unix world. The Linux kernel, glibc, GCC, and the great majority of GNU projects assume GNU Make’s semantics and extensions. - Its extensions defined modern make idioms. Pattern rules with
%, automatic variables in their modern form,$(eval), the jobserver, order-only prerequisites, and.PHONYdiscipline are all GNU contributions that are taken for granted by anyone who writes makefiles today. - It is a small declarative language that has outlived nearly every fashion in build tooling. Despite many attempts to replace it — Ant, Maven, SCons, Rake, Gradle, Bazel, Buck, Pants, Ninja, Meson — GNU Make remains, often as the substrate that those higher-level systems eventually call into.
- It exemplifies the GNU philosophy of “compatible superset.” GNU Make behaves like POSIX make when asked to and like a much richer tool by default, the same pattern applied across coreutils, findutils, grep, sed, and the rest of the GNU userland.
For a tool whose surface job is “rebuild files when their inputs change,” GNU Make has proved astonishingly durable: a small, declarative dependency language with an embedded macro processor, almost forty years old in its GNU incarnation, and still the workhorse that quietly compiles much of the world’s free software.
Timeline
Notable Uses & Legacy
The Linux Kernel
The Linux kernel build system (`Kbuild`) is built on top of GNU Make and depends explicitly on GNU extensions — pattern rules, `$(eval)`, `$(call)`, recursive sub-makes, and the jobserver — to coordinate the compilation of tens of thousands of source files. Building the kernel without GNU Make is essentially impossible.
GNU Autotools (Autoconf / Automake)
The GNU build system generates portable `Makefile.in` templates that are processed by `configure` into `Makefile`s designed to work with both GNU Make and (portably) POSIX make. Automake-generated makefiles are the lingua franca of `./configure && make && make install` and underlie a substantial fraction of all free-software releases.
GCC, Glibc, Binutils, and the GNU Toolchain
The core GNU toolchain projects use GNU Make for their internal build systems. Bootstrapping GCC, in particular, involves multi-stage builds orchestrated entirely by makefiles that recursively invoke themselves with different configurations.
Embedded and Cross-Compilation Toolchains
Build systems for embedded Linux distributions — Buildroot, Yocto/OpenEmbedded's BitBake (partly), and OpenWrt — are built around GNU Make as the orchestration layer for cross-compilation of thousands of upstream packages. Buildroot in particular is a several-thousand-line GNU Make program.
CMake, Meson, and Other Build-System Generators
Higher-level build systems such as CMake (Unix Makefiles generator) and historical Autotools setups emit GNU Make–compatible makefiles as one of their backend formats. Even projects that author their build logic in CMake or Meson typically execute the final compilation through a `make` invocation.
Documentation, Data Pipelines, and Reproducible Research
Outside of compiling C/C++ code, GNU Make is widely used as a general-purpose task runner — driving LaTeX document builds, static-site generators, ETL/data pipelines, and reproducible-research workflows — because its dependency graph and timestamp-based incremental rebuild model fit those problems naturally.
Language Influence
Influenced By
Influenced
Running Today
Run examples using the official Docker image:
docker pull gcc:latestExample usage:
docker run --rm -v $(pwd):/work -w /work gcc:latest make