Sự khác biệt giữa các phép gán biến GNU Makefile = ,? = ,: = Và + = là gì?


765

Bất cứ ai cũng có thể đưa ra một lời giải thích rõ ràng về cách phân công biến thực sự hoạt động trong Makefiles.

Sự khác biệt giữa:

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

Tôi đã đọc phần trong hướng dẫn của GNU Make, nhưng nó vẫn không có ý nghĩa với tôi.

Câu trả lời:


1029

Bộ đồ lười

VARIABLE = value

Cài đặt bình thường của một biến, nhưng bất kỳ biến nào khác được đề cập với value trường đều được mở rộng đệ quy với giá trị của chúng tại điểm mà biến đó được sử dụng, không phải là biến mà nó có khi khai báo

Đặt ngay lập tức

VARIABLE := value

Thiết lập một biến có mở rộng đơn giản các giá trị bên trong - các giá trị bên trong nó được mở rộng tại thời điểm khai báo.

Lười bộ nếu vắng mặt

VARIABLE ?= value

Chỉ thiết lập một biến nếu nó không có giá trị. valueluôn được đánh giá khi VARIABLEđược truy cập. Nó tương đương với

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

Xem tài liệu để biết thêm chi tiết.

Nối

VARIABLE += value

Nối giá trị được cung cấp vào giá trị hiện có (hoặc đặt vào giá trị đó nếu biến không tồn tại)


25
A + = B có mở rộng B không? Đó là nếu tôi làm A + = B, và sau đó B + = C, liệu A có đánh giá được nối của $ {B} và $ {C} không?
Anton Daneyko

15
Như phần liên kết của hướng dẫn nói. + = hoạt động theo bất kỳ ngữ nghĩa đơn giản hoặc đệ quy nào mà bài tập ban đầu có. Vì vậy, có, nó sẽ mở rộng RHS nhưng liệu nó có thực hiện được ngay lập tức hay theo cách trì hoãn hay không phụ thuộc vào loại biến trên LHS.
Etan Reisner

6
Bạn có ý nghĩa gì khi bạn nói giá trị biến được mở rộng?
Sashko Lykhenko

3
@ СашкоЛихенко hãy xem ở đây để hiểu ý nghĩa của việc mở rộng gnu.org/software/make/manual/make.html#Flavors
Umair R

7
là "thiết lập nếu vắng mặt" lười biếng hay ngay lập tức? tôi có thể "lười biếng thiết lập nếu vắng mặt" và "thiết lập ngay lập tức nếu hủy bỏ"?
Woodrow Barlow

268

Sử dụng =gây ra biến được gán một giá trị. Nếu biến đã có một giá trị, nó được thay thế. Giá trị này sẽ được mở rộng khi nó được sử dụng. Ví dụ:

HELLO = world
HELLO_WORLD = $(HELLO) world!

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

HELLO = hello

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

Sử dụng :=tương tự như sử dụng =. Tuy nhiên, thay vì giá trị được mở rộng khi nó được sử dụng, nó được mở rộng trong quá trình gán. Ví dụ:

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)

Sử dụng ?= gán cho biến một giá trị iff biến không được gán trước đó. Nếu trước đó biến đã được gán một giá trị trống ( VAR=), thì nó vẫn được coi là tập hợp tôi nghĩ . Nếu không, các chức năng chính xác như= .

Sử dụng +=cũng giống như sử dụng =, nhưng thay vì thay thế giá trị, giá trị được gắn vào giá trị hiện tại, với khoảng trắng ở giữa. Nếu biến được đặt trước đó :=, nó được mở rộng tôi nghĩ . Giá trị kết quả được mở rộng khi nó được sử dụng tôi nghĩ . Ví dụ:

HELLO_WORLD = hello
HELLO_WORLD += world!

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

Nếu một cái gì đó giống như HELLO_WORLD = $(HELLO_WORLD) world!được sử dụng, đệ quy sẽ có kết quả, rất có thể sẽ kết thúc việc thực hiện Makefile của bạn. Nếu A := $(A) $(B)được sử dụng, kết quả sẽ không chính xác như sử dụng+=Bđược mở rộng :=trong khi +=sẽ không Bđược mở rộng.


