Qual é a diferença entre as atribuições de variável GNU Makefile =,? =,: = E + =?

765

Alguém pode dar uma explicação clara de como a atribuição de variáveis ​​realmente funciona nos Makefiles.

Qual é a diferença entre :

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

Eu li a seção no manual do GNU Make, mas ainda não faz sentido para mim.

mmoris
fonte

Respostas:

1029

Conjunto preguiçoso

VARIABLE = value

Configuração normal de uma variável, mas quaisquer outras variáveis ​​mencionadas com o valuecampo são recursivamente expandidas com seu valor no ponto em que a variável é usada, não a que tinha quando foi declarada

Conjunto imediato

VARIABLE := value

Configuração de uma variável com expansão simples dos valores internos - os valores dentro dela são expandidos no momento da declaração.

Conjunto preguiçoso se ausente

VARIABLE ?= value

Configuração de uma variável apenas se ela não tiver um valor. valueé sempre avaliado quando VARIABLEé acessado. É equivalente a

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

Veja a documentação para mais detalhes.

Acrescentar

VARIABLE += value

Anexando o valor fornecido ao valor existente (ou definindo esse valor se a variável não existir)

Alnitak
fonte
25
A + = B expande B? Isto é, se eu fizer A + = B e, em seguida, B + = C, A avaliaria a concatenação de $ {B} e $ {C}?
Anton Daneyko
15
Como a seção vinculada do manual diz. + = opera de acordo com a semântica simples ou recursiva que a atribuição original possuía. Portanto, sim, ele expandirá o RHS, mas se o fizer imediatamente ou de maneira diferida depende do tipo de variável no LHS.
Etan Reisner 03/03
6
O que você quer dizer quando diz que o valor da variável é expandido?
Sashko Lykhenko
3
@ СашкоЛихенко dê uma olhada aqui para obter significado da expansão gnu.org/software/make/manual/make.html#Flavors
Umair R
7
é "definido se ausente" preguiçoso ou imediato? posso "conjunto lento se ausente" e "conjunto imediato se ausente"?
Woodrow Barlow
268

O uso =faz com que seja atribuído um valor à variável. Se a variável já tiver um valor, ela será substituída. Este valor será expandido quando usado. Por exemplo:

HELLO = world
HELLO_WORLD = $(HELLO) world!

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

HELLO = hello

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

Usar :=é semelhante a usar =. No entanto, em vez de o valor ser expandido quando usado, ele é expandido durante a atribuição. Por exemplo:

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)

O uso ?=atribui à variável um valor se a variável não foi atribuída anteriormente. Se a variável tiver sido atribuída anteriormente a um valor em branco ( VAR=), ainda será considerado definido, eu acho . Caso contrário, funciona exatamente como =.

Usar +=é como usar =, mas, em vez de substituir o valor, ele é anexado ao atual, com um espaço no meio. Se a variável foi definida anteriormente :=, é expandida, eu acho . O valor resultante é expandido quando usado, eu acho . Por exemplo:

HELLO_WORLD = hello
HELLO_WORLD += world!

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

Se algo como HELLO_WORLD = $(HELLO_WORLD) world!fosse usado, resultaria em recursão, o que provavelmente terminaria a execução do seu Makefile. Se A := $(A) $(B)fosse usado, o resultado não seria exatamente o mesmo que usar, +=porque Bé expandido com :=enquanto +=não causaria Bexpansão.

strager
fonte
3
uma conseqüência disso é, portanto, VARIABLE = literale VARIABLE := literalsempre é equivalente. Eu entendi direito?
aiao
1
@aiao, sim como literais são invariáveis com seus usos
Sebastian
Uma diferença sutil é: -?: Pode melhorar o desempenho em makefiles chamados recursivos. Por exemplo, se $? = $ (shell some_command_that_runs_long_time). Nas chamadas recursivas, isso será avaliado apenas uma vez. causando ganhos no desempenho da compilação. : = Será mais lento, uma vez que o comando é desnecessariamente executar várias vezes
KeshV
61

Eu sugiro que você faça alguns experimentos usando "make". Aqui está uma demonstração simples, mostrando a diferença entre =e :=.

/* 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 impressões:

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

Verifique aqui uma explicação mais elaborada

nickand
fonte
5
Seria melhor usar um @na frente de cada receita para evitar essa repetição confusa de resultados.
Alexandro de Oliveira
2
Marca não suporta /* ... */bloco de comentário
yoonghm
31

Quando você usa VARIABLE = value, se valueé realmente uma referência a outra variável, o valor é determinado apenas quando VARIABLEé usado. Isso é melhor ilustrado com um exemplo:

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

# VARIABLE and VAL will both evaluate to "bar"

Quando você usa VARIABLE := value, obtém o valor value como é agora . Por exemplo:

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

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

Usar VARIABLE ?= valsignifica que você define apenas o valor de VARIABLE se VARIABLE ainda não estiver definido. Se ainda não estiver definido, a configuração do valor será adiada até que VARIABLEseja usada (como no exemplo 1).

VARIABLE += valueapenas anexa valuea VARIABLE. O valor real de valueé determinado como era quando foi definido inicialmente, usando =ou :=.

mipadi
fonte
Na verdade, no seu primeiro exemplo, VARIABLE é $ (VAL) e VAL é bar. VARIÁVEL expandido quando usado.
Strager 15/01/09
1
Sim, os comentários estão explicando o que aconteceria quando fossem usados.
Mipadi
Ah; Eu acho que você o corrigiu, ou eu interpretei errado "avaliar" como "ser".
Strager
7

Nas respostas acima, é importante entender o que se entende por "valores expandidos no momento da declaração / uso". Dar um valor semelhante *.cnão implica nenhuma expansão. Somente quando essa string é usada por um comando é que talvez acione alguns globbing. Da mesma forma, um valor como $(wildcard *.c)ou $(shell ls *.c)não implica em nenhuma expansão e é completamente avaliado no momento da definição, mesmo que tenhamos usado :=na definição da variável.

Experimente o seguinte Makefile no diretório em que você tem alguns arquivos 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

A execução makeacionará uma regra que cria um arquivo C extra (vazio), chamado, foo.cmas nenhuma das 6 variáveis ​​possui foo.cseu valor.

phs
fonte
Esta é uma grande chamada e tem uma abundância de exemplos para a expansão em tempo de declaração, que seria útil alargar a resposta com um exemplo e algumas palavras para a expansão a tempo de uso
Robert Monfera