Text functions: wildcard, patsubst, foreach

Make · Practical · 12 min

Functions let a Makefile discover files and transform names instead of hard-coding every path.

Function calls look like $(name arg1,arg2). Note there is no space after commas unless you want it in the value. The most useful:

  • $(wildcard src/*.c) the files matching a glob (only ones that exist now).
  • $(patsubst %.c,%.o,$(SRC)) replaces by pattern; the shorthand $(SRC:.c=.o) does the same for suffixes.
  • $(subst from,to,text) plain substring replacement.
  • $(filter %.c,$(FILES)) keeps matching words; filter-out drops them.
  • $(addprefix build/,$(OBJ)), $(addsuffix ...), $(notdir ...), $(dir ...), $(basename ...).
  • $(sort list) sorts and removes duplicates; $(words ...), $(word n,...), $(firstword ...).
  • $(foreach v,list,text) repeats text for each word.
  • $(shell cmd) runs a shell command and inserts its output (newlines become spaces).
  • $(if cond,then,else), $(error msg), $(warning msg), $(info msg) for checks and debugging.

make thinks in space-separated words, so file names containing spaces are effectively unsupported. Keep build paths free of spaces.

Assign function results with := so they're evaluated once. With =, a $(shell ...) call would run again every time the variable is used.

Example

SRC   := $(wildcard src/*.c)
OBJ   := $(patsubst src/%.c,build/%.o,$(SRC))
NAMES := $(notdir $(basename $(SRC)))
TESTS := $(filter test_%,$(NAMES))
DIRS  := $(sort $(dir $(OBJ)) build/ build/)
GIT   := $(shell git rev-parse --short HEAD 2>/dev/null || echo none)

ifeq ($(SRC),)
$(error no sources found in src/)
endif

show:
	@echo SRC=$(SRC)
	@echo OBJ=$(OBJ)
	@echo NAMES=$(NAMES) TESTS=$(TESTS)
	@echo DIRS=$(DIRS) GIT=$(GIT)
	@echo $(foreach n,$(NAMES),bin/$(n))

# with src/main.c, src/util.c, src/test_util.c:
# SRC=src/main.c src/test_util.c src/util.c
# OBJ=build/main.o build/test_util.o build/util.o
# NAMES=main test_util util TESTS=test_util
# DIRS=build/ GIT=none
# bin/main bin/test_util bin/util

Remember

  • $(wildcard), $(patsubst) and $(SRC:.c=.o) build file lists
  • filter, addprefix, notdir, sort and foreach transform word lists
  • Assign $(shell ...) with := so it runs once

Watch out for

  • Adding a space after a comma, as in $(subst a, b,x), which puts the space into the result.

Check yourself

  1. What does $(patsubst %.c,%.o,a.c b.h c.c) produce?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.gnu.org/software/make/manual/make.html#Functions