Визначте make змінну під час виконання правила


209

У своєму GNUmakefile я хотів би мати правило, яке використовує тимчасовий каталог. Наприклад:

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

Як написано, вищевказане правило створює тимчасовий каталог під час розбору правила . Це означає, що навіть я не роблю out.tar весь час, створюється багато тимчасових каталогів. Я хотів би уникати того, щоб мій / tmp був засмічений невикористаними тимчасовими каталогами.

Чи є спосіб зробити так, щоб змінна була визначена лише при запуску правила, на відміну від того, коли вона визначається?

Моя головна думка - скинути mktemp та tar в сценарій оболонки, але це здається дещо непривабливим.

Відповіді:


322

У вашому прикладі, 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 оцінює рядок так, ніби вона була введена в makefile вручну. У цьому випадку він встановлює 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. Сподіваємось, цього достатньо для запобігання конфліктів.


2
Охолодіть кілька роз'яснень ... це не охопить TMP до цієї мети, чи не так? Отже, якщо існують інші правила, які мають власне використання $ (TMP) (можливо, паралельно з -j), можуть виникнути конфлікти? Також, чи потрібен @echo? Здається, ви могли просто залишити це.
Еміль Сит

3
Здається, зробити трюк (хоча трохи непрозорий для середнього гуру, який не робить :-) Дякую!
Еміль Сит

29
Будьте обережні з цим рішенням! $(eval $@_TMP := $(shell mktemp -d))станеться, коли Makefile спочатку оцінюється не в порядку процедур з правилами. Іншими словами, $(eval ...)це відбувається швидше, ніж ви думаєте. Хоча це може бути нормально для цього прикладу, цей метод спричинить проблеми для деяких послідовних операцій.
JamesThomasMoon1979

1
@ JamesThomasMoon1979 чи знаєте ви, як подолати ці обмеження, наприклад, оцінюйте деякі змінні, які повинні бути результатом кроків, виконаних раніше у правилі?
Вадим Котов

2
Відповідаючи на власне запитання: для мене вирішенням було створення файлів під час виконання 1-го правила та намагання їх оцінити на першому кроці 2-го правила.
Вадим Котов

63

Порівняно простий спосіб зробити це - записати всю послідовність у вигляді сценарію оболонки.

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


3
Це, безумовно , самий простий і тому найкращою відповіддю (уникаючи @і evalі робити ту ж роботу). Зауважте, що у вашому виході ви бачите $TMP(наприклад tar -C $TMP ...), хоча значення правильно передається команді.
Карл Ріхтер

Ось як це зазвичай робиться; Я сподіваюся, що люди бачать цю відповідь, оскільки на практиці ніхто не переживає всіх зусиль прийнятого.
пмос

Що робити, якщо ви використовуєте команди для оболонки? У цьому випадку команди оболонки повинні бути тією ж мовою оболонки, що і та, що викликає, чи не так? Я бачив якийсь makefile з shebang.
ptitpion

@ptitpion: Ви також можете SHELL := /bin/bashу своєму файлі make ввімкнути функції, характерні для BASH.
nobar

31

Інша можливість полягає у використанні окремих рядків для настройки Зробити змінні, коли правило спрацьовує.

Наприклад, ось макіяж з двома правилами. Якщо правило спрацьовує, воно створює темп-dir і встановлює 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

Як нагадування, GNU make має синтаксис для передумов лише для замовлення, які можуть знадобитися для доповнення цього підходу.
анол

11
Будьте обережні з цим рішенням! ruleA: TMP = $(shell mktemp -d testruleA_XXXX)і ruleB: TMP = $(shell mktemp -d testruleB_XXXX)станеться, коли Makefile буде вперше оцінено. Іншими словами, ruleA: TMP = $(shell ...це відбувається швидше, ніж ви думаєте. Хоча це може працювати в даному конкретному випадку, цей метод спричинить проблеми для деяких послідовних операцій.
JamesThomasMoon1979

1

Мені не подобаються відповіді "Не", але ... ні.

makeЗмінні змінні є глобальними і повинні бути оцінені під час етапу "розбору" makefile, а не під час виконання.

У цьому випадку до тих пір, поки змінна локальна для однієї цілі, слідкуйте за відповіддю @ nobar і зробіть її змінною оболонки.

Змінні, пов’язані з ціллю, також вважаються шкідливими в інших реалізаціях: kati , Mozilla pymake . Через них ціль може бути побудована по-різному, залежно від того, чи вона побудована окремо, або як залежність від батьківської цілі зі змінною, орієнтованою на ціль. І ви не знатимете, як це було , бо не знаєте, що вже побудовано.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.