2009-12-19 3 views
14

У меня есть каталог «lib» в главном каталоге моих приложений, который содержит произвольное количество подкаталогов, каждый из которых имеет свой собственный Makefile.make wildcard подкаталог цели

Я хотел бы иметь один Makefile в главном каталоге, который вызывает Makefile каждого подкаталога. Я знаю, что это возможно, если я вручную перечислил поддиры, но я хотел бы, чтобы это было сделано автоматически.

Я думал о чем-то вроде следующего, но он, очевидно, не работает. Обратите внимание, что у меня также есть чистые, тестовые и т. Д. Цели, поэтому%, вероятно, не очень хорошая идея.

LIBS=lib/* 

all: $(LIBS) 

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

Любая помощь приветствуется!

ответ

23

Ниже будет работать с GNU make:

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

Если там может быть что-то другое, а не каталоги в lib, можно альтернативно использовать:

LIBS=$(shell find lib -type d) 

Для решения проблемы множественных целей, вам может создавать специальные цели для каждого каталога, а затем отключать префикс для подстроки:

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, Благодарю. Работает как шарм! – Zed

+1

Почему вы не определили '$ (clean_LIBS)' как фальшивый? Например. '.PHONY: $ (LIBS) $ (clean_LIBS)', тогда я думаю, что вам не нужна «сила». –

+0

@ dma_k: Вы делаете отличную оценку. Я не тестировал ваше предложение, но ваши рассуждения звучат правильно для меня. На самом деле это звучит очень похоже на то, что предлагает руководство по GNU Make (см. Бит в поддирерах): http://www.gnu.org/software/make/manual/make.html#Phony-Targets – mrkj

2

Существует также способ перечисления подкаталогов с gmake только команды, без использования каких-либо команд оболочки:

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

Это будет список всех подкаталогов с задней '/'. Чтобы удалить его, вы можете использовать шаблон замены:

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

Тогда на самом деле создать правила исполняющих мейкфайлы в каждом подкаталоге вы можете использовать небольшую хитрость - фальшивую цель в несуществующей директории. Я думаю, что в этом случае пример покажет больше, чем каких-либо объяснений:

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 

В принципе, цель 'all' Выводит список всех вложенных каталогов в качестве предпосылок. Например, если LIB_DIRS содержал lib/folder1 lib/folder2 того разложение будет выглядеть следующим образом:

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

Then «делает» для того, чтобы выполнить правило 'all', пытается соответствовать каждой предпосылке с существующей целью. В этом случае цель равна 'make-rule/%:', которая использует '$*' для извлечения строки после 'make-rule/' и использует ее в качестве аргумента в рецепте. Например, первое условие будет соответствовать и расширена следующим образом:

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

Я верю что 'all: DIRS_CMD' должен вместо этого быть' all: $ DIRS_CMD', поскольку текущая итерация неправильно расширяет. Я получал что-то вроде «make: *** Нет правила, чтобы сделать цель« DIRS_CMD », необходимой« всем ». Стоп. ' – jnt30

+0

Это возможно. Я думаю, что для меня это работало без $, но у меня нет источников и я не могу проверить. – Amiramix

0

Что делать, если вы хотите вызвать различные цели, чем все неизвестное количество подкаталогов?

Следующая Makefile использует макросы таким образом, создать переадресации фиктивной мишень для ряда подкаталогов применить данную цель из командной строки для каждого из них:

# 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)))) 

Надеется, что это помогает.Действительно полезно при работе с паралелизмом: make -j12 очистить все на дереве с make-файлами, имеющими эти цели ... Как всегда: игра с маркой опасна, разные мета-уровни программирования слишком близки друг к другу, -)