Необъяснимое поведение в 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...) на время выполнения рецепта.