¿Cuál es la diferencia entre las asignaciones de variables GNU Makefile =,? =,: = Y + =?

765

¿Alguien puede dar una explicación clara de cómo funciona realmente la asignación de variables en Makefiles?

Cuál es la diferencia entre :

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

He leído la sección en el manual de GNU Make, pero todavía no tiene sentido para mí.

mmoris
fuente

Respuestas:

1029

Set perezoso

VARIABLE = value

Configuración normal de una variable, pero cualquier otra variable mencionada con el valuecampo se expande recursivamente con su valor en el punto en el que se usa la variable, no la que tenía cuando se declaró

Conjunto inmediato

VARIABLE := value

Configuración de una variable con expansión simple de los valores internos: los valores internos se expanden en el momento de la declaración.

Conjunto perezoso si está ausente

VARIABLE ?= value

Configuración de una variable solo si no tiene un valor. valuesiempre se evalúa cuando VARIABLEse accede. Es equivalente a

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

Consulte la documentación para más detalles.

Adjuntar

VARIABLE += value

Agregar el valor suministrado al valor existente (o establecer ese valor si la variable no existiera)

Alnitak
fuente
25
¿A + = B expande B? Es decir, si hago A + = B, y luego B + = C, ¿A evaluaría la concatenación de $ {B} y $ {C}?
Anton Daneyko
15
Como dice la sección vinculada del manual. + = opera de acuerdo con la semántica simple o recursiva que tenía la asignación original. Entonces sí, expandirá el RHS, pero si lo hace de manera inmediata o diferida depende del tipo de variable en el LHS.
Etan Reisner
66
¿Qué quieres decir cuando dices que el valor variable se expande?
Sashko Lykhenko
3
@ СашкоЛихенко eche un vistazo aquí para entender el significado de la expansión gnu.org/software/make/manual/make.html#Flavors
Umair R
77
¿"establecer si está ausente" es flojo o inmediato? ¿Puedo "conjunto diferido si está ausente" y "conjunto inmediato si abset"?
Woodrow Barlow
268

El uso =hace que a la variable se le asigne un valor. Si la variable ya tenía un valor, se reemplaza. Este valor se expandirá cuando se use. Por ejemplo:

HELLO = world
HELLO_WORLD = $(HELLO) world!

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

HELLO = hello

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

Usar :=es similar a usar =. Sin embargo, en lugar de que el valor se expanda cuando se usa, se expande durante la asignación. Por ejemplo:

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)

El uso ?=asigna a la variable un valor si la variable no se asignó previamente. Si a la variable se le asignó previamente un valor en blanco ( VAR=), todavía se considera un conjunto , creo . De lo contrario, funciona exactamente como =.

Usar +=es como usar =, pero en lugar de reemplazar el valor, el valor se agrega al actual, con un espacio en el medio. Si la variable se estableció previamente con :=, creo que se expande . El valor resultante se expande cuando se usa , creo . Por ejemplo:

HELLO_WORLD = hello
HELLO_WORLD += world!

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

Si HELLO_WORLD = $(HELLO_WORLD) world!se usara algo así , se produciría una recursión, que probablemente terminaría con la ejecución de su Makefile. Si A := $(A) $(B)se usaran, el resultado no sería exactamente el mismo que usar +=porque Bse expande con, :=mientras +=que no provocaría Bque se expanda.

extraño
fuente
3
una consecuencia de eso es VARIABLE = literaly VARIABLE := literales siempre equivalente. ¿Lo entendí bien?
aiao
1
@aiao, sí como literales son invariantes a sus usos
Sebastián
Una sutil diferencia es: -?: Puede mejorar el rendimiento en recursivos llamados makefiles. Por ejemplo, si $? = $ (shell some_command_that_runs_long_time). En llamadas recursivas, esto se evaluará solo una vez. causando ganancias en el rendimiento de construcción. : = Será más lento ya que el comando se ejecuta varias veces innecesariamente
KeshV
61

Te sugiero que hagas algunos experimentos usando "make". Aquí hay una demostración simple, que muestra la diferencia entre =y :=.

/* 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 huellas dactilares:

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

Consulte la explicación más elaborada aquí

nickand
fuente
55
Sería mejor usar un @frente en cada receta para evitar esta repetición confusa de resultados.
Alexandro de Oliveira
2
Make no admite /* ... */comentarios de bloque
yoonghm
31

Cuando usa VARIABLE = value, si en valuerealidad es una referencia a otra variable, entonces el valor solo se determina cuando VARIABLEse usa. Esto se ilustra mejor con un ejemplo:

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

# VARIABLE and VAL will both evaluate to "bar"

Cuando lo usas VARIABLE := value, obtienes el valor de value como es ahora . Por ejemplo:

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

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

Utilizando VARIABLE ?= val significa que solo establece el valor de VARIABLE if VARIABLE no está configurado ya. Si aún no está configurado, la configuración del valor se difiere hasta que VARIABLEse utiliza (como en el ejemplo 1).

VARIABLE += valuesimplemente anexa valuea VARIABLE. El valor real de valuese determina como era cuando se configuró inicialmente, usando =o :=.

mipadi
fuente
En realidad, en su primer ejemplo, VARIABLE es $ (VAL) y VAL es barra. VARIABLE expandida cuando se usa.
extraño
1
Sí, los comentarios explican lo que sucedería cuando se usen.
mipadi
Ah Supongo que lo corrigió, o leí mal "evaluar" como "ser".
extraño
7

En las respuestas anteriores, es importante comprender qué se entiende por "los valores se expanden en el momento de la declaración / uso". Dar un valor como *.cno implica ninguna expansión. Es solo cuando esta cadena es utilizada por un comando que tal vez desencadene un poco de bloqueo. Del mismo modo, un valor como $(wildcard *.c)o $(shell ls *.c)no implica ninguna expansión y se evalúa por completo en el momento de la definición, incluso si lo utilizamos :=en la definición de variable.

Pruebe el siguiente Makefile en el directorio donde tiene algunos archivos 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

La ejecución makeactivará una regla que crea un archivo C adicional (vacío), llamado foo.cpero ninguna de las 6 variables tiene foo.cen su valor.

phs
fuente
Esta es una gran llamada y tiene muchos ejemplos para la expansión en el momento de la declaración, sería útil extender la respuesta con un ejemplo y algunas palabras para la expansión en el momento del uso
Robert Monfera