# make — Build-Abläufe mit Makefiles automatisieren

> Praxis-Guide zu GNU Make: Targets, Prerequisites und Rezepte in Makefiles – Build-Automatisierung und universeller Task-Runner für jedes Projekt.

Source: https://www.jpkc.com/db/cheatsheets/build-languages/make/

<!-- PROSE:intro -->
GNU Make ist das klassische Werkzeug zur Build-Automatisierung: Es liest ein Makefile und entscheidet anhand von Zeitstempeln, welche Targets neu gebaut werden müssen. Jede Regel besteht aus einem Target, seinen Prerequisites (Abhängigkeiten) und einem Rezept – den Shell-Kommandos, die das Target erzeugen. Wichtig: Rezeptzeilen müssen zwingend mit einem echten Tabulator beginnen, niemals mit Leerzeichen, sonst bricht Make mit „missing separator" ab. Längst nicht mehr nur für C/C++ gedacht, eignet sich Make als universeller Task-Runner für nahezu jedes Projekt.
<!-- PROSE:intro:end -->

## Make ausführen

`make` — Führt das erste (Standard-)Target im Makefile aus.

```bash
make
```

`make <target>` — Führt ein bestimmtes Target aus.

```bash
make build
```

`make -f <file>` — Verwendet ein bestimmtes Makefile statt des Standards.

```bash
make -f Makefile.prod build
```

`make -n` — Probelauf: Zeigt die Kommandos an, die ausgeführt würden, ohne sie auszuführen.

```bash
make -n deploy
```

`make -j <n>` — Führt bis zu n Jobs parallel aus für schnellere Builds.

```bash
make -j$(nproc)
```

`make -j` — Führt unbegrenzt viele Jobs parallel aus (mit Vorsicht verwenden).

```bash
make -j
```

`make -B` — Baut alle Targets bedingungslos neu (erzwungener Rebuild).

```bash
make -B
```

`make -s` — Stiller Modus: Gibt die Kommandos beim Ausführen nicht aus.

```bash
make -s build
```

`make -k` — Weitermachen: Baut andere Targets weiter, auch wenn eines fehlschlägt.

```bash
make -k test
```

`make -C <directory>` — Wechselt vor dem Lesen des Makefiles in das angegebene Verzeichnis.

```bash
make -C src/ build
```

`make VAR=value` — Überschreibt eine Variable auf der Kommandozeile.

```bash
make CC=clang build
```

## Makefile-Grundlagen

`target: prerequisites` — Grundregel: Das Target hängt von den Prerequisites ab und wird durch das Rezept gebaut. Rezeptzeilen müssen mit einem Tabulator beginnen.

```makefile
build: main.o utils.o
	gcc -o app main.o utils.o
```

`VAR = value` — Rekursiv expandierte Variable (bei jeder Verwendung neu ausgewertet).

```makefile
CC = gcc
CFLAGS = -Wall -O2
```

`VAR := value` — Einfach expandierte Variable (einmalig beim Zuweisen ausgewertet).

```makefile
DATE := $(shell date +%Y%m%d)
```

`VAR ?= value` — Setzt die Variable nur, wenn sie noch nicht definiert ist.

```makefile
PREFIX ?= /usr/local
```

`VAR += value` — Hängt an eine bestehende Variable an.

```makefile
CFLAGS += -g
```

`export VAR` — Exportiert eine Variable an Sub-Make-Prozesse und Rezept-Shells.

```makefile
export PATH := $(PWD)/bin:$(PATH)
```

## Automatische Variablen

`$@` — Der Name des Targets der aktuellen Regel.

```makefile
build:
	echo "Building $@"
```

`$<` — Das erste Prerequisite.

```makefile
%.o: %.c
	$(CC) -c $< -o $@
```

`$^` — Alle Prerequisites (Duplikate entfernt).

```makefile
app: main.o utils.o
	$(CC) -o $@ $^
```

`$?` — Alle Prerequisites, die neuer sind als das Target.

```makefile
lib.a: a.o b.o
	ar rcs $@ $?
```

`$*` — Der Stamm (% -Teil), den eine Pattern-Regel gematcht hat.

```makefile
%.o: %.c
	echo "Compiling $*"
```

`$(@D) / $(@F)` — Der Verzeichnis- bzw. Dateinamen-Teil des Targets.

```makefile
build/%.o: src/%.c
	mkdir -p $(@D)
	$(CC) -c $< -o $@
```

## Pattern-Regeln & Wildcards

`%.o: %.c` — Pattern-Regel: Baut jede .o-Datei aus der zugehörigen .c-Datei.

```makefile
%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@
```

`$(wildcard <pattern>)` — Expandiert ein Glob-Muster zu den passenden Dateinamen.

```makefile
SOURCES := $(wildcard src/*.c)
```

`$(patsubst <from>,<to>,<text>)` — Ersetzt ein Muster in einer Wortliste.

```makefile
OBJECTS := $(patsubst %.c,%.o,$(SOURCES))
```

`$(SOURCES:.c=.o)` — Substitutionsreferenz: Kurzform für patsubst.

```makefile
OBJECTS := $(SOURCES:.c=.o)
```

`$(filter <pattern>,<text>)` — Behält nur die Wörter, die zum Muster passen.

```makefile
C_FILES := $(filter %.c,$(ALL_FILES))
```

`$(filter-out <pattern>,<text>)` — Entfernt die Wörter, die zum Muster passen.

```makefile
SOURCES := $(filter-out test_%,$(ALL_SOURCES))
```

## Funktionen

`$(shell <command>)` — Führt ein Shell-Kommando aus und fängt dessen Ausgabe ab.

```makefile
GIT_HASH := $(shell git rev-parse --short HEAD)
```

`$(info <text>)` — Gibt während des Makefile-Parsings eine Informationsmeldung aus.

