อะไรคือความแตกต่างระหว่างการกำหนดตัวแปร GNU Makefile =,? =,: = และ + =?


764

ใครช่วยอธิบายได้อย่างชัดเจนว่าการมอบหมายตัวแปรนั้นทำงานได้อย่างไรใน Makefiles

อะไรคือความแตกต่างระหว่าง :

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

ฉันได้อ่านหัวข้อในคู่มือของ GNU Make แล้ว แต่ก็ยังไม่สมเหตุสมผลสำหรับฉัน

คำตอบ:


1028

ชุดขี้เกียจ

VARIABLE = value

การตั้งค่าปกติของตัวแปร แต่ตัวแปรอื่น ๆ ที่กล่าวถึงกับvalueฟิลด์นั้นจะถูกขยายซ้ำด้วยค่าของมัน ณ จุดที่ใช้ตัวแปรนั้นไม่ใช่ตัวแปรที่มีเมื่อมีการประกาศ

ชุดทันที

VARIABLE := value

การตั้งค่าของตัวแปรที่มีการขยายตัวง่าย ๆ ของค่าภายใน - ค่าภายในนั้นจะถูกขยายในเวลาที่ประกาศ

Lazy Set หากไม่มี

VARIABLE ?= value

การตั้งค่าตัวแปรเฉพาะเมื่อมันไม่มีค่า valueถูกประเมินเสมอเมื่อVARIABLEเข้าถึง มันเทียบเท่ากับ

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

ดูเอกสารประกอบสำหรับรายละเอียดเพิ่มเติม

ผนวก

VARIABLE += value

ต่อท้ายค่าที่ให้มากับค่าที่มีอยู่ (หรือการตั้งค่าเป็นค่านั้นถ้าตัวแปรไม่มีอยู่)


25
A + = B ขยาย B หรือไม่? นั่นคือถ้าฉันทำ A + = B แล้ว B + = C A จะประเมินค่าการต่อข้อมูล $ {B} และ $ {C} หรือไม่
Anton Daneyko

15
เป็นส่วนที่เชื่อมโยงของคู่มือกล่าวว่า + = ทำงานตามความหมายที่เรียบง่ายหรือแบบเรียกซ้ำที่การมอบหมายเริ่มต้นมีอยู่ ดังนั้นใช่มันจะขยาย RHS แต่ไม่ว่าจะทำทันทีหรือในลักษณะที่เลื่อนออกไปขึ้นอยู่กับชนิดของตัวแปรใน LHS
Etan Reisner

6
คุณหมายถึงอะไรเมื่อคุณพูดว่าตัวแปรค่าขยายตัว?
Sashko Lykhenko

2
@ СашкоЛихенкоดูที่นี่เพื่อรับความหมายของการขยายตัว gnu.org/software/make/manual/make.html#Flavors
Umair R

7
"ถูกตั้งค่าหากไม่มี" ขี้เกียจหรือทันทีหรือไม่ ฉัน "ขี้เกียจตั้งถ้าขาด" และ "ตั้งทันทีถ้า abset"?
Woodrow Barlow

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การขยาย


3
ผลที่ตามมาก็คือVARIABLE = literalและVARIABLE := literalเสมอกันเสมอ ฉันเข้าใจถูกไหม?
aiao

1
@ aiao, ใช่เนื่องจากตัวอักษรมีความคงที่ต่อการใช้งาน
Sebastian

ความแตกต่างเล็กน้อยคือ: -?: สามารถปรับปรุงประสิทธิภาพในแบบเรียกซ้ำที่เรียกว่า makefiles เช่นถ้า $? = $ (เชลล์ some_command_that_runs_long_time) ในการเรียกแบบเรียกซ้ำสิ่งนี้จะถูกประเมินเพียงครั้งเดียว ก่อให้เกิดกำไรในการสร้างประสิทธิภาพ : = จะช้าลงเนื่องจากคำสั่งรันหลายครั้งโดยไม่จำเป็น
KeshV

61

ฉันขอแนะนำให้คุณทำการทดลองโดยใช้ "ทำ" นี่คือตัวอย่างง่ายๆแสดงให้เห็นความแตกต่างระหว่างและ=:=

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

ตรวจสอบคำอธิบายเพิ่มเติมได้ที่นี่


5
มันจะเป็นการดีกว่าถ้าใช้@สูตรต่อหน้าเพื่อหลีกเลี่ยงการทำซ้ำผลลัพธ์ที่สับสน
Alexandro de Oliveira

2
ทำให้ไม่สนับสนุน/* ... */ความคิดเห็นบล็อก
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 ถ้า VARIABLEยังไม่ได้ตั้งค่า หากยังไม่ได้ตั้งค่าการตั้งค่าจะถูกเลื่อนออกไปจนกว่าVARIABLEจะใช้ (ดังในตัวอย่างที่ 1)

VARIABLE += valueเพียงผนวกไปvalue VARIABLEค่าที่แท้จริงของvalueจะถูกกำหนดตามที่มันเป็นเมื่อมันถูกตั้งค่าเริ่มต้นโดยใช้หรือ=:=


ที่จริงแล้วในตัวอย่างแรกของคุณ VARIABLE คือ $ (VAL) และ VAL คือแถบ ตัวแปรขยายเมื่อมีการใช้งาน
แปลกหน้า

1
ใช่ความคิดเห็นอธิบายว่าจะเกิดอะไรขึ้นเมื่อมีการใช้งาน
mipadi

อา; ฉันเดาว่าคุณแก้ไขมันหรือฉันอ่านผิด "ประเมิน" ว่า "เป็น"
แปลกหน้า

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อยู่ในค่า


นี่คือการโทรที่ยอดเยี่ยมและมีตัวอย่างมากมายสำหรับการขยายตัวในเวลาที่ประกาศมันจะเป็นประโยชน์ในการขยายคำตอบด้วยตัวอย่างและคำบางคำสำหรับการขยายเวลาใช้งาน
Robert Monfera
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.