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

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

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

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

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

Моя основная мысль - сбросить mktemp и tar в сценарий оболочки, но это кажется несколько некрасивым.


person Emil Sit    schedule 15.12.2009    source источник


Ответы (4)


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

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

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

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

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

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

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

person e.James    schedule 15.12.2009
comment
Прикольные уточнения ... это не повлияет на TMP на эту цель, не так ли? Итак, если есть другие правила, которые имеют собственное использование $ (TMP) (возможно, параллельно с -j), могут быть конфликты? Кроме того, необходимо ли вообще @echo? Кажется, ты мог бы просто оставить это без внимания. - person Emil Sit; 15.12.2009
comment
Я не думал, что вы сможете сделать это без @echo, но я проверил его, и он работает. Хороший улов! Я изменю это в своем ответе. - person e.James; 15.12.2009
comment
Что касается области видимости, переменная TMP не будет специфичной для этого конкретного правила. Он будет существовать в глобальном пространстве имен и может конфликтовать с другими переменными с таким же именем. Это желаемое поведение? Вероятно, есть способы обойти это, если нужно. - person e.James; 15.12.2009
comment
Было бы полезно; это часть более крупного проекта, в котором мы определенно хотим избежать конфликтов и использовать параллельные сборки. Возможно, вы можете объявить целевую переменную, которую вы затем записываете внутри правила ... хотя это, похоже, не работает для меня. Хм. - person Emil Sit; 16.12.2009
comment
В своем ответе я опубликовал возможное решение. Надеюсь, это поможет. Удачи! - person e.James; 16.12.2009
comment
Кажется, это помогает (хотя и немного непонятно для среднего гуру, не занимающегося Make :-) Спасибо! - person Emil Sit; 16.12.2009
comment
Будьте осторожны с этим раствором! $(eval $@_TMP := $(shell mktemp -d)) произойдет, когда Makefile сначала оценивается не в порядке процедур правил. Другими словами, $(eval ...) происходит раньше, чем вы думаете. Хотя для этого примера это может быть нормально, но этот метод вызовет проблемы для некоторых последовательных операций. - person JamesThomasMoon; 11.06.2015
comment
две (не по теме) детали: я полагаю, что знак > отсутствует в строке эхо-сигнала @echo hi > $(TMP)/hi.txt, или есть система, в которой этот знак не нужен ?. И моя команда tar не принимала cf параметры без начального тире -cf, но я видел версии, которые это принимают. - person boclodoa; 24.07.2015
comment
@ JamesThomasMoon1979 знаете ли вы, как преодолеть эти ограничения, например eval некоторые переменные, которые должны быть результатом шагов, выполненных ранее в правиле? - person Vadim Kotov; 05.06.2017
comment
Отвечая на мой собственный вопрос: обходным путем для меня было создание файлов во время выполнения 1-го правила и попытка их оценки на первом этапе 2-го правила. - person Vadim Kotov; 05.06.2017
comment
Зачем использовать := вместо = для присвоения переменных внутри рецепта? - person CMCDragonkai; 03.07.2018
comment
@ JamesThomasMoon1979, если вы запустите make в этом Makefile, похоже, что он не будет выполняться при первой оценке Makefile. Другими словами, папка создается не с make, а только с make TEST, как ожидалось. gist.github.com/jsign/c7313f359daa99c02bafb222becacad3 - person Ignacio Hagopian; 29.07.2019

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

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

Здесь я собрал несколько советов по теме: https://stackoverflow.com/a/29085684/86967

person Brent Bradburn    schedule 16.03.2015
comment
Это определенно самый простой и, следовательно, лучший ответ (избегая @ и eval и выполняя ту же работу). Обратите внимание, что в вашем выводе вы видите $TMP (например, tar -C $TMP ...), хотя значение корректно передано команде. - person Karl Richter; 08.05.2015
comment
Вот как это обычно делается; Я надеюсь, что люди увидят этот ответ, потому что на практике никто не преодолевает все усилия принятого. - person smheidrich; 08.03.2017
comment
Что делать, если вы используете команды, специфичные для оболочки? В этом случае команды оболочки должны быть на том же языке оболочки, что и команда make, верно? Я видел некоторые make-файлы с помощью shebang. - person ptitpion; 25.04.2018
comment
@ptitpion: вам также может понадобиться SHELL := /bin/bash в вашем файле makefile, чтобы включить функции, специфичные для BASH. - person Brent Bradburn; 25.04.2018

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

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

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
person user3124434    schedule 21.12.2013
comment
Напоминаем, что в GNU make есть синтаксис для только заказа предварительные условия, которые могут потребоваться для дополнения этого подхода. - person anol; 05.06.2014
comment
Будьте осторожны с этим раствором! ruleA: TMP = $(shell mktemp -d testruleA_XXXX) и ruleB: TMP = $(shell mktemp -d testruleB_XXXX) произойдут при первой оценке Makefile. Другими словами, ruleA: TMP = $(shell ... происходит раньше, чем вы думаете. Хотя это может сработать в данном конкретном случае, этот метод вызовет проблемы для некоторых последовательных операций. - person JamesThomasMoon; 11.06.2015
comment
Это не мой опыт - локальные переменные правила расширяются только тогда, когда запрашивается конкретное правило (явно или как зависимость) - person Zaar Hai; 23.10.2020
comment
Это все равно должно работать, если вы сделаете ruleA: private TMP = .... См. целевые переменные. - person Victor Sergienko; 22.01.2021

Мне не нравятся ответы «Не надо», но ... нет.

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

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

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

person Victor Sergienko    schedule 11.10.2019
comment
локальные переменные были бы полезны, даже если они действительно локальные, то есть они не видны вниз для правил, которые выполняются под нами. - person Attila Lendvai; 06.08.2020
comment
@AttilaLendvai на момент написания ответа я не знал вы можете сделать целевую переменную частной. Это, безусловно, делает их более безопасными, но я бы все равно не одобрял их использование. - person Victor Sergienko; 22.01.2021