Необъяснимое поведение в GNU
0 hgiesel [2016-01-06 13:56:00]
У меня есть следующий файл Makefile:
SHELLS = $(call adjust, profile bash zsh)
adjust = $(foreach arg, $(1), $(DIR)/$(DOT)$(arg))
sys:
$(eval DIR = /etc)
$(eval DOT = )
usr:
$(eval DIR = $(wildcard ~))
$(eval DOT = .)
# Installation Recipes {{{1
shell-sys: $(SHELLS) | sys
@echo $(SHELLS)
shell-usr: $(SHELLS) | usr
@echo $(SHELLS)
$(SHELLS): $(DIR)/$(DOT)%: $(wildcard %/*)
@echo $(SHELLS)
Теперь я запускаю make shell-sys и ожидаю следующего результата:
$ make shell-sys
/etc/profile /etc/bash /etc/zsh
/etc/profile /etc/bash /etc/zsh
/etc/profile /etc/bash /etc/zsh
/etc/profile /etc/bash /etc/zsh
Однако я получаю следующее:
$ make shell-sys
/profile /bash /zsh
/profile /bash /zsh
/profile /bash /zsh
/etc/profile /etc/bash /etc/zsh
Внутри правил статического шаблона $(DIR) и $(DOT) не могут расширяться, однако в обычных правилах они, похоже, просто расширяются. Что я делаю не так? Есть ли лучший способ достичь того, что делают цели sys и usr?
bash makefile gnu-make macos
1 ответ
1 Решение bobbogo [2016-01-06 18:55:00]
Перед запуском любых рецептов (команд оболочки) make будет читать весь Makefile. Пока он делает это, он расширяет линии зависимостей. Сами рецепты просто хранятся в виде рекурсивно расширенных переменных. В конце этого этапа make имеет граф зависимостей.
В вашем случае make расширяет
shell-sys: $(SHELLS) | sys
$(SHELLS) становится /profile/bash/zsh учитывая пустоту $DIR и $DOT. Вы можете получить некоторый намек на то, чтобы --warn эти пустые переменные, предоставив параметр командной строки --warn. Таким образом, как будто вы написали:
shell-sys: /profile /bash /zsh | sys
@echo $(SHELLS)
/profile /bash /zsh: /%:
@echo $(SHELLS)
(Я сомневаюсь, что у вас есть папка на вашем диске с именем % поэтому я предполагаю, что $(wildcard...) расширяется до нуля.)
Как только у него есть график зависимости в его маленьких руках, make может ходить по нему, выполняя рецепты по мере их появления. Вы попросили его создать shell-sys, поэтому сначала он пытается создать /profile. [Вероятно. Makefiles, которые полагаются на этот неявный порядок зависимостей, нарушены IMHO.]
Сделайте сейчас сборку /profile. Он знает, как это сделать, вы сказали это раньше. Он расширяет рецепт. @echo $(SHELLS) становится @echo/profile/bash/zsh. Сделать pass echo/profile/bash/zsh оболочкой, не сообщая вам, что это произошло из-за префикса @. echo dutifully prints /profile/bash/zsh.
Аналогично для целей /bash и /zsh.
Затем мы построим sys. Make расширяет рецепт для sys. Каждая отдельная строка в расширении передается новому вызову оболочки. Расширение, однако, пусто, поэтому оболочка не называется. В качестве побочного эффекта DIR становится /etc
Наконец, рецепт для shell-sys. Надеюсь, теперь вы можете увидеть, что это расширяется до @echo/etc/profile/etc/bash/etc/zsh
В вашей формулировке много не нравится, но самый большой совет, который я могу дать, заключается не в том, чтобы увеличить $(eval...) на время выполнения рецепта.