В чем разница между присваиванием переменных в Makefile GNU =,? =,: = И + =?

766

Кто-нибудь может дать четкое объяснение того, как на самом деле работает назначение переменных в Makefiles.

В чем разница между :

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

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

mmoris
источник

Ответы:

1030

Ленивый Набор

VARIABLE = value

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

Немедленный сет

VARIABLE := value

Установка переменной с простым расширением значений внутри - значения внутри нее раскрываются во время объявления.

Ленивый набор, если отсутствует

VARIABLE ?= value

Установка переменной, только если она не имеет значения. valueвсегда оценивается при VARIABLEобращении. Это эквивалентно

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

Смотрите документацию для более подробной информации.

Append

VARIABLE += value

Присоединение предоставленного значения к существующему значению (или установка этого значения, если переменная не существует)

Альнитак
источник
25
A + = B расширяет B? То есть, если бы я сделал A + = B, а затем B + = C, оценил бы A конкатенацию $ {B} и $ {C}?
Антон Данейко
15
Как сказано в связанном разделе руководства. + = работает в соответствии с любой простой или рекурсивной семантикой, которую имело исходное назначение. Так что да, он расширит RHS, но будет ли он делать это немедленно или с отсрочкой, зависит от типа переменной в LHS.
Этан Рейснер
6
Что вы имеете в виду, когда говорите, что значение переменной расширено?
Сашка Лыхенко
3
@ СашкоЛихенко посмотрите здесь , чтобы получить смысл расширения 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будет расширяться.

strager
источник
3
следствие этого поэтому VARIABLE = literalи VARIABLE := literalвсегда эквивалентны. Я правильно понял?
aiao
1
@aiao, да, поскольку литералы инвариантны к их использованию
Себастьян,
Тонкое различие: -?: Может улучшить производительность в рекурсивных, называемых make-файлах. Например, если $? = $ (shell some_command_that_runs_long_time). В рекурсивных вызовах это будет оцениваться только один раз. вызывая увеличение производительности сборки. : = будет медленнее, так как команда выполняется без необходимости несколько раз
KeshV
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

Проверьте более подробное объяснение здесь

nickand
источник
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 if, VARIABLE которое еще не установлено. Если он еще не установлен, установка значения будет отложена до VARIABLEиспользования (как в примере 1).

VARIABLE += valueпросто добавляет valueк VARIABLE. Фактическое значение valueопределяется так, как оно было, когда оно было первоначально установлено, используя либо =или :=.

mipadi
источник
На самом деле, в вашем первом примере VARIABLE - это $ (VAL), а VAL - это bar. VARIABLE расширяется при использовании.
Страгер
1
Да, комментарии объясняют, что произойдет, когда они будут использованы.
Мипади
Ах; Я полагаю, вы исправили это, или я неправильно прочитал "оценивать" как "быть".
Страгер
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своего значения.

PHS
источник
Это отличный вызов, и у него есть множество примеров для расширения во время объявления, было бы полезно дополнить ответ примером и несколькими словами для расширения во время использования
Роберт Монфера