2009-12-19 28 views
14

Tôi có thư mục "lib" trong thư mục chính của ứng dụng, chứa một số thư mục con tùy ý, mỗi thư mục có Makefile riêng.làm cho mục tiêu thư mục con ký tự đại diện

Tôi muốn có một Makefile duy nhất trong thư mục chính, gọi cho mỗi thư mục con của Makefile. Tôi biết điều này là có thể nếu tôi tự liệt kê các subdir, nhưng tôi muốn nó được thực hiện tự động.

Tôi đã nghĩ về một cái gì đó như sau, nhưng rõ ràng là nó không hoạt động. Lưu ý rằng tôi cũng có mục tiêu sạch, kiểm tra, v.v., vì vậy, có lẽ% không phải là một ý tưởng hay.

LIBS=lib/* 

all: $(LIBS) 

%: 
    (cd [email protected]; $(MAKE)) 

Mọi trợ giúp đều được đánh giá cao!

Trả lời

23

Sau đây sẽ làm việc với GNU make:

LIBS=$(wildcard lib/*) 
all: $(LIBS) 
.PHONY: force 
$(LIBS): force 
    cd [email protected] && pwd 

Nếu có thể có một cái gì đó khác hơn so với thư mục trong lib, bạn cách khác có thể sử dụng:

LIBS=$(shell find lib -type d) 

Để giải quyết các mục tiêu phát hành nhiều, bạn có thể tạo mục tiêu đặc biệt cho mỗi thư mục, sau đó loại bỏ tiền tố cho tiểu công cụ xây dựng:

LIBS=$(wildcard lib/*) 
clean_LIBS=$(addprefix clean_,$(LIBS)) 
all: $(LIBS) 
clean: $(clean_LIBS) 
.PHONY: force 
$(LIBS): force 
    echo make -C [email protected] 
$(clean_LIBS): force 
    echo make -C $(patsubst clean_%,%,[email protected]) clean 
+0

Wow, cảm ơn. Làm việc như một say mê! – Zed

+1

Tại sao bạn chưa định nghĩa '$ (clean_LIBS)' là giả mạo? Ví dụ. '.PHONY: $ (LIBS) $ (clean_LIBS)', sau đó tôi nghĩ bạn không cần 'force'. –

+0

@dma_k: Bạn có một điểm tuyệt vời. Tôi đã không thử nghiệm đề xuất của bạn, nhưng lý do của bạn có vẻ đúng với tôi. Trong thực tế, nó có vẻ rất giống với những gì GNU Make manual đang gợi ý ở đây (xem bit trên subdirs): http://www.gnu.org/software/make/manual/make.html#Phony-Targets – mrkj

2

Ngoài ra còn có một cách để liệt kê thư mục con với gmake lệnh duy nhất, mà không sử dụng bất kỳ lệnh shell:

test: 
    @echo $(filter %/, $(wildcard lib/*/)) 

này sẽ liệt kê tất cả các thư mục con với trailing '/'. Để loại bỏ nó, bạn có thể sử dụng các mô hình thay thế:

subdirs = $(filter %/, $(wildcard lib/*/)) 
test: 
    @echo $(subdirs:%/=%) 

Sau đó, để thực sự tạo các quy tắc thực hiện makefiles trong mỗi thư mục con bạn có thể sử dụng một thủ thuật nhỏ - một mục tiêu giả mạo trong một thư mục không tồn tại. Tôi nghĩ trong trường hợp này, một ví dụ sẽ cho biết nhiều hơn bất kỳ lời giải thích nào:

FULL_DIRS =$(filter %/, $(wildcard lib/*/)) 
LIB_DIRS =$(FULL_DIRS:%/=%) 
DIRS_CMD =$(foreach subdir, $(LIB_DIRS), make-rule/$(subdir)) 

make-rule/%: 
    cd $* && $(MAKE) 

all: DIRS_CMD 

Về cơ bản, mục tiêu là tất cả các thư mục con làm điều kiện tiên quyết. Ví dụ, nếu LIB_DIRS chứa lib/folder1 lib/folder2 sau đó mở rộng sẽ trông như thế này:

all: make-rule/lib/folder1 make-rule/lib/folder2 

Then 'make', để thực hiện quy tắc 'all', cố gắng để phù hợp với từng điều kiện tiên quyết với mục tiêu hiện có. Trong trường hợp này, mục tiêu là 'make-rule/%:', sử dụng '$*' để trích xuất chuỗi sau 'make-rule/' và sử dụng nó làm đối số trong công thức. Ví dụ, điều kiện tiên quyết đầu tiên sẽ được xuất hiện và mở rộng như thế này:

make-rule/lib/folder1: 
    cd lib/folder1 && $(MAKE) 
+0

Tôi tin rằng rằng 'tất cả: DIRS_CMD' thay vào đó sẽ là' tất cả: $ DIRS_CMD' vì lặp lại hiện tại không mở rộng đúng cách. Tôi đã nhận được một cái gì đó dọc theo dòng của 'make: *** Không có quy tắc để làm cho mục tiêu 'DIRS_CMD', cần thiết bởi 'tất cả'. Stop.' – jnt30

+0

Có thể. Tôi nghĩ rằng đối với tôi nó làm việc mà không có $ nhưng tôi không có nguồn và không thể xác minh. – Amiramix

0

gì nếu bạn muốn gọi mục tiêu khác nhau hơn so với tất cả trong một số không rõ các thư mục con?

Các Makefile sau đây sử dụng macro để tạo ra một chuyển tiếp giả mục tiêu cho một số thư mục con để áp dụng các mục tiêu đặt ra từ dòng lệnh để mỗi người trong số họ:

# all direct directories of this dir. uses "-printf" to get rid of the "./" 
DIRS=$(shell find . -maxdepth 1 -mindepth 1 -type d -not -name ".*" -printf '%P\n') 
# "all" target is there by default, same logic as via the macro 
all: $(DIRS) 

$(DIRS): 
    $(MAKE) -C [email protected] 
.PHONY: $(DIRS) 

# if explcit targets where given: use them in the macro down below. each target will be delivered to each subdirectory contained in $(DIRS). 
EXTRA_TARGETS=$(MAKECMDGOALS) 

define RECURSIVE_MAKE_WITH_TARGET 
# create new variable, with the name of the target as prefix. it holds all 
# subdirectories with the target as suffix 
$(1)_DIRS=$$(addprefix $(1)_,$$(DIRS)) 

# create new target with the variable holding all the subdirectories+suffix as 
# prerequisite 
$(1): $$($1_DIRS) 

# use list to create target to fullfill prerequisite. the rule is to call 
# recursive make into the subdir with the target 
$$($(1)_DIRS): 
     $$(MAKE) -C $$(patsubst $(1)_%,%,[email protected]) $(1) 

# and make all targets .PHONY 
.PHONY: $$($(1)_DIRS) 
endef 

# evaluate the macro for all given list of targets 
$(foreach t,$(EXTRA_TARGETS),$(eval $(call RECURSIVE_MAKE_WITH_TARGET,$(t)))) 

Hope this helps.Thực sự hữu ích khi đối phó với sự tương đương: make -j12 làm sạch tất cả trong một cây với makefiles có những mục tiêu này ... Như mọi khi: chơi với make là nguy hiểm, các cấp meta khác nhau của chương trình quá gần nhau, -)

Các vấn đề liên quan