Xác định biến trong thời gian thực hiện quy tắc


208

Trong GNUmakefile của tôi, tôi muốn có một quy tắc sử dụng một thư mục tạm thời. Ví dụ:

out.tar: TMP := $(shell mktemp -d)
        echo hi $(TMP)/hi.txt
        tar -C $(TMP) cf $@ .
        rm -rf $(TMP)

Như đã viết, quy tắc trên tạo thư mục tạm thời tại thời điểm quy tắc được phân tích cú pháp . Điều này có nghĩa là, ngay cả khi tôi không thực hiện. Mọi lúc, nhiều thư mục tạm thời được tạo. Tôi muốn tránh / tmp của tôi bị vấy bẩn với các thư mục tạm thời không sử dụng.

Có cách nào để biến biến chỉ được xác định khi quy tắc được kích hoạt, trái ngược với bất cứ khi nào nó được xác định không?

Suy nghĩ chính của tôi là đổ mktemp và tar vào một kịch bản shell nhưng điều đó có vẻ hơi khó coi.

Câu trả lời:


321

Trong ví dụ của bạn, TMPbiến được đặt (và các thư mục tạm thời tạo ra) bất cứ khi nào quy định cho out.tarđược đánh giá. Để chỉ tạo thư mục khi out.tarthực sự bị bắn, bạn cần chuyển việc tạo thư mục xuống theo các bước:

out.tar : 
    $(eval TMP := $(shell mktemp -d))
    @echo hi $(TMP)/hi.txt
    tar -C $(TMP) cf $@ .
    rm -rf $(TMP)

Hàm eval đánh giá một chuỗi như thể nó đã được nhập vào tệp tạo tệp thủ công. Trong trường hợp này, nó đặt TMPbiến thành kết quả của lệnh shellgọi hàm.

chỉnh sửa (để phản hồi ý kiến):

Để tạo một biến duy nhất, bạn có thể làm như sau:

out.tar : 
    $(eval $@_TMP := $(shell mktemp -d))
    @echo hi $($@_TMP)/hi.txt
    tar -C $($@_TMP) cf $@ .
    rm -rf $($@_TMP)

Điều này sẽ đặt trước tên của mục tiêu (out.tar, trong trường hợp này) cho biến, tạo ra một biến có tên out.tar_TMP. Hy vọng, điều đó là đủ để ngăn ngừa xung đột.


2
Làm mát một vài điều rõ ràng ... điều này sẽ không phạm vi TMP đến mục tiêu này, phải không? Vì vậy, nếu có các quy tắc khác có cách sử dụng $ (TMP) của riêng họ (có thể song song với -j), có thể có xung đột? Ngoài ra, @echo thậm chí còn cần thiết? Có vẻ như bạn có thể bỏ nó đi.
Emil Ngồi

3
Có vẻ để thực hiện các mẹo (mặc dù hơi mờ đối với người không phải là bậc thầy trung bình :-) Cảm ơn!
Emil Ngồi

29
Hãy cẩn thận với giải pháp này! $(eval $@_TMP := $(shell mktemp -d))sẽ xảy ra khi Makefile được đánh giá đầu tiên không theo thứ tự các quy trình quy tắc. Nói cách khác, $(eval ...)xảy ra sớm hơn bạn nghĩ. Trong khi điều đó có thể ổn với ví dụ này, phương pháp này sẽ gây ra sự cố cho một số hoạt động tuần tự.
JamesThomasMoon1979

1
@ JamesThomasMoon1979 bạn có biết cách khắc phục những hạn chế đó không, ví dụ: eval một số biến nên là kết quả của các bước được thực hiện trước đó trong quy tắc?
Vadim Kotov

2
Trả lời câu hỏi của riêng tôi: cách giải quyết đối với tôi là tạo các tệp trong quá trình thực thi quy tắc 1 và cố gắng loại bỏ chúng trong bước đầu tiên của quy tắc 2.
Vadim Kotov

63

