Яка різниця між призначеннями змінної GNU Makefile =,? =,: = І + =?


764

Хто-небудь може дати чітке пояснення того, як призначення змінної насправді працює в Makefiles.

Яка різниця між :

 VARIABLE = value
 VARIABLE ?= value
 VARIABLE := value
 VARIABLE += value

Я прочитав розділ у посібнику GNU Make, але він все ще не має сенсу для мене.

Відповіді:


1028

Ледачий набір

VARIABLE = value

Нормальне встановлення змінної, але будь-які інші змінні, згадані з valueполем, рекурсивно розширюються їх значенням у точці, в якій використовується змінна, а не тій, яку вона мала, коли вона була оголошена

Негайний набір

VARIABLE := value

Встановлення змінної з простим розширенням значень всередині - значення всередині неї розширюються під час оголошення.

Ледачий набір, якщо він відсутній

VARIABLE ?= value

Встановлення змінної, лише якщо вона не має значення. valueзавжди оцінюється при VARIABLEзверненні. Це еквівалентно

ifeq ($(origin FOO), undefined)
  FOO = bar
endif

Детальнішу інформацію див. У документації .

Додавати

VARIABLE += value

Додавання доданого значення до існуючого значення (або встановлення цього значення, якщо змінна не існувала)


25
Чи A + = B розширює B? Тобто, якби я зробив A + = B, а потім B + = C, чи оцінив би A об'єднання $ {B} і $ {C}?
Антон Данейко

15
Як сказано в пов'язаному розділі посібника. + = функціонує відповідно до будь-якої простої або рекурсивної семантики оригінального завдання. Так, так, це розширить RHS, але чи буде це робити негайно чи відкладено, залежить від типу змінної на LHS.
Ітан Рейснер

6
Що ти маєш на увазі, коли ти кажеш, що змінне значення розширюється?
Сашко Лихченко

2
@ СашкоЛихенко подивіться тут, щоб отримати сенс розширення gnu.org/software/make/manual/make.html#Flavors
Umair R

7
"встановлено, якщо немає" лінивим чи негайним? Чи можу я "лінивий встановити, якщо немає" та "негайно встановити, якщо відсутній"?
Вудро Барлоу

268

Використання =призводить до присвоєння змінній значення. Якщо змінна вже мала значення, її замінюють. Це значення буде розширено при використанні. Наприклад:

HELLO = world
HELLO_WORLD = $(HELLO) world!

# This echoes "world world!"
echo $(HELLO_WORLD)

HELLO = hello

# This echoes "hello world!"
echo $(HELLO_WORLD)

Використання :=подібне до використання =. Однак замість значення, яке розширюється при його використанні, воно розширюється під час призначення. Наприклад:

HELLO = world
HELLO_WORLD := $(HELLO) world!

# This echoes "world world!"
echo $(HELLO_WORLD)

HELLO = hello

# Still echoes "world world!"
echo $(HELLO_WORLD)

HELLO_WORLD := $(HELLO) world!

# This echoes "hello world!"
echo $(HELLO_WORLD)

Використання ?=призначає змінної значення, якщо змінна раніше не була призначена. Якщо для змінної раніше було присвоєно порожнє значення ( VAR=), я вважаю , що це встановлено як я вважаю . Інакше функції точно так само =.

Використовувати +=подібне до використання =, але замість заміни значення значення додається до поточного з проміжком між ними. Якщо змінна була встановлена ​​раніше :=, я думаю , вона буде розширена . Отримане значення розширюється, коли воно використовується, я думаю . Наприклад:

HELLO_WORLD = hello
HELLO_WORLD += world!

# This echoes "hello world!"
echo $(HELLO_WORLD)

Якщо б щось подібне HELLO_WORLD = $(HELLO_WORLD) world!було використано, виникла б рекурсія, яка, швидше за все, закінчила б виконання вашого Makefile. Якби вони A := $(A) $(B)були використані, результат не був би таким самим, як використання, +=оскільки Bвін розширений, :=тоді як +=не призведе Bдо розширення.


3
Отже, наслідок цього є VARIABLE = literalі VARIABLE := literalзавжди рівнозначний. Я правильно зрозумів?
аяо

