Определить переменную make во время выполнения правила

В моем GNUmakefile я хотел бы иметь правило, которое использует временный каталог. Например:

out.tar: TMP := $(shell mktemp -d)
        echo hi $(TMP)/hi.txt
        tar -C $(TMP) cf [email protected] .
        rm -rf $(TMP)

Как написано, приведенное выше правило создает временную директорию в то время, когда правило анализируется. Это означает, что даже я не делаю out.tar все время, создаются многие временные каталоги. Я бы хотел, чтобы мой /tmp был завален неиспользуемыми временными каталогами.

Есть ли способ заставить переменную определять только при запуске правила, в отличие от всякий раз, когда он определен?

Моя основная мысль - выгрузить mktemp и tar в оболочку script, но это кажется несколько неприглядным.

Ответ 1

В вашем примере переменная TMP устанавливается (и временная директория), когда оцениваются правила для out.tar. Чтобы создать каталог только при фактическом запуске out.tar, вам необходимо переместить создание каталога в следующие этапы:

out.tar : 
    $(eval TMP := $(shell mktemp -d))
    @echo hi $(TMP)/hi.txt
    tar -C $(TMP) cf [email protected] .
    rm -rf $(TMP)

Функция eval оценивает строку так, как если бы она была введена в make файл вручную. В этом случае он устанавливает переменную TMP в результате вызова функции shell.

изменить (в ответ на комментарии):

Чтобы создать уникальную переменную, вы можете сделать следующее:

out.tar : 
    $(eval [email protected]_TMP := $(shell mktemp -d))
    @echo hi $([email protected]_TMP)/hi.txt
    tar -C $([email protected]_TMP) cf [email protected] .
    rm -rf $([email protected]_TMP)

Это добавило бы имя цели (out.tar, в данном случае) к переменной, создав переменную с именем out.tar_TMP. Надеюсь, этого достаточно, чтобы предотвратить конфликты.

Ответ 2

Относительно простой способ сделать это - написать всю последовательность в виде оболочки script.

out.tar:
   set -e ;\
   TMP=$$(mktemp -d) ;\
   echo hi $$TMP/hi.txt ;\
   tar -C $$TMP cf [email protected] . ;\
   rm -rf $$TMP ;\

Я собрал несколько связанных советов здесь: fooobar.com/questions/41289/...

Ответ 3

Другая возможность заключается в использовании отдельных строк для настройки переменных Make при возникновении правила.

Например, вот make файл с двумя правилами. Если правило срабатывает, оно создает временный каталог и устанавливает TMP для имени temp dir.

PHONY = ruleA ruleB display

all: ruleA

ruleA: TMP = $(shell mktemp -d testruleA_XXXX)
ruleA: display

ruleB: TMP = $(shell mktemp -d testruleB_XXXX)
ruleB: display

display:
    echo ${TMP}

Запуск кода дает ожидаемый результат:

$ ls
Makefile
$ make ruleB
echo testruleB_Y4Ow
testruleB_Y4Ow
$ ls
Makefile  testruleB_Y4Ow

Ответ 4

Мне не нравятся ответы "Не", но... нет.

Переменные make являются глобальными и должны оцениваться на этапе "разбора" make файла, а не на этапе выполнения.

В этом случае, если переменная локальна для одной цели, следуйте @nobar answer и сделайте ее переменной оболочки.

Целевые переменные также считаются вредоносными в других реализациях make: kati, Mozilla pymake. Из-за них цель может быть построена по-разному в зависимости от того, была ли она построена автономно, или как зависимость родительской цели с переменной, специфичной для цели. И ты не узнаешь, как это было, потому что не знаешь, что уже построено.