Một cách tương đối dễ dàng để làm điều này là viết toàn bộ chuỗi dưới dạng shell script.

out.tar:
   set -e ;\
   TMP=$$(mktemp -d) ;\
   echo hi $$TMP/hi.txt ;\
   tar -C $$TMP cf $@ . ;\
   rm -rf $$TMP ;\

Tôi đã tổng hợp một số mẹo liên quan ở đây: https://stackoverflow.com/a/29085684/86967


3
Đây chắc chắn là câu trả lời đơn giản nhất và do đó tốt nhất (tránh @evalvà làm công việc giống nhau). Lưu ý rằng trong đầu ra của bạn, bạn thấy $TMP(ví dụ tar -C $TMP ...) mặc dù giá trị là chính xác được truyền cho lệnh.
Karl Richter

Đây là cách nó thường được thực hiện; Tôi hy vọng mọi người nhìn thấy câu trả lời này bởi vì trong thực tế không ai trải qua tất cả nỗ lực của người được chấp nhận.
pmos

Điều gì nếu bạn đang sử dụng các lệnh cụ thể shell? Trong trường hợp đó, các lệnh shell sẽ có cùng ngôn ngữ shell so với lệnh gọi make, phải không? Tôi đã thấy một số makefile với shebang.
ptitpion

@ptitpion: Bạn cũng có thể muốn SHELL := /bin/bashtrong tệp tạo tệp của mình để bật các tính năng dành riêng cho BASH.
tộc

31

Một khả năng khác là sử dụng các dòng riêng biệt để thiết lập Tạo các biến khi quy tắc kích hoạt.

Ví dụ, đây là một tệp thực hiện với hai quy tắc. Nếu một quy tắc kích hoạt, nó sẽ tạo một thư mục tạm thời và đặt TMP thành tên thư mục tạm thời.

PHONY = ruleA ruleB display

all: ruleA

ruleA: TMP = $(shell mktemp -d testruleA_XXXX)
ruleA: display

ruleB: TMP = $(shell mktemp -d testruleB_XXXX)
ruleB: display

display:
    echo ${TMP}

Chạy mã tạo ra kết quả mong đợi:

$ ls
Makefile
$ make ruleB
echo testruleB_Y4Ow
testruleB_Y4Ow
$ ls
Makefile  testruleB_Y4Ow

Cũng như một lời nhắc nhở, GNU make có một cú pháp cho các điều kiện tiên quyết chỉ có thứ tự có thể cần thiết để bổ sung cho phương pháp này.
anol

11
Hãy cẩn thận với giải pháp này! ruleA: TMP = $(shell mktemp -d testruleA_XXXX)ruleB: TMP = $(shell mktemp -d testruleB_XXXX)sẽ xảy ra khi Makefile được đánh giá đầu tiên. Nói cách khác, ruleA: TMP = $(shell ...xảy ra sớm hơn bạn nghĩ. Mặc dù điều này có thể hoạt động cho trường hợp cụ thể này, phương pháp này sẽ gây ra sự cố cho một số hoạt động tuần tự.
JamesThomasMoon1979

1

Tôi không thích câu trả lời "Đừng", nhưng ... không.

makeCác biến là toàn cục và được cho là được đánh giá trong giai đoạn "phân tích cú pháp" của makefile, không phải trong giai đoạn thực thi.

Trong trường hợp này, miễn là biến cục bộ cho một mục tiêu, hãy làm theo câu trả lời của @ nobar và biến nó thành biến vỏ.

Các biến đặc trưng cho mục tiêu cũng được coi là có hại bởi các triển khai thực hiện khác: kati , Mozilla pymake . Do chúng, mục tiêu có thể được xây dựng khác nhau tùy thuộc vào việc nó được xây dựng độc lập hay là phụ thuộc của mục tiêu chính với biến cụ thể theo mục tiêu. Và bạn sẽ không biết nó là gì , bởi vì bạn không biết những gì đã được xây dựng.

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.