使通配符子目录成为目标

24

我的应用程序主目录中有一个名为“lib”的文件夹,其中包含任意数量的子文件夹,每个子文件夹都有自己的Makefile。

我希望在主目录中有一个单独的Makefile,它调用每个子目录的Makefile。如果我手动列出子目录,我知道这是可能的,但我希望自动完成。

我考虑了以下的方法,但显然它不起作用。注意,我还有清理、测试等目标,因此%可能根本不是一个好主意。

LIBS=lib/*

all: $(LIBS)

%:
  (cd $@; $(MAKE))

非常感谢您的帮助!

3个回答

35
以下内容适用于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

1
为什么你没有将 $(clean_LIBS) 定义为虚拟目标呢?例如 .PHONY: $(LIBS) $(clean_LIBS),这样的话我认为你就不需要使用 force 了。 - dma_k
@dma_k:你说得很好。我还没有测试过你的建议,但是你的推理听起来对我来说是正确的。实际上,它听起来非常像GNU Make手册在这里建议的内容(请参见子目录部分):http://www.gnu.org/software/make/manual/make.html#Phony-Targets - mrkj

3

还有一种通过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

然后,为了执行规则'all','make'会尝试将每个前提与现有目标进行匹配。在这种情况下,目标是'make-rule/%:',它使用'$*'提取在'make-rule/'之后的字符串,并将其用作配方的参数。例如,第一个前提将被匹配和扩展如下:

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

我认为 all: DIRS_CMD 应该改为 all: $DIRS_CMD,因为当前的迭代没有正确扩展。我得到了类似于 make: *** No rule to make target 'DIRS_CMD', needed by 'all'. Stop. 的东西。 - jnt30
可以的。我认为对我来说没有$也可以,但是我没有源代码,无法验证。 - Greg

1
如果你想调用未知数量子目录中的不同目标,该怎么办?下面的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))))

希望这可以帮到你。在处理并行性时非常有帮助:在具有这些目标的makefile树中使用“make -j12 clean all”...像往常一样:玩弄make是危险的,不同的元编程级别太过接近。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接