Зависимости Makefile не работают для фальшивой цели
Вот приведенная версия моего Makefile:
.PHONY: all
all: src/server.coffee
mkdir -p bin
./node_modules/.bin/coffee -c -o bin src/server.coffee
Я хочу запустить make
и пересобирать его только тогда, когда src/server.coffee
изменился. Тем не менее, он перекомпилирует каждый раз, когда я запускаю make
:
$ make
mkdir -p bin
./node_modules/.bin/coffee -c -o bin src/server.coffee
$ make
mkdir -p bin
./node_modules/.bin/coffee -c -o bin src/server.coffee
Если я изменю свой Makefile, чтобы не использовать фальшивую цель, она работает так, как ожидалось. Новый файл Makefile:
bin/server.js: src/server.coffee
mkdir -p bin
./node_modules/.bin/coffee -c -o bin src/server.coffee
Результат:
$ make
mkdir -p bin
./node_modules/.bin/coffee -c -o bin src/server.coffee
$ make
make: `bin/server.js' is up to date.
Почему он не будет уважать мои зависимости с фальшивой целью? Причина, о которой я прошу, заключается в том, что на самом деле я не буду просто компилировать один файл в один другой файл, поэтому я не хочу отслеживать имена всех выходных файлов, которые будут использоваться в качестве целей.
Ответы
Ответ 1
Вместо фальшивой цели (которая, как указывает @cmotley, работает точно так, как она должна), что вы можете использовать, когда хотите избежать дополнительной работы, это "пустая цель" :
Пустая цель - это вариант фальшивой цели; он используется для хранения рецептов для действия, которое вы запрашиваете явным образом время от времени. В отличие от фальшивой цели, этот целевой файл действительно может существовать; но содержимое файла не имеет значения и обычно пусто.
Цель пустого целевого файла - записать с его временем последней модификации, когда последний рецепт правил был выполнен. Он делает это, потому что одна из команд в рецепте - это сенсорная команда для обновления целевого файла.
Однако, в этом случае нет необходимости добавлять дополнительный пустой выходной файл - у вас уже есть выход из вашей компиляции CoffeeScript! Это соответствует более типичному шаблону Makefile, как вы уже продемонстрировали в своем вопросе. Что вы можете сделать, так это рефакторинг этого подхода:
.PHONY: all
all: bin/server.js
bin/server.js: src/server.coffee
mkdir -p bin
./node_modules/.bin/coffee -c -o bin src/server.coffee
Теперь у вас есть две вещи: хорошая обычная "все" цель, которая правильно фальшивая, но не будет делать дополнительной работы. Вы также можете лучше сделать это более общим, чтобы вы могли легко добавить больше файлов:
.PHONY: all
all: bin/server.js bin/other1.js bin/other2.js
bin/%.js: src/%.coffee
mkdir -p bin
./node_modules/.bin/coffee -c -o bin $<
Ответ 2
В соответствии с документацией Make:
The prerequisites of the special target .PHONY are considered
to be phony targets. When it is time to consider such a target,
make will run its recipe unconditionally, regardless of whether
a file with that name exists or what its last-modification time is.
http://www.gnu.org/software/make/manual/html_node/Special-Targets.html
Make запускает рецепт целей PHONY безоговорочно - предпосылки не имеют значения.
Ответ 3
Для сравнения с временем модификации файла server.coffee должен быть выбран целевой файл. Поскольку у вас нет конкретной цели make
, не может знать, является ли результат более новым, чем зависимость или нет, поэтому он всегда будет строить all
.
Ответ 4
Как уже упоминалось, сделайте просмотр временных меток файлов, чтобы выяснить, изменились ли зависимости.
Если вы хотите "подражать" фальшивую цель с зависимостями, вам нужно будет создать реальный файл с этим именем и использовать команду touch
(в Unix-системах).
Мне понадобилось решение только очистить каталог, если были изменены make файлы (т.е. были изменены флаги компилятора, поэтому необходимо было перекомпилировать объектные файлы).
Вот то, что я использовал (и запускается перед каждой компиляцией) с файлом с именем makefile_clean
:
makefile_clean: makefile
@rm '*.o'
@sudo touch makefile_clean
Команда touch
обновляет временную метку последнего изменения до текущего времени.