```makefile
$(info Building version $(VERSION))
```

`$(warning <text>)` — Gibt eine Warnung aus (inklusive Datei und Zeilennummer).

```makefile
$(warning CC is set to $(CC))
```

`$(error <text>)` — Gibt eine Fehlermeldung aus und bricht die Ausführung ab.

```makefile
ifndef CONFIG
$(error CONFIG is not set)
endif
```

`$(foreach var,list,body)` — Schleife: Wertet den Rumpf für jedes Wort der Liste aus.

```makefile
$(foreach dir,src lib test,$(wildcard $(dir)/*.c))
```

`$(if condition,then,else)` — Bedingte Funktion: Gibt then zurück, wenn die Bedingung nicht leer ist.

```makefile
FLAGS := $(if $(DEBUG),-g -O0,-O2)
```

`$(call func,arg1,arg2)` — Ruft eine selbst definierte Funktion auf (mehrzeiliges Makro).

```makefile
define compile
	$(CC) $(CFLAGS) -c $(1) -o $(2)
endef

%.o: %.c
	$(call compile,$<,$@)
```

## Bedingungen

`ifeq ($(VAR),value) … endif` — Bedingter Block: Wird ausgeführt, wenn die Variable dem Wert entspricht.

```makefile
ifeq ($(OS),Linux)
CFLAGS += -DLINUX
endif
```

`ifneq ($(VAR),value) … endif` — Bedingter Block: Wird ausgeführt, wenn die Variable NICHT dem Wert entspricht.

```makefile
ifneq ($(DEBUG),)
CFLAGS += -g
endif
```

`ifdef VAR … endif` — Bedingter Block: Wird ausgeführt, wenn die Variable definiert ist.

```makefile
ifdef VERBOSE
Q :=
else
Q := @
endif
```

`ifndef VAR … endif` — Bedingter Block: Wird ausgeführt, wenn die Variable NICHT definiert ist.

```makefile
ifndef CC
CC := gcc
endif
```

## Spezielle Targets & Direktiven

`.PHONY: <targets>` — Deklariert Targets, die keine echten Dateien sind (laufen immer).

```makefile
.PHONY: all build clean test deploy
```

`.DEFAULT_GOAL := <target>` — Legt das Standard-Target fest, wenn auf der Kommandozeile keines angegeben wird.

```makefile
.DEFAULT_GOAL := build
```

`.SILENT: <targets>` — Gibt die Rezepte der angegebenen Targets nicht aus.

```makefile
.SILENT: help
```

`include <file>` — Bindet ein weiteres Makefile ein (Fehler, wenn nicht gefunden).

```makefile
include config.mk
```

`-include <file>` — Bindet ein weiteres Makefile ein (wird stillschweigend ignoriert, wenn nicht gefunden).

```makefile
-include .env.mk
```

`@<command>` — Stellt einer Rezeptzeile ein @ voran, um das Echo des Kommandos zu unterdrücken.

```makefile
help:
	@echo "Available targets: build, test, clean"
```

`-<command>` — Stellt ein - voran, um Fehler dieses Kommandos zu ignorieren.

```makefile
clean:
	-rm -f *.o app
```

## Häufige Makefile-Muster

`all: build` — Konvention: Das Target „all" baut alles.

```makefile
all: build docs
```

`clean:` — Konvention: „clean" entfernt die Build-Artefakte.

```makefile
.PHONY: clean
clean:
	rm -rf build/ dist/
```

`install:` — Konvention: „install" kopiert die gebauten Dateien in System-Verzeichnisse.

```makefile
install: build
	install -m 755 app $(PREFIX)/bin/
```

`test:` — Konvention: „test" führt die Test-Suite aus.

```makefile
.PHONY: test
test:
	go test ./...
```

`help:` — Selbstdokumentierendes Help-Target: Extrahiert ##-Kommentare aus den Targets.

```makefile
build: ## Build the application
	go build -o app

help: ## Show this help
	@grep -E '^[a-zA-Z_-]+:.*?## ' $(MAKEFILE_LIST) | sort | \
		awk 'BEGIN {FS = ":.*?## "}; {printf "  %-20s %s\n", $$1, $$2}'
```

<!-- PROSE:outro -->
## Fazit

Make ist seit Jahrzehnten der Standard für Build-Automatisierung unter Unix und Linux – und trotz vieler neuerer Build-Tools bleibt es allgegenwärtig. Wenn du die drei Grundbausteine verstanden hast – Targets, Prerequisites und Rezepte –, kannst du Make für nahezu jede wiederkehrende Aufgabe einsetzen, nicht nur zum Kompilieren von C-Code. Achte auf die berüchtigte Tab-Pflicht in Rezeptzeilen und deklariere mit `.PHONY` jene Targets, die keine Dateien erzeugen. Dann bleibt dein Makefile robust, portabel und sowohl für Mitarbeitende als auch für KI-Systeme leicht nachvollziehbar.

## Weiterführende Links

- [GNU Make – Handbuch](https://www.gnu.org/software/make/manual/) – offizielle Referenz zu allen Funktionen und Direktiven (englisch)
- [Make – Wikipedia](https://de.wikipedia.org/wiki/Make) – Überblick zu Geschichte, Konzepten und Varianten
<!-- PROSE:outro:end -->

## Verwandte Kommandos

- [artisan](https://www.jpkc.com/db/cheatsheets/build-languages/artisan/) – Kommandozeilen-Werkzeug des Laravel-Frameworks
- [cargo](https://www.jpkc.com/db/cheatsheets/build-languages/cargo/) – Build-System und Paketmanager für Rust
- [composer](https://www.jpkc.com/db/cheatsheets/build-languages/composer/) – Abhängigkeitsverwaltung für PHP-Projekte

