在每个子目录中运行make。

89

我有一个目录(root_dir),其中包含许多子目录(subdir1, subdir2, ...)。

我希望在每个子目录中使用放置在其中的Makefile运行make。 (显然假设每个subdir...内部都有自己的Makefile)。

因此,基本上有两个问题:

  1. 如何自动获取Makefile中的目录列表?
  2. 如何为每个目录内的make文件运行make

据我所知,要在特定目录中运行make,需要执行以下操作:

$(MAKE) -C subdir
9个回答

142

在单个配方中使用 for 循环执行子 make 存在诸多问题。最佳的多个子目录处理方法如下:

SUBDIRS := $(wildcard */.)

all: $(SUBDIRS)
$(SUBDIRS):
        $(MAKE) -C $@

.PHONY: all $(SUBDIRS)
< p >(只是提醒一下,这是特定于GNU Make的;您没提到您使用的make版本有任何限制)。 < p >< em> ETA 这里是一个支持多个顶层目标的版本。
TOPTARGETS := all clean

SUBDIRS := $(wildcard */.)

$(TOPTARGETS): $(SUBDIRS)
$(SUBDIRS):
        $(MAKE) -C $@ $(MAKECMDGOALS)

.PHONY: $(TOPTARGETS) $(SUBDIRS)

10
如果我也想要干净的目标,该怎么办? - Claudio
2
为什么$(SUBDIRS)在.PHONY中?这是因为目录的修改日期/时间无关紧要吗?我有这个问题,因为这些目录不是伪造的。.PHONY应该是用于伪目标的。 - KRoy
1
.PHONY 是用于那些你希望每次运行 makefile 时都构建的目标,无论它是否存在、是否过期。 - MadScientist
2
@shuva 无论子目录是否已更改,您最有可能希望在子目录中运行makefile,因为该makefile可能具有自己的虚假目标。 - osvein
1
(1) 无法在多个子目录中使用并行构建,(2) 要么在失败时不会退出,要么不正确支持 -k。可能还有其他我忘记的问题。 - MadScientist
显示剩余6条评论

12

试试这个:

SUBDIRS = foo bar baz

subdirs:
    for dir in $(SUBDIRS); do \
        $(MAKE) -C $$dir; \
    done

这可能对你有帮助:链接

编辑:你也可以这样做:

最简单的方法是:

CODE_DIR = code

.PHONY: project_code

project_code:
       $(MAKE) -C $(CODE_DIR)

.PHONY规则的意思是project_code不是需要构建的文件,-C标志表示更改目录(等同于在调用make之前运行cd code)。您可以使用相同的方法调用代码Makefile中的其他目标。

例如:

clean:
   $(MAKE) -C $(CODE_DIR) clean

源代码


2
实际上,我不想在Makefile中列出目录列表。 - Alex

9
这是另一种方法,与MadScientist的答案相似.PHONY是GNU特有的功能,可用于强制make递归到每个子目录。然而,一些非GNU版本的make不支持.PHONY,因此一种替代方法是使用强制目标

4.7 Rules without Recipes or Prerequisites

If a rule has no prerequisites or recipe, and the target of the rule is a nonexistent file, then make imagines this target to have been updated whenever its rule is run. This implies that all targets depending on this one will always have their recipe run.

An example will illustrate this:

clean: FORCE
        rm $(objects)
FORCE:

Here the target ‘FORCE’ satisfies the special conditions, so the target clean that depends on it is forced to run its recipe. There is nothing special about the name ‘FORCE’, but that is one name commonly used this way.

As you can see, using ‘FORCE’ this way has the same results as using ‘.PHONY: clean’.

Using ‘.PHONY’ is more explicit and more efficient. However, other versions of make do not support ‘.PHONY’; thus ‘FORCE’ appears in many makefiles. See Phony Targets.

以下是一个最小化的示例,它将make递归到每个子目录中,每个子目录中都包含一个Makefile文件。如果您只运行make,则仅处理第一个子目录(这是不确定的)。您还可以运行make subdir1 subdir2 ...
# Register all subdirectories in the project's root directory.
SUBDIRS := $(wildcard */.)

# Recurse `make` into each subdirectory.
$(SUBDIRS): FORCE
        $(MAKE) -C $@

# A target without prerequisites and a recipe, and there is no file named `FORCE`.
# `make` will always run this and any other target that depends on it.
FORCE:

以下是与顶级虚拟目标相关的另一个示例:allclean。请注意,通过$(MAKECMDGOALS)从命令行传递的allclean目标分别由每个子目录的allclean目标处理。

# Register all subdirectories in the project's root directory.
SUBDIRS := $(wildcard */.)

