Сделать подкаталог подстановочных знаков
У меня есть каталог "lib" в главном каталоге моих приложений, который содержит произвольное количество подкаталогов, каждый из которых имеет свой собственный Makefile.
Я хотел бы иметь один Makefile в основном каталоге, который вызывает каждый подкаталог Makefile. Я знаю, что это возможно, если я вручную перечислю поддиры, но я бы хотел, чтобы это было сделано автоматически.
Я думал о чем-то вроде следующего, но он явно не работает. Обратите внимание, что у меня также есть чистые, тестовые и т.д. Цели, поэтому%, вероятно, не очень хорошая идея.
LIBS=lib/*
all: $(LIBS)
%:
(cd [email protected]; $(MAKE))
Любая помощь приветствуется!
Ответы
Ответ 1
Следующее будет работать с 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
Ответ 2
Существует также способ перечисления подкаталогов только с командами gmake, без использования каких-либо команд оболочки:
test:
@echo $(filter %/, $(wildcard lib/*/))
Здесь перечислены все подкаталоги с конечным '/'
. Чтобы удалить его, вы можете использовать шаблон замены:
subdirs = $(filter %/, $(wildcard lib/*/))
test:
@echo $(subdirs:%/=%)
Затем для фактического создания правил, выполняющих make файлы в каждом подкаталоге, вы можете использовать небольшой трюк - фальшивую цель в несуществующей директории. Я думаю, в этом случае пример расскажет больше, чем любое объяснение:
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
В принципе, target 'all'
перечисляет все подкаталоги в качестве предварительных условий. Например, если LIB_DIRS
содержит lib/folder1 lib/folder2
, то расширение будет выглядеть так:
all: make-rule/lib/folder1 make-rule/lib/folder2
Затем "make", чтобы выполнить правило 'all'
, пытается сопоставить каждое предварительное условие с существующей целью. В этом случае цель 'make-rule/%:'
, которая использует '$*'
для извлечения строки после 'make-rule/'
и использует ее как аргумент в рецепте. Например, первое предварительное условие будет согласовано и расширено следующим образом:
make-rule/lib/folder1:
cd lib/folder1 && $(MAKE)
Ответ 3
Что делать, если вы хотите вызывать разные цели, а не все в неизвестном числе подкаталогов?
Следующий 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 файлами, имеющими эти цели... Как всегда: игра с make опасна, разные мета-уровни программирования слишком близки друг к другу, -)