Makefile игнорирует зависимость через раз - PullRequest
0 голосов
/ 12 октября 2018

Я хочу, чтобы мой Makefile выполнял следующие действия:

  • Всегда запускайте некоторые команды, когда главная цель требует определенный файл
  • Перестройте все, что зависит от этого файла, если оноизменено (в результате выполнения этих команд)

В моем случае этот файл является файлом C-заголовка, который должен содержать информацию о версии;Я хочу, чтобы make-файл всегда обновлял этот заголовок версии и перестраивал все файлы, которые его включают.

Что происходит: когда я запускаю make, обновление заголовка версии выполняется каждый раз (как я хочу).Но файлы, которые зависят от этого заголовка, перестраиваются только каждый ДРУГОЙ раз.

Вот мой урезанный Makefile:

SOURCES:= main.c

app.exe: $(SOURCES:%.c=%.o)
    gcc -o $@ $^

# always recompile when dependency information is missing
$(SOURCES:%.c=%.o): %.o: %.c %.d
    gcc -c $< -MMD

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

version.h: update-version

update-version:
    touch version.h

# include C/C++ header-dependencies
-include $(SOURCES:%.c=%.d)

.PHONY: update-version

# disables builtin suffix-rules
.SUFFIXES:

Может кто-нибудь помочь мне понять, почему это не перекомпилируется каждый раз,но только каждый второй раз?

make - это GNU Make 4.2.1, и remake ведет себя точно так же.

1 Ответ

0 голосов
/ 13 октября 2018

Этот отрывок из make руководства объясняет, почему ваш makefile не работает так, как вы предполагали:

GNU make выполняет свою работу в два разаотдельные фазы.На первом этапе он считывает все make-файлы, включая make-файлы и т. Д. И усваивает все переменные и их значения, неявные и явные правила, а также строит граф зависимостей для всех целей и их предварительных условий.На втором этапе make использует эти внутренние структуры, чтобы определить, какие цели необходимо восстановить, и вызвать необходимые для этого правила.

Поскольку вы выполняете команду (touch, в вашемпример) внутри правила make выполнит его на втором этапе.Но к тому времени граф зависимостей и устаревшие цели уже были определены;это произошло на первом этапе.

Имея это в виду, вы можете понять, почему это работает каждый раз.Ваша желаемая команда действительно выполняется каждый раз, но main.o будет считаться устаревшей только в том случае, если файл version.h был уже новее, чем main.o на первом этапе, то есть перед любым изправила были выполнены.Таким образом, main.o будет считаться устаревшим, только если он старше version.h в тот момент, когда вы вызываете команду make.Это тот случай, если main.o не был перекомпилирован в последний раз, когда вы вызывали make.

Хитрость, которую вы могли бы использовать для выполнения вашей команды во времени, каждый раз, заключалась бы в добавлении фиктивной переменной где-нибудь вначало и заставить его выполнить желаемую команду во время его оценки.Принимая touch снова в качестве примера команды, вы будете делать что-то вроде этого в первой строке:

DUMMY := $(shell touch version.h)

Поскольку это непосредственное назначение, команда будет выполнена на первом этапе.Если эта команда обновит version.h, то все цели, зависящие от version.h, будут считаться устаревшими и перестроенными.Это включает main.o.

. При таком подходе вам больше не нужен механизм .PHONY: update-version и связанные с ним правила и рецепты.


Обновление

Перечитывая, я понимаю, что вы написали: "Всегда запускайте некоторые команды , когда основной целевой файл требует определенный файл".Что ты имеешь в виду?Далее вы написали: «Когда я запускаю make, обновление заголовка версии выполняется каждый раз (как я хочу)» - это то, что делает makefile, полученный из этого ответа.Вы можете уточнить?А что вы подразумеваете под "основной целью"?

...