Предположим, ваш единственный новый исходный файл - foo.c
, который содержит строки
#include "foo.h"
#include "bar.h"
Make определяет, что foo.d
устарел (потому что он не существует или foo.c новее), поэтому он выполняет правило.
%.d: %.c
@set -e; rm -f $@; \
$(CC) -MM $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
sinclude $(SOURCES:.c=.d)
Make оценивает $$$$
как $$
и передает его в оболочку;оболочка интерпретирует это как значение параметра $
, который представляет собой идентификатор процесса оболочки, который правило хочет использовать для создания уникального имени файла.Это останется неизменным только в пределах одной команды , поэтому правило пишется с продолжением строки ("\").Это не очень хороший способ сделать это;если есть разные процессы, пытающиеся собрать foo.d
в одно и то же время, то вы, вероятно, все равно будете в безопасности.Таким образом, мы можем переписать правило:
%.d: %.c
@set -e; rm -f $@
$(CC) -MM $< > $@.temp
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.temp > $@
rm -f $@.temp
sinclude $(SOURCES:.c=.d)
Мы можем обойтись без первого правила, оно на самом деле не касается вопроса.
Вторая команда, $(CC) -MM $< > $@.temp
, расширяется доgcc -MM foo.c > foo.d.temp
(или другой компилятор).Флаг -MM
указывает компилятору составить список зависимостей:
foo.o: foo.c foo.h bar.h
Следующая строка проверяет этот список с помощью sed
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g'
, что примерно переводится как "change * 1029"* to foo.o foo.d :
":
foo.o foo.d : foo.c foo.h bar.h
(И последняя команда удаляет временный файл.)
Это не лучший способ обработки зависимостей, так как он восстановит все %.d
файлы каждый раз, когда вы запускаете Make, даже те, которые не относятся к вашей цели.Более совершенный подход - Advanced Auto-Dependency Generation .