Makefile воссоздает отсутствующие файлы зависимостей только при использовании правила статического шаблона - PullRequest
0 голосов
/ 01 ноября 2019

Рассмотрим код make-файла в листинге 1. Цель - создать файл зависимостей $ (DEPDIR) /%. D для каждого объектного файла% .o, сгенерированного при запуске компилятора C в строке 9. В частности, файлы зависимостейгенерируются как побочный эффект вызова компилятора C в строке 9 для преобразования файла% .c в файл% .o - т. е. в строке 9 выделяются два файла: $ (DEPDIR) /%. d и% .o.

Листинг 1.

1   DEPDIR := deps
2
3   SOURCES.c  := $(wildcard *.c)
4   OBJS.c  := $(SOURCES.c:.c=.o)
5
6   CPP_DEPFLAGS = -MT $@ -MMD -MF $(DEPDIR)/$*.d
#
#   Other rules, variable definitions, etc. are here...
#
7   %.o : %.c
8   $(OBJS.c) : %.o : %.c $(DEPDIR)/%.d | $(DEPDIR)
9       $(COMPILE.c) $(CPP_DEPFLAGS) $(OUTPUT_OPTION) $<
10
11  $(DEPDIR) : ; mkdir -p $@
12
13  # Ensure make considers missing $(DEPDIR)/%.d files as "not updated"
14  $(DEPDIR)/%.d : ;
15
16  # [EDIT]
17  .PRECIOUS: $(DEPDIR)/%.d

Этот код make-файла работает как надо , но я не понимаю, почему (см. Ниже) . В частности, при вызове make создается папка DEPDIR, если она не существует, а рецепт в строке 9 генерирует файлы $ (DEPDIR) /%. D, а также файлы% .o.

Теперь предположим, что пользовательудаляет один или несколько файлов% .d или всю папку DEPDIR. Повторный вызов make перестраивает отсутствующие файлы зависимостей по желанию , но, опять же, я не понимаю, почему .

$ rm -fr depdir *.o
$ make # Generates DEPDIR and the $(DEPDIR)/%.d files
$ rm -fr depdir
# NB: The %.o files still exist
$ make # Regenerates DEPDIR and the $(DEPDIR)/%.d files

Теперь замените правило статического шаблона в строке 8 неявным правилом, показанным вЛистинг 2, а в остальном оставьте все остальное как было:

Листинг 2.

7   %.o : %.c
8   %.o : %.c $(DEPDIR)/%.d | $(DEPDIR)
9       $(COMPILE.c) $(CPP_DEPFLAGS) $(OUTPUT_OPTION) $<

Когда используется неявное правило, make не восстанавливает удаленный / отсутствующийфайлы зависимостей - это НЕ ожидаемый результат:

$ rm -fr depdir *.o
$ make # Generates DEPDIR and the $(DEPDIR)/%.d files
$ rm -fr depdir
# NB: The %.o files still exist
$ make # Generates DEPDIR only; DOES NOT regenerate the $(DEPDIR)/%.d files

Версия правила статического шаблона учитывает удаленное / отсутствующее предварительное условие $ (DEPDIR) /%. d (в строке 8). ) будет обновлено (что я НЕ ожидал) по правилу в строке 14, что сделает цель% .o устаревшей, и, следовательно, рецепт в строке 9вызывается для восстановления файла% .o.

С другой стороны, неявное шаблонное правило, по-видимому, считает удаленное / отсутствующее условие $ (DEPDIR) /%. D (в строке 8) равным не обновлено актуальный (что на НЕ то, что я ожидал), и поэтому make считает, что цель% .o обновлена, и рецепт в строке 9 не вызывается.

Так почему же неявное правило шаблона (листинг 2) не вызывает строку рецепта в строке 9? В документации GNU Make я не нахожу ничего, что объясняет эту разницу между правилом статического шаблона и неявным правилом.

Ответы [ 2 ]

2 голосов
/ 01 ноября 2019

Если вы хотите понять это, вы можете прочитать Генерация автозависимостей . Но для объяснения:

Причина, по которой работает правило статического шаблона, заключается в строке 14:

13  # Ensure make considers missing $(DEPDIR)/%.d files as "not updated"
14  $(DEPDIR)/%.d : ;

. Это создает неявное правило, которое говорит, как создать файл .d с пустымрецепт. Рецепт ничего не делает, но если необходимо запустить make, make сочтет любую цель устаревшей. Таким образом, если файл отсутствует, make перестроит любую цель, которая зависит от него.

Удален исходный неправильный ответ

ОК, теперь я попробовал это сам и понял.

Причина, по которой ваше неявное правило не работает, заключается в том, что файлы .d считаются промежуточными файлами . Поскольку они являются промежуточными файлами, если они не существуют, make не пытается их перестраивать, если только их не нужно перестраивать по какой-либо другой причине;здесь его нет.

Чтобы доказать, что моя теория верна, сначала удалите все файлы .d, а затем прикоснитесь к файлам .c, чтобы принудительно восстановить их:

$ touch *.c
$ rm deps/*.d
$ make
cc    -c -MT a.o -MMD -MF deps/a.d -o a.o a.c
cc    -c -MT b.o -MMD -MF deps/b.d -o b.o b.c
cc    -c -MT d.o -MMD -MF deps/d.d -o d.o d.c
rm deps/a.d deps/d.d deps/b.d

Обратите внимание на последнюю строку здесь: это удаление всех промежуточных файлов.

Если вы посмотрите мой пост в блоге выше, вы увидите, что я рекомендовал изменить неявное правило на явное правило, для вас это будетчто-то вроде этого:

$(SOURCES.c:%.c=$(DEPDIR)/%.d):

Теперь все файлы .d являются явными целями и, следовательно, не могут быть промежуточными.

Обратите внимание на сообщение в блоге, на которое я ссылаюсь выше, дает немного лучший способ обработкиэто.

0 голосов
/ 02 ноября 2019

После дальнейших экспериментов проблема с листингом 1 в моем исходном посте - строка # 14:

$(DEPDIR)/%.d: ;

Yikes. О чем я только думал!? Вместо этого эта строка должна быть выражена через ссылку замещения (или через вызов patsubst и т. Д.):

$(OBJS:%.o=$(DEPDIR)/%.d): ;

Или следовать предложению MadScientist, которое, IMHO, предлагает улучшенную читаемость (и с небольшим изменениеммоя часть использовать OBJS вместо SOURCES),

DEPFILES := $(OBJS:%.o=$(DEPDIR)/%.d)
$(DEPFILES): ;

где

OBJS.c := $(SOURCES.c:%.c=%.o)
OBJS.cc := $(SOURCES.cc:%.cc=%.o)
OBJS := $(OBJS.c) $(OBJS.cc)

После изменения / исправления строки # 14 мой make-файл последовательно вызывает строку рецепта № 9 (в листинге 1) длявосстановить удаленные / отсутствующие файлы зависимостей как для случая правила статического шаблона, так и для случая неявного правила.

[EDIT]

Согласно комментариям MadScientist, я удалил строку .PRECIOUS из make-файла, так какбольше не требуется (после внесения изменений, которые я описал выше), и строка .PRECIOUS может вызвать нежелательное поведение.

...