Jaka jest różnica między przypisaniami zmiennych GNU Makefile =,? =,: = I + =?

764

Czy ktoś może wyjaśnić, jak naprawdę działa przypisanie zmiennych w Makefiles.

Jaka jest różnica pomiędzy :

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

Przeczytałem ten rozdział w instrukcji GNU Make, ale nadal nie ma to dla mnie sensu.

mmoris
źródło

Odpowiedzi:

1028

Leniwy zestaw

VARIABLE = value

Normalne ustawienie zmiennej, ale wszelkie inne zmienne wymienione w valuepolu są rekurencyjnie rozszerzane o ich wartość w punkcie, w którym zmienna jest używana, a nie w tej, którą miała w momencie deklaracji

Zestaw natychmiastowy

VARIABLE := value

Ustawienie zmiennej z prostym rozszerzaniem wartości wewnątrz - wartości w niej są rozszerzane w czasie deklaracji.

Lazy Set If Absent

VARIABLE ?= value

Ustawienie zmiennej tylko wtedy, gdy nie ma wartości. valuejest zawsze oceniany, kiedy VARIABLEjest dostępny. Jest to równoważne z

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

Więcej informacji znajduje się w dokumentacji .

Dodać

VARIABLE += value

Dołączanie podanej wartości do wartości istniejącej (lub ustawienie tej wartości, jeśli zmienna nie istnieje)

Alnitak
źródło
25
Czy A + = B rozwija B? To znaczy, jeśli zrobię A + = B, a następnie B + = C, czy A oceni łączenie {B} i {C}?
Anton Daneyko
15
Jak mówi powiązana sekcja instrukcji. + = działa według dowolnej prostej lub rekurencyjnej semantyki, jaką miało pierwotne przypisanie. Tak, rozszerzy RHS, ale to, czy zrobi to natychmiast, czy w odroczony sposób, zależy od rodzaju zmiennej w LHS.
Etan Reisner
6
Co masz na myśli mówiąc, że wartość zmiennej jest rozwijana?
Sashko Lykhenko
2
@ СашкоЛихенко zajrzyj tutaj, aby uzyskać znaczenie rozszerzenia gnu.org/software/make/manual/make.html#Flavours
Umair R
7
jest „ustawiony, jeśli nieobecny” leniwy lub natychmiastowy? czy mogę „ustawić leniwy, jeśli nieobecny” i „natychmiastowy zestaw, jeśli nieobecny”?
Woodrow Barlow,
268

Użycie =powoduje przypisanie zmiennej wartości. Jeśli zmienna ma już wartość, jest zastępowana. Ta wartość zostanie rozszerzona, gdy zostanie użyta. Na przykład:

HELLO = world
HELLO_WORLD = $(HELLO) world!

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

HELLO = hello

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

Korzystanie :=jest podobne do korzystania =. Jednak zamiast wartości, która jest używana, jest ona rozwijana podczas przypisywania. Na przykład:

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)

Użycie ?=przypisuje zmiennej wartość, jeśli zmienna nie była wcześniej przypisana. Jeśli do tej zmiennej wcześniej przypisano pustą wartość ( VAR=), myślę , że nadal jest uważana za zestaw . W przeciwnym razie działa dokładnie tak samo =.

Używanie +=jest jak używanie =, ale zamiast zamiany wartości, wartość jest dołączana do bieżącej, z odstępem pomiędzy nimi. Jeśli zmienna była wcześniej ustawiona za pomocą :=, to myślę , że jest rozwinięta . Otrzymana wartość jest rozszerzona, gdy jest używany myślę . Na przykład:

HELLO_WORLD = hello
HELLO_WORLD += world!

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

Jeśli zostanie HELLO_WORLD = $(HELLO_WORLD) world!użyte coś podobnego , nastąpi rekurencja, co najprawdopodobniej zakończy wykonanie pliku Makefile. Jeśli A := $(A) $(B)użyto, wynik nie byłby dokładnie taki sam, jak przy użyciu +=ponieważ Bjest rozszerzona o :=natomiast +=nie spowoduje Bzostać rozszerzony.

