← Весь справочник

make

Запуск команд по правилам из Makefile — цели, зависимости, переменные, пересборка только устаревшего

make читает Makefile и выполняет правила: как получить цель из её зависимостей. Цель пересобирается, только если её нет или какая-то зависимость новее её (сравнивается время изменения файлов). Поэтому make подходит и для сборки, и как короткое меню команд проекта.

Синтаксис

make [опции] [цель…] [ПЕРЕМЕННАЯ=значение…]

Без цели выполняется первая цель в файле (или та, что задана в .DEFAULT_GOAL). Файл ищется под именами GNUmakefile, makefile, Makefile.

Правило в Makefile:

цель: зависимость1 зависимость2
	команда        # строка рецепта начинается с символа TAB
	ещё_команда

Опции

ОпцияЧто делает
-nпоказать команды, ничего не выполняя (dry run)
-f файлвзять другой файл вместо Makefile
-C каталогперейти в каталог перед запуском
-sне печатать команды
-Bпересобрать всё, не глядя на время файлов
-j 4выполнять до 4 рецептов параллельно
-kпри ошибке продолжать с другими целями, которые от неё не зависят
-iигнорировать ошибки во всех рецептах
-qничего не делать, только код возврата: 0 — цель актуальна, 1 — нет
-eпеременные окружения важнее присваиваний в Makefile
-pнапечатать все правила и переменные (включая встроенные)
--debug=bобъяснить, почему цель пересобирается

Правила и цели

ЭлементЧто значит
цель: зависимостизаголовок правила; строки с TAB под ним — рецепт
a b: зависимостиодно правило для нескольких целей
%.html: %.mdшаблонное правило: любой .html из одноимённого .md
цель: обычные | порядковыезависимости после | должны существовать, но их время не проверяется (удобно для каталогов)
.PHONY: имя…цели, которые не являются файлами (clean, test, build)
.DEFAULT_GOAL := имяцель по умолчанию вместо первой в файле
.ONESHELL:выполнять весь рецепт одной оболочкой, а не построчно
include файл.mkвставить другой Makefile

Префиксы строк рецепта

ПрефиксЧто делает
@командане печатать саму команду перед выполнением
-командапродолжить, даже если команда завершилась с ошибкой
\ в конце строкипродолжить команду на следующей строке (одна оболочка)

Автоматические переменные

ПеременнаяЗначение
$@имя текущей цели
$<первая зависимость
$^все зависимости через пробел, без повторов
$+все зависимости с повторами, в исходном порядке
$?зависимости, которые новее цели
$*«основа» имени в шаблонном правиле (что совпало с %)
$(@D) / $(@F)каталог / имя файла цели
$$знак $ для оболочки ($$HOME, $$f в цикле)

Переменные

ЗаписьЧто делает
VAR = значениезначение раскрывается при каждом использовании
VAR := значениевычисляется сразу, в момент присваивания
VAR ?= значениезадать, только если переменная ещё не задана (например, из окружения)
VAR += ещёдописать к значению через пробел
$(VAR) или ${VAR}подставить значение
export VARпередать переменную в окружение команд рецепта
make VAR=xзначение из командной строки сильнее любого присваивания в файле

Переменные окружения тоже видны как $(HOME), но присваивание в Makefile их перекрывает.

Функции

ФункцияЧто возвращает
$(wildcard src/*.c)файлы по шаблону
$(patsubst %.c,%.o,$(SRC))замена по шаблону в каждом слове
$(SRC:.c=.o)короткая запись той же замены суффикса
$(subst from,to,текст)простая замена подстроки
$(notdir путь) / $(dir путь)имя файла / каталог
$(basename файл) / $(suffix файл)без расширения / только расширение
$(addprefix p/,a b) / $(addsuffix .txt,a b)приписать префикс / суффикс к каждому слову
$(filter %.c,список) / $(filter-out …)оставить / убрать подходящие слова
$(foreach x,список,текст)повторить текст для каждого элемента
$(shell команда)вывод команды оболочки
$(if усл,то,иначе)условие внутри выражения
$(error текст) / $(warning текст)остановить с ошибкой / вывести предупреждение

Условия

ifeq ($(MODE),prod)
FLAGS = -O2
else
FLAGS = -g
endif

ifdef DEBUG
FLAGS += -DDEBUG
endif

Также есть ifneq и ifndef. Условия проверяются при чтении файла, до выполнения рецептов.

Примеры

Небольшой Makefile, который склеивает два файла:

OUT = combined.txt

.PHONY: build clean

build: $(OUT)

$(OUT): part1.txt part2.txt
	cat $^ > $@

clean:
	rm -f $(OUT)
# что будет выполнено, без запуска
make -n

# собрать цель по умолчанию (первую — build)
make

# повторный запуск ничего не делает: цель новее зависимостей
make

# после изменения зависимости цель пересоберётся
touch part2.txt && make

# конкретная цель и переопределение переменной
make clean
make OUT=all.txt

# запустить Makefile из другого каталога
make -C /path/to/project build

# актуальна ли цель — для скриптов
make -q build && echo 'всё собрано'

Шаблонное правило, порядковая зависимость и цикл в рецепте:

SRC := $(wildcard src/*.md)
OUT := $(patsubst src/%.md,out/%.html,$(SRC))

.PHONY: all list

all: $(OUT)

out/%.html: src/%.md | out
	cp $< $@

out:
	mkdir -p $@

list:
	@for f in $(SRC); do \
	  echo "source: $$f"; \
	done

Подводные камни

  • missing separator. Stop. — строка рецепта начинается с пробелов, а не с TAB. Проверь через cat -A Makefile: TAB виден как ^I.
  • Каждая строка рецепта выполняется в отдельной оболочке. cd dir на одной строке не влияет на следующую. Пиши cd dir && команда в одну строку, продолжай строку через \ или включи .ONESHELL:.
  • Цель без .PHONY совпала с файлом или каталогом. Если рядом есть файл test или каталог docs, make test ответит 'test' is up to date и ничего не сделает.
  • $ в рецепте принадлежит make. Переменная оболочки пишется $$VAR, а $VAR make поймёт как $(V)AR — получится пустота и хвост AR.
  • Ошибка команды останавливает make с ненулевым кодом, следующие строки рецепта не выполняются. Префикс - гасит ошибку, но злоупотреблять им не стоит.
  • make -n печатает и команды с @ — так видно, что спрятано.
  • = и := ведут себя по-разному. A = $(B) возьмёт значение B на момент использования, A := $(B) — на момент присваивания. $(shell …) с = будет запускаться при каждом обращении.
  • Пересборка опирается на время изменения. Если файл скопировали с сохранением старой даты, make может решить, что цель актуальна. Тогда поможет make -B.
  • Без -j рецепты идут по очереди; с -j порядок не гарантирован, поэтому все зависимости должны быть указаны явно.

См. также

git · variables · exit-codes · redirection