3
do đó, một hệ quả của điều đó là VARIABLE = literalVARIABLE := literalluôn luôn tương đương. Tôi đã hiểu đúng chưa?
aiao

1
@aiao, vâng, vì nghĩa đen là bất biến đối với việc sử dụng của họ
Sebastian

Một sự khác biệt tinh tế là: -?: Có thể cải thiện hiệu suất trong đệ quy được gọi là makefiles. Ví dụ: nếu $? = $ (shell some_command_that_runs_long_time). Trong các cuộc gọi đệ quy, điều này sẽ chỉ được đánh giá một lần. gây ra hiệu suất xây dựng. : = sẽ chậm hơn vì lệnh không cần thiết phải chạy nhiều lần
KeshV

61

Tôi đề nghị bạn thực hiện một số thí nghiệm bằng cách sử dụng "make". Dưới đây là một bản demo đơn giản, cho thấy sự khác biệt giữa =:=.

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

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

Kiểm tra giải thích chi tiết hơn ở đây


5
Sẽ là tốt hơn để sử dụng @ở phía trước mỗi công thức để tránh sự lặp lại khó hiểu này của kết quả.
Alexandro de Oliveira

2
Thực hiện không hỗ trợ /* ... */bình luận khối
yoonghm

31

Khi bạn sử dụng VARIABLE = value, nếu valuethực sự là một tham chiếu đến một biến khác, thì giá trị chỉ được xác định khi VARIABLEđược sử dụng. Điều này được minh họa tốt nhất với một ví dụ:

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

# VARIABLE and VAL will both evaluate to "bar"

Khi bạn sử dụng VARIABLE := value, bạn nhận được giá trị value như bây giờ . Ví dụ:

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

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

Sử dụng VARIABLE ?= valcó nghĩa là bạn chỉ đặt giá trị VARIABLE nếu VARIABLE chưa được đặt. Nếu nó chưa được đặt, cài đặt của giá trị sẽ được hoãn lại cho đến khiVARIABLE được sử dụng (như trong ví dụ 1).

VARIABLE += valuechỉ cần thêm valuevào VARIABLE. Giá trị thực tế valueđược xác định như khi nó được đặt ban đầu, sử dụng =hoặc :=.


Trên thực tế, trong ví dụ đầu tiên của bạn, VARIABLE là $ (VAL) và VAL là thanh. VARIABLE mở rộng khi nó được sử dụng.
strager

1
Vâng, các ý kiến ​​đang giải thích những gì sẽ xảy ra khi chúng được sử dụng.
mipadi

Ah; Tôi đoán bạn đã sửa nó hoặc tôi đọc sai "đánh giá" là "được."
strager

7

Trong các câu trả lời trên, điều quan trọng là phải hiểu ý nghĩa của "giá trị được mở rộng tại thời điểm khai báo / sử dụng". Đưa ra một giá trị như *.ckhông đòi hỏi bất kỳ sự mở rộng nào. Chỉ khi chuỗi này được sử dụng bởi một lệnh thì nó mới có thể kích hoạt một số khối. Tương tự, một giá trị như $(wildcard *.c)hoặc $(shell ls *.c)không đòi hỏi bất kỳ sự mở rộng nào và được đánh giá hoàn toàn tại thời điểm xác định ngay cả khi chúng tôi sử dụng:= trong định nghĩa biến.

Hãy thử Makefile sau trong thư mục nơi bạn có một số tệp 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

Chạy makesẽ kích hoạt một quy tắc tạo ra một tệp C bổ sung (trống), được gọi foo.cnhưng không có biến nào trong số 6 biến có foo.cgiá trị của nó.


Đây là một cuộc gọi tuyệt vời và có rất nhiều ví dụ để mở rộng tại thời điểm khai báo, sẽ rất hữu ích khi mở rộng câu trả lời bằng một ví dụ và một số từ để mở rộng tại thời điểm sử dụng
Robert Monfera
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.