strager
źródło
3
konsekwencja tego jest zatem VARIABLE = literali VARIABLE := literalzawsze jest równoważna. Czy dobrze to zrozumiałem?
aiao
1
@ aiao, tak, ponieważ literały są niezmienne dla ich zastosowań
Sebastian
Subtelna różnica polega na: -?: Może poprawić wydajność w rekurencyjnych nazwanych makefile. Na przykład jeśli $? = $ (shell some_command_that_runs_long_time). W połączeniach rekurencyjnych zostanie to ocenione tylko raz. powodując wzrost wydajności kompilacji. : = będzie wolniejszy, ponieważ polecenie jest niepotrzebnie uruchamiane wiele razy
KeshV
61

Sugeruję wykonanie kilku eksperymentów z użyciem „make”. Oto proste demo pokazujące różnicę między =i :=.

/* 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 drukuje:

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

Sprawdź bardziej szczegółowe wyjaśnienie tutaj

Nickand
źródło
5
Lepiej byłoby użyć z @przodu każdego przepisu, aby uniknąć mylącego powtarzania wyników.
Alexandro de Oliveira
2
Marka nie obsługuje /* ... */komentowania bloków
yoonghm,
31

Gdy używasz VARIABLE = value, jeśli valuetak naprawdę jest odwołaniem do innej zmiennej, to wartość jest określana tylko wtedy, gdy VARIABLEjest używana. Najlepiej ilustruje to przykład:

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

# VARIABLE and VAL will both evaluate to "bar"

Kiedy używasz VARIABLE := value, otrzymujesz wartość value taką, jaka jest teraz . Na przykład:

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

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

Używanie VARIABLE ?= valoznacza, że ​​ustawiasz tylko wartość VARIABLE if, która VARIABLE nie jest już ustawiona. Jeśli nie jest jeszcze ustawiony, ustawienie wartości jest odraczane do momentu VARIABLEużycia (jak w przykładzie 1).

VARIABLE += valuepo prostu dołącza valuedo VARIABLE. Rzeczywista wartość valuejest ustalana w stanie, w jakim była początkowo ustawiona, przy użyciu albo =lub :=.

mipadi
źródło
W rzeczywistości w pierwszym przykładzie ZMIENNA jest $ (VAL), a VAL to słupek. ZMIENNA rozwinięta, gdy jest używana.
strager
1
Tak, komentarze wyjaśniają, co by się stało, gdy zostaną użyte.
mipadi
Ach; Chyba poprawiłeś to, albo źle odczytałem „oceniam” jako „bądź”.
strager
7

W powyższych odpowiedziach ważne jest, aby zrozumieć, co należy rozumieć przez „wartości są rozszerzane w czasie deklaracji / użytkowania”. Podanie wartości podobnej *.cnie pociąga za sobą żadnego rozszerzenia. Tylko wtedy, gdy ten ciąg jest używany przez polecenie, może wywołać trochę globowania. Podobnie, wartość taka jak $(wildcard *.c)lub $(shell ls *.c)nie pociąga za sobą żadnego rozszerzenia i jest całkowicie oceniana w czasie definicji, nawet jeśli zastosowaliśmy ją :=w definicji zmiennej.

Wypróbuj następujący Makefile w katalogu, w którym masz jakieś pliki 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

Uruchomienie makeuruchomi regułę, która tworzy dodatkowy (pusty) plik C, wywoływany, foo.cale żadna z 6 zmiennych nie ma foo.cswojej wartości.

phs
źródło
To świetne wezwanie i zawiera wiele przykładów rozszerzenia w czasie deklaracji, przydatne byłoby rozszerzenie odpowiedzi o przykład i kilka słów do rozszerzenia w czasie używania
Robert Monfera