1
@aiao, так, тому що літерали інваріантно застосовують їх
Себастьян

Тонка різниця полягає в: -?: Може підвищити продуктивність у рекурсивних, званих makefiles. Наприклад, якщо $? = $ (оболонка some_command_that_runs_long_time). У рекурсивних дзвінках це буде оцінено лише один раз. спричиняючи приріст у нарощуванні ефективності : = буде повільніше, оскільки команда
зайво

61

Я пропоную вам зробити кілька експериментів, використовуючи "make". Ось проста демонстрація, яка показує різницю між =і :=.

/* Filename: Makefile*/
x := foo
y := $(x) bar
x := later

a = foo
b = $(a) bar
a = later

test:
    @echo x - $(x)
    @echo y - $(y)
    @echo a - $(a)
    @echo b - $(b)

make test відбитки:

x - later
y - foo bar
a - later
b - later bar

Перевірте більш детальне пояснення тут


5
Було б краще використовувати @попереду кожен рецепт, щоб уникнути цього заплутаного повторення результатів.
Олександро де Олівейра

2
Make не підтримує /* ... */коментар блоку
yoonghm

31

Якщо ви використовуєте VARIABLE = value, якщо valueнасправді є посилання на іншу змінну, то значення визначається лише тоді, коли VARIABLEвикористовується. Це найкраще проілюструвати на прикладі:

VAL = foo
VARIABLE = $(VAL)
VAL = bar

# VARIABLE and VAL will both evaluate to "bar"

Під час використання VARIABLE := valueви отримуєте значення такого, value як є зараз . Наприклад:

VAL = foo
VARIABLE := $(VAL)
VAL = bar

# VAL will evaluate to "bar", but VARIABLE will evaluate to "foo"

Використання VARIABLE ?= valозначає, що ви встановлюєте лише значення, VARIABLE якщо VARIABLE воно вже не встановлено. Якщо його вже не встановлено, значення значення відкладається до тих пір, поки не VARIABLEбуде використано (як у прикладі 1).

VARIABLE += valueпросто приєднує valueдо VARIABLE. Дійсне значення valueвизначається таким, яким воно було, коли воно було встановлено спочатку, використовуючи =або :=.


Насправді у вашому першому прикладі VARIABLE - $ (VAL), а VAL - бар. VARIABLE розширюється при його використанні.
страгер

1
Так, коментарі пояснюють, що буде, коли вони будуть використані.
mipadi

Ах; Я думаю, ви це виправили, або я неправильно прочитав "оцінку" як "бути".
страгер

7

У наведених вище відповідях важливо зрозуміти, що означає "значення розширюються під час декларації / використання". Надання такого значення, як *.cі раніше, не тягне за собою будь-якого розширення. Лише тоді, коли ця рядок буде використана командою, вона, можливо, спровокує деяке глобулювання. Аналогічно, значення, яке подобається $(wildcard *.c)або $(shell ls *.c)не спричиняє будь-якого розширення, і повністю оцінюється під час визначення, навіть якщо ми використовуємо :=у визначенні змінної.

Спробуйте наступний Makefile у каталозі, де у вас є декілька файлів C:

VAR1 = *.c
VAR2 := *.c
VAR3 = $(wildcard *.c)
VAR4 := $(wildcard *.c)
VAR5 = $(shell ls *.c)
VAR6 := $(shell ls *.c)

all :
    touch foo.c
    @echo "now VAR1 = \"$(VAR1)\"" ; ls $(VAR1)
    @echo "now VAR2 = \"$(VAR2)\"" ; ls $(VAR2)
    @echo "now VAR3 = \"$(VAR3)\"" ; ls $(VAR3)
    @echo "now VAR4 = \"$(VAR4)\"" ; ls $(VAR4)
    @echo "now VAR5 = \"$(VAR5)\"" ; ls $(VAR5)
    @echo "now VAR6 = \"$(VAR6)\"" ; ls $(VAR6)
    rm -v foo.c

Запуск makeвикликає правило, яке створює додатковий (порожній) файл C, який називається, foo.cале жодна з 6 змінних не має foo.cсвого значення.


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