Apa perbedaan antara penugasan variabel GNU Makefile =,? =,: = Dan + =?

765

Adakah yang bisa memberikan penjelasan yang jelas tentang bagaimana tugas variabel benar-benar bekerja di Makefiles.

Apa perbedaan antara :

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

Saya telah membaca bagian dalam manual GNU Make, tetapi masih tidak masuk akal bagi saya.

mmoris
sumber

Jawaban:

1029

Set Malas

VARIABLE = value

Pengaturan normal suatu variabel, tetapi variabel lain apa pun yang disebutkan dengan valuebidang diperluas secara rekursif dengan nilainya pada titik di mana variabel digunakan, bukan yang ada saat dideklarasikan

Set Segera

VARIABLE := value

Pengaturan variabel dengan ekspansi sederhana dari nilai-nilai di dalamnya - nilai-nilai di dalamnya diperluas pada waktu deklarasi.

Set Malas Jika Tidak Ada

VARIABLE ?= value

Pengaturan variabel hanya jika tidak memiliki nilai. valueselalu dievaluasi saat VARIABLEdiakses. Ini setara dengan

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

Lihat dokumentasi untuk lebih jelasnya.

Menambahkan

VARIABLE += value

Menambahkan nilai yang disediakan ke nilai yang ada (atau mengatur ke nilai itu jika variabel tidak ada)

Alnitak
sumber
25
Apakah A + = B memperluas B? Itu jika jika saya melakukan A + = B, dan kemudian B + = C, apakah A akan mengevaluasi gabungan dari $ {B} dan $ {C}?
Anton Daneyko
15
Seperti yang dikatakan bagian manual yang tertaut. + = beroperasi sesuai dengan semantik sederhana atau rekursif apa pun yang diberikan tugas asli. Jadi ya, itu akan memperluas RHS tetapi apakah itu segera atau dalam cara yang ditangguhkan tergantung pada jenis variabel pada LHS.
Etan Reisner
6
Apa maksud Anda ketika Anda mengatakan nilai variabel diperluas?
Sashko Lykhenko
3
@ СашкоЛихенко lihat di sini untuk mendapatkan arti ekspansi gnu.org/software/make/manual/make.html#Flavours
Umair R
7
apakah "disetel jika tidak ada" malas atau langsung? dapatkah saya "set malas jika tidak ada" dan "set segera jika abset"?
Woodrow Barlow
268

Menggunakan =menyebabkan variabel diberi nilai. Jika variabel sudah memiliki nilai, itu diganti. Nilai ini akan diperluas saat digunakan. Sebagai contoh:

HELLO = world
HELLO_WORLD = $(HELLO) world!

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

HELLO = hello

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

Menggunakan :=mirip dengan menggunakan =. Namun, alih-alih nilai diperluas ketika digunakan, itu diperluas selama penugasan. Sebagai contoh:

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)

Menggunakan ?=memberikan nilai variabel jika variabel sebelumnya tidak ditugaskan. Jika variabel sebelumnya diberi nilai kosong ( VAR=), itu masih dianggap set saya pikir . Kalau tidak, fungsinya persis seperti =.

Menggunakan +=seperti menggunakan =, tetapi alih-alih mengganti nilai, nilai ditambahkan ke nilai saat ini, dengan spasi di antaranya. Jika variabel sebelumnya diatur dengan :=, saya pikir diperluas . Nilai yang dihasilkan diperluas ketika digunakan saya pikir . Sebagai contoh:

HELLO_WORLD = hello
HELLO_WORLD += world!

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

Jika sesuatu seperti HELLO_WORLD = $(HELLO_WORLD) world!digunakan, rekursi akan terjadi, yang kemungkinan besar akan mengakhiri eksekusi Makefile Anda. Jika A := $(A) $(B)digunakan, hasilnya tidak akan sama persis dengan menggunakan +=karena Bdiperluas dengan :=sedangkan +=tidak akan menyebabkan Bdiperluas.

strager
sumber
3
konsekuensi dari itu adalah karena itu VARIABLE = literaldan VARIABLE := literalselalu setara. Apakah saya benar?
aiao
1
@ Aiiao, ya karena literalnya tidak berbeda dengan kegunaannya
Sebastian
Perbedaan yang tidak kentara adalah: -?: Dapat meningkatkan kinerja dalam makefile yang disebut rekursif. Untuk mis jika $? = $ (shell some_command_that_runs_long_time). Dalam panggilan rekursif, ini hanya akan dievaluasi sekali. menyebabkan keuntungan dalam membangun kinerja. : = akan lebih lambat karena perintah tidak perlu berjalan beberapa kali
KeshV
61

Saya sarankan Anda melakukan beberapa percobaan menggunakan "make". Berikut ini adalah demo sederhana, menunjukkan perbedaan antara =dan :=.

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

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

Lihat penjelasan lebih rinci di sini

Nickand
sumber
5
Akan lebih baik menggunakan a @di depan setiap resep untuk menghindari pengulangan hasil yang membingungkan ini.
Alexandro de Oliveira
2
Make tidak mendukung /* ... */blok komentar
yoonghm
31

Ketika Anda menggunakan VARIABLE = value, jika valuesebenarnya referensi ke variabel lain, maka nilainya hanya ditentukan saat VARIABLEdigunakan. Ini digambarkan dengan contoh:

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

# VARIABLE and VAL will both evaluate to "bar"

Ketika Anda menggunakan VARIABLE := value, Anda mendapatkan nilai value seperti sekarang . Sebagai contoh:

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

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

Menggunakan VARIABLE ?= valberarti Anda hanya menetapkan nilai VARIABLE jika VARIABLE belum ditetapkan. Jika belum disetel, pengaturan nilai ditunda hingga VARIABLEdigunakan (seperti pada contoh 1).

VARIABLE += valuehanya ditambahkan valueke VARIABLE. Nilai aktual valueditentukan seperti ketika awalnya ditetapkan, menggunakan salah satu =atau :=.

mipadi
sumber
Sebenarnya, dalam contoh pertama Anda, VARIABLE adalah $ (VAL) dan VAL adalah bar. VARIABEL diperluas saat digunakan.
strager
1
Ya, komentar menjelaskan apa yang akan terjadi ketika mereka digunakan.
mipadi
Ah; Saya kira Anda memperbaikinya, atau saya salah membaca "evaluasi" sebagai "menjadi."
strager
7

Dalam jawaban di atas, penting untuk memahami apa yang dimaksud dengan "nilai diperluas pada waktu deklarasi / penggunaan". Memberikan nilai seperti *.ctidak memerlukan ekspansi apa pun. Hanya ketika string ini digunakan oleh sebuah perintah maka mungkin akan memicu beberapa globbing. Demikian pula, nilai suka $(wildcard *.c)atau $(shell ls *.c)tidak memerlukan ekspansi apa pun dan sepenuhnya dievaluasi pada waktu definisi bahkan jika kita digunakan :=dalam definisi variabel.

Cobalah direktori Makefile berikut di mana Anda memiliki beberapa file 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

Menjalankan makeakan memicu aturan yang membuat file C ekstra (kosong), dipanggil foo.ctetapi tidak ada dari 6 variabel yang memiliki foo.cnilainya.

phs
sumber
Ini adalah panggilan yang bagus dan memiliki banyak contoh untuk ekspansi pada waktu deklarasi, akan berguna untuk memperluas jawaban dengan contoh dan beberapa kata untuk ekspansi pada waktu penggunaan
Robert Monfera