Real builds need debug and release modes, platform differences and shared fragments without copy-paste.
Conditionals are evaluated when make reads the Makefile, not when recipes run:
ifeq ($(DEBUG),1)
CFLAGS += -O0 -g
else
CFLAGS += -O2
endifAlso available: ifneq, ifdef VAR (true if the variable has a non-empty value), ifndef. Detect the platform with UNAME_S := $(shell uname -s) and ifeq ($(UNAME_S),Darwin); on Windows $(OS) is Windows_NT.
Don't indent conditional directives with a tab: lines beginning with a tab are treated as recipe lines.
include reads other makefiles as if pasted in: include config.mk for shared settings, -include local.mk for optional per-developer overrides (not committed). make tries to remake included files: if there is a rule for config.mk, make builds it, then restarts reading. That powers generated configuration but can surprise you.
For build modes that should not mix, put outputs in separate directories (build/debug, build/release), so switching DEBUG=1 doesn't leave you linking objects compiled with different flags. make doesn't track flag changes on its own; a common trick is a build/flags.txt file rewritten only when the flags change and listed as a prerequisite.
$(MAKECMDGOALS) holds the targets given on the command line, handy to skip expensive includes when only clean was requested.
Example
UNAME_S := $(shell uname -s)
MODE := $(if $(filter 1,$(DEBUG)),debug,release)
BUILD := build/$(MODE)
ifeq ($(MODE),debug)
CFLAGS += -O0 -g -fsanitize=address
else
CFLAGS += -O2 -DNDEBUG
endif
ifeq ($(UNAME_S),Darwin)
LDLIBS += -framework CoreFoundation
endif
-include local.mk # optional, not in git
info:
@echo "$(MODE) -> $(BUILD), CFLAGS=$(CFLAGS)"
# $ make info -> release -> build/release, CFLAGS=-O2 -DNDEBUG
# $ make info DEBUG=1 -> debug -> build/debug, CFLAGS=-O0 -g -fsanitize=addressRemember
- ifeq/ifdef are evaluated while reading the Makefile
- include shares fragments; -include makes them optional
- Separate output directories per build mode
Watch out for
- Indenting ifeq with a tab inside a rule area, so make treats it as a shell command.