在每个子目录中运行make

时间:2013-07-24 12:51:54

标签: makefile gnu-make

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

我想在make中的每个目录中运行root_dir,并使用放置在其中的Makefile。 (显然假设每个subdir...都有自己的Makefile。)

所以基本上有两个问题:

  1. 如何在Makefile中自动获取目录列表?
  2. 如何为make文件中的每个目录运行make?
  3. 我知道为了在特定目录中运行make,我应该注意以下事项:

    $(MAKE) -C subdir
    

7 个答案:

答案 0 :(得分:66)

在单个配方中的for循环中执行sub-make会有各种问题。执行多个子目录的最佳方法是这样的:

SUBDIRS := $(wildcard */.)

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

.PHONY: all $(SUBDIRS)

(只是要指出这是GNU make具体的;你没有提到对你正在使用的make版本的任何限制。)

ETA 这是一个支持多个顶级目标的版本。

TOPTARGETS := all clean

SUBDIRS := $(wildcard */.)

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

.PHONY: $(TOPTARGETS) $(SUBDIRS)

答案 1 :(得分:4)

试试这个:

SUBDIRS = foo bar baz

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

这可能会对您有所帮助link

编辑:你也可以这样做:

最简单的方法是:

CODE_DIR = code

.PHONY: project_code

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

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

例如:

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

Source

答案 2 :(得分:1)

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

关于github的一些信息:https://github.com/igagis/prorab/blob/master/wiki/HomePage.md

基本上,使用 prorab 调用子目录中的所有makefile都是这样的:

include prorab.mk

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

答案 3 :(得分:1)

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

# 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文件。

答案 4 :(得分:1)

这是MadScientist's answer的另一种方法。 .PHONY是GNU特定的功能,可用于强制make递归到每个子目录中。但是,make的某些非GNU版本不支持.PHONY,因此可以选择force target

  

4.7没有配方或先决条件的规则

     

如果规则没有先决条件或配方,并且没有规则的目标   是一个不存在的文件,则使该目标成为   每当运行其规则时更新。这意味着所有目标   取决于这一点,总是可以运行他们的配方。

     

一个例子将说明这一点:

clean: FORCE
        rm $(objects)
FORCE:
     

目标“ FORCE”满足特殊条件,因此   依赖于它的目标清除将被强制运行其配方。有   “ FORCE”这个名字没什么特别的,但这通常是一个名字   用这种方式。

     

如您所见,以这种方式使用“ FORCE”具有与使用相同的结果   ‘.PHONY:干净’。

     

使用“ .PHONY”更为明确和有效。但是,其他   make版本不支持“ .PHONY”;因此,“ FORCE”出现在许多   生成文件。参见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。请注意,从命令行通过all传递的clean$(MAKECMDGOALS)目标分别由每个子目录的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:

答案 5 :(得分:1)

您还可以在Makefile中定义一个函数(当然,您当然也需要在每个子目录中另外一个makefile)。这是外壳相关的,但可能有用:

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)

答案 6 :(得分:0)

由于我不了解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)