2012-12-27 25 views
6

Thực tiễn tốt nhất để viết mã có thể tái sử dụng trong Makefile là gì?Thực hành tốt nhất để viết mã có thể tái sử dụng

Giả sử tôi có một Makefile:

.PHONY: all task01-all task01-clean task01-run task02-all task02-clean task02-run 

all: task01-all task02-all 

############################################################################### 
task01-all: task01-clean task01 task01-run 

task01-clean: 
    rm task01 task01.{exi,o} -f 

task01: 
    compiler task01.ext -O2 --make 

task01-run: 
    ./task01 

############################################################################### 
task02-all: task02-clean task02 task02-run 

task02-clean: 
    rm task02 task02.{exi,o} -f 

task02: 
    compiler task02.ext -O2 --make 

task02-run: 
    ./task02 

Bây giờ tôi muốn thêm gia đình mới của nhiệm vụ (task03), và tôi cần phải copypaste toàn phần, làm cho s/02/03/ cho nó và thêm chúng vào .PHONY phần - đó là ồn ào, ghê tởm và không đúng.

Làm cách nào để tránh điều đó? Tôi có thể xác định lại tất cả các tác vụ với các mẫu bằng cách nào đó để có cơ chế tốt đẹp để thêm nhóm nhiệm vụ mới trong một dòng?

Trả lời

9

Kể từ khi câu hỏi là về cách viết lại có thể sử dụng mã trong Makefiles, tôi sẽ đưa ra một ví dụ về cách sử dụng quy tắc mẫu trong GNU Make (nó trông như đó là những gì bạn đang sử dụng kể từ khi bạn đề cập đến mục tiêu .PHONY). Tuy nhiên, nếu bạn không sử dụng bất kỳ kiểm tra phụ thuộc Make, nó có thể được đơn giản để làm điều này với một kịch bản shell - một cái gì đó như:

#!/bin/sh 
TASKS="task01 task02 task03" 

for i in $TASKS; do 
    rm $i $i.ext $i.o -f; 
    compiler $i.ext -O2 --make; 
    ./$i; 
done 

Tuy nhiên, để mở rộng nguyên tắc để thực hiện, chúng tôi có một vấn đề khác để giải quyết. Dòng có dạng:

task01-all: task01-clean task01 task01-run 

đừng nói Hãy theo thứ tự nào để xây dựng các điều kiện tiên quyết - chỉ rằng tất cả họ đều cần phải được thực hiện trước task01-all được xây dựng. Thay vào đó, mỗi bước chạy sẽ phụ thuộc vào bước trước đó. Một cái gì đó như thế này:

TASKS=task01-run task02-run task03-run 

.PHONY: all $(TASKS) $(TASKS:run=clean) 

all: $(TASKS) 

$(TASKS:run=clean): %-clean: 
    rm $* $*.ext $*.o -f 

%: %.ext | %-clean 
    compiler $< -O2 --make 

$(TASKS): %-run: % 
    ./$< 

Các quy tắc với % được gọi là "quy tắc mẫu", và họ là một công cụ tuyệt vời để tránh tái bằng văn bản cùng một quy tắc nhiều lần cho các mục tiêu khác nhau. Một lưu ý là Make thường không kiểm tra các quy tắc mẫu cho mục tiêu .PHONY; chúng tôi yêu cầu Thực hiện điều này một cách rõ ràng bằng cách đặt trước các quy tắc đó với danh sách các mục tiêu và dấu hai chấm thứ hai (ví dụ: $(TASKS):).

task01-run cần task01 để hoạt động, chúng tôi thực hiện mục tiêu %-run phụ thuộc vào %. Ngoài ra, Makefile của bạn cho thấy rằng bạn muốn chạy sạch mọi lúc, vì vậy chúng tôi thực hiện % phụ thuộc vào %-clean. Vì %-clean không thực sự tạo ra bất kỳ đầu ra nào, chúng tôi thực hiện điều này là phụ thuộc "chỉ đặt hàng" - Không tìm kiếm dấu thời gian hoặc bất kỳ thứ gì, nó sẽ chỉ chạy quy tắc %-clean trước bất kỳ lúc nào cần chạy % qui định. "Order chỉ" phụ thuộc được đặt sau một |:

%: %.ext | %-clean 

Đó là đáng nói đến là một trong những thế mạnh lớn nhất Make là nó có thể tiết kiệm thời gian bằng cách không lặp đi lặp lại công việc mà không cần phải được lặp đi lặp lại - tức là, nó chỉ chạy một quy tắc nếu các phụ thuộc mới hơn mục tiêu.Vì vậy, bạn có thể rời khỏi sự phụ thuộc vào %-clean, mà có thể gây ra làm để chỉ chạy compiler $< -O2 --make nếu %.ext mới hơn %:

%: %.ext 
    compiler $< -O2 --make 

Sau đó, bạn có thể thêm một quy tắc chỉ để chạy tất cả các %-clean mục tiêu:

.PHONY: all $(TASKS) $(TASKS:run=clean) clean 

clean: $(TASKS:run=clean) 

Điều cuối cùng: Tôi sử dụng một số biến đặc biệt trong công thức nấu ăn. [email protected] là viết tắt của mục tiêu đang được xây dựng. $< là viết tắt của sự phụ thuộc đầu tiên. $* là viết tắt của "gốc" của quy tắc mẫu (tức là phần khớp với %).

+0

Giải thích hay. Cảm ơn! –

+0

Cung cấp ví dụ về "|" thật tuyệt, cảm ơn. –

2

Hình như những gì tôi đang tìm kiếm:

ALLS=task01-all task02-all 
BUILDS=${ALLS:-all=-build} 
CLEANS=${ALLS:-all=-clean} 
RUNS=${ALLS:-all=-run} 

.PHONY: all $(ALLS) $(CLEANS) $(BUILDS) $(RUNS) 

all: $(ALLS) 

############################################################################### 
$(ALLS): $(CLEANS) $(BUILDS) $(RUNS) 

$(CLEANS): 
     rm ${@:-clean=} ${@:-clean=}.{ext,o} -f 

$(BUILDS): 
     compiler ${@:-build=}.ext -O2 --make 

$(RUNS): 
     ./${@:-run=} 
Các vấn đề liên quan