# Top-level phony targets.
all clean: $(SUBDIRS) FORCE
# Similar to:
# .PHONY: all clean
# all clean: $(SUBDIRS)
# GNU's .PHONY target is more efficient in that it explicitly declares non-files.

# Recurse `make` into each subdirectory
# Pass along targets specified at command-line (if any).
$(SUBDIRS): FORCE
        $(MAKE) -C $@ $(MAKECMDGOALS)

# Force targets.
FORCE:

感谢您的建议。我不得不在我的Mac OS(Ventura 13.5)上使用它才能使其正常工作。 - Sarfraaz Ahmed

4
你也可以在Makefile中定义一个函数(当然,在每个子目录中你需要有额外的makefile)。这是依赖于shell的,但是非常实用:
define FOREACH
    for DIR in packages/*; do \
        $(MAKE) -C $$DIR $(1); \
    done
endef

.PHONY: build
build:
    $(call FOREACH,build)

.PHONY: clean
clean:
    $(call FOREACH,clean)

.PHONY: test
test:
    $(call FOREACH,test)

3

在MadScientist的答案之后只是锦上添花,以使子目录中的所有单个目标从顶层可用(您需要定义SUBDIRS变量才能使用以下代码片段 - 您可以使用MadScientist的答案):

# Make all the individual targets in the sub-directories available from the top
# level; as in, for instance, `make foo/my_program` or `make bar/clean`
$(foreach __dir__,$(SUBDIRS),$(__dir__)/%):
        @$(MAKE) -C '$(@D)' '$(@F)'

使用上述代码,您可以运行例如:
make foo/my_program

或者

make bar/clean

此外,通过粘贴上面的代码,您甚至可以使用子目录中的个别目标作为顶级目标的先决条件。例如:
my_target: my_subdirectory/my_prerequisite
        'my_subdirectory/my_prerequisite' > 'my_target'

通过以上的例子,从顶层目录中运行 make my_target 命令将首先构建 my_subdirectory/my_prerequisite 程序,然后再构建 my_target 文件。


2

有一个名为prorab的库,可用于GNU make,支持在子目录中包含独立的makefile。

关于github上的一些信息: https://github.com/cppfw/prorab/blob/master/wiki/HomePage.adoc

基本上,使用prorab调用所有子目录中的makefile看起来像这样:

include prorab.mk

$(eval $(prorab-build-subdirs))

2

由于我不知道“MAKECMDGOALS”变量,并忽视了MadScientist具有多个顶级目标的自身实现,因此我编写了另一种实现。也许有人会觉得它有用。

SUBDIRS := $(wildcard */.)

define submake
        for d in $(SUBDIRS);                  \
        do                                    \
                $(MAKE) $(1) --directory=$$d; \
        done
endef

all:
        $(call submake,$@)

install:
        $(call submake,$@)

.PHONY: all install $(SUBDIRS)

我不知道,我相信 https://dev59.com/f2Mm5IYBdhLWcg3wIsbj#tBDsnYgBc1ULPQZFMiqr 的意思是使用并行构建时for循环会有问题。请参见下面的答案。 - netskink

1

关于https://stackoverflow.com/posts/17845120/revisions的参考。

这是我从那篇文章中学到的内容。

顶层Makefile

# set the default goal.
# I want the default to really just dump contents of dirs
# as a stub.  For instance, I don't want it to
# push code or
.DEFAULT_GOAL := deploy

TOPTARGETS := all clean

SUBDIRS := docs src 

$(TOPTARGETS): $(SUBDIRS)
$(SUBDIRS):
    echo "make arg is" $(MAKECMDGOALS)
    $(MAKE) -C $@ $(MAKECMDGOALS)




SUBCLEAN = $(addsuffix .clean,$(SUBDIRS))

clean: $(SUBCLEAN)

$(SUBCLEAN): %.clean:
    $(MAKE) -C $* clean

deploy:
    echo do deploy stub

这个 Makefile 目录中的 src/docs/ 都有相应的 Makefile

这里是文档设置的示例:

# set the default goal.
.DEFAULT_GOAL := list_docs



list_docs:
    ls -l


clean:
    echo "docs: make clean"
    -rm "*.backup"


0

我和其他答案有所不同,因为我不想定义每个可能的制作目标。

SUBDIRS := $(patsubst %/,%,$(wildcard */))

.PHONY: all $(MAKECMDGOALS) $(SUBDIRS)
$(MAKECMDGOALS) all: $(SUBDIRS)

$(SUBDIRS):
        $(MAKE) -C $@ $(MAKECMDGOALS)

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