Зависимости 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 обновляет временную метку последнего изменения до текущего времени.