我的应用程序主目录中有一个“lib”目录,其中包含任意数量的子目录,每个子目录都有自己的 Makefile。
我想在主目录中有一个 Makefile,它调用每个子目录的 Makefile。我知道如果我手动列出子目录,这是可能的,但我希望它自动完成。
我在想类似下面的东西,但它显然不起作用。请注意,我也有 clean、test 等目标,因此 % 可能根本不是一个好主意。
LIBS=lib/*
all: $(LIBS)
%:
(cd $@; $(MAKE))
任何帮助表示赞赏!
我的应用程序主目录中有一个“lib”目录,其中包含任意数量的子目录,每个子目录都有自己的 Makefile。
我想在主目录中有一个 Makefile,它调用每个子目录的 Makefile。我知道如果我手动列出子目录,这是可能的,但我希望它自动完成。
我在想类似下面的东西,但它显然不起作用。请注意,我也有 clean、test 等目标,因此 % 可能根本不是一个好主意。
LIBS=lib/*
all: $(LIBS)
%:
(cd $@; $(MAKE))
任何帮助表示赞赏!
以下将适用于 GNU make:
LIBS=$(wildcard lib/*)
all: $(LIBS)
.PHONY: force
$(LIBS): force
cd $@ && 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 $@
$(clean_LIBS): force
echo make -C $(patsubst clean_%,%,$@) clean
还有一种仅使用 gmake 命令列出子目录的方法,而不使用任何 shell 命令:
test:
@echo $(filter %/, $(wildcard lib/*/))
这将列出所有带有尾随的子目录'/'。要删除它,您可以使用替换模式:
subdirs = $(filter %/, $(wildcard lib/*/))
test:
@echo $(subdirs:%/=%)
然后,要在每个子目录中实际创建执行 makefile 的规则,您可以使用一个小技巧 - 一个不存在的目录中的虚假目标。我认为在这种情况下,一个例子比任何解释都更能说明问题:
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
然后“make”为了执行 rule 'all',尝试将每个先决条件与现有目标匹配。在这种情况下,目标是'make-rule/%:',它用于'$*'在之后提取字符串'make-rule/'并将其用作配方中的参数。例如,第一个先决条件将像这样匹配和扩展:
make-rule/lib/folder1:
cd lib/folder1 && $(MAKE)
如果您想在未知数量的子目录中调用不同的目标怎么办?
以下 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 $@
.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)_%,%,$$@) $(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 clean all in a tree with makefiles have these targets...一如既往:玩make很危险,不同的元级编程太接近了,-)