в каком порядке выполняется Makefile?

NAME = test
CC = gcc
CFLAGS = -Wall -Wextra -Werror
INC = -I ./include
FILES = test.c main.c
LIBFT = ../ft_printf/libftprintf.a
OBJ = $(addprefix build/, $(FILES:.c=.o))
SRC = $(addprefix src/, $(FILES))

all: $(NAME)

$(NAME): $(OBJ)
    @$(CC) -o $(NAME) $(LIBFT) $(OBJ)
build/%.o: src/%.c | build
    @$(CC) -c $(CFLAGS) $(INC) $< -o $@
build:
    @mkdir build

Если выполнить команду Make в каком порядке будет сборка? я думал будет выполнятся all: $(NAME) и после $(NAME): $(OBJ), но откуда make знает что нужно выполнить build/%.o: src/%.c | build @$(CC) -c $(CFLAGS) $(INC) $< -o $@ build: @mkdir build?


Ответы (1 шт):

Автор решения: Stanislav Volodarskiy

При запуске без параметров make выполняет первую цель. Хотя так говорить не правильно. make работает (как минимум) в два прохода: сперва отыскивает все зависимости (это вроде поиска в ширину в графе), затем выполняет команды в обратном порядке.

Итак первая цель all. Она зависит от пререквизита (цели) test. "Зависит" означает что чтобы достигнуть цели all, нужно достигнуть цели test. Здесь и далее я буду раскрывать макросы, чтобы была видна механика:

all: test

Цель test зависит от build/test.o и build/main.o. Чтобы достигнуть её, нужно построить два объектных файла и запустить gcc для сборки исполнимого модуля:

test: build/test.o build/main.o
    gcc -o test ../ft_printf/libftprintf.a build/test.o build/main.o

Цель build/test.o соответствует обобщенному правилу. По нему она зависит от build/test.с и build. На значок | пока не обращайте внимания. Обобщённое правило порождает конкретное правило:

build/test.o: src/test.c | build
    gcc -c -Wall -Wextra -Werror -I ./include src/test.с -o build/test.o

Цель build/main.o обрабатывается аналогично:

build/main.o: src/main.c | build
    gcc -c -Wall -Wextra -Werror -I ./include src/main.с -o build/main.o

Цели src/test.c и src/main.c не имеют соответствующих правил. В этой ситуации make убеждается что такие файлы есть на диске и помечает цели как достигнутые.

Цель build ни отчего не зависит, но приводит к созданию каталога build если его ещё не было на диске:

build:
    mkdir build

Поиск завершён. Теперь выполняем команды в обратном порядке:

mkdir build
gcc -c -Wall -Wextra -Werror -I ./include src/test.с -o build/test.o
gcc -c -Wall -Wextra -Werror -I ./include src/main.с -o build/main.o
gcc -o test ../ft_printf/libftprintf.a build/test.o build/main.o

В описании поиска в графе я опустил сравнение времен файлов. make сравнивает времена цели и её пререквизитов и если цель свежая, то она считается достигнутой сразу.

Вы можете попросить make показать какие команды от собирается исполнить (make -n) или объяснить почему он собирается их выполнять (make --trace).

В комментариях заметил важный момент. make все цели считает файлами на диске. Если у вас на диске окажется файл с именем all и дата у него будет свежее чем у test, то make посчитает что делать ничего не надо. Цели, которые "не файлы" помечайте так:

.PHONY: all
all: test
→ Ссылка