Makefile 在每个子目录中运行make

Makefile 在每个子目录中运行make,makefile,gnu-make,Makefile,Gnu Make,我有一个目录(root\u dir),其中包含许多子目录(subdir1,subdir2,…) 我想在root\u dir中的每个目录中运行make,使用放置在其中的Makefile。 (显然,假设每个子目录…都有自己的Makefile) 因此,基本上有两个问题: 如何(自动)获取Makefile中的目录列表 如何为make文件中的每个目录运行make 据我所知,为了在特定目录中运行make,我需要执行以下操作: $(MAKE) -C subdir 试试这个: SUBDIRS = foo ba

我有一个目录(
root\u dir
),其中包含许多子目录(
subdir1,subdir2,…

我想在
root\u dir
中的每个目录中运行
make
,使用放置在其中的Makefile。 (显然,假设每个
子目录…
都有自己的Makefile)

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

  • 如何(自动)获取Makefile中的目录列表
  • 如何为make文件中的每个目录运行
    make
  • 据我所知,为了在特定目录中运行
    make
    ,我需要执行以下操作:

    $(MAKE) -C subdir
    
    试试这个:

    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_代码不是需要修改的文件 构建,并且-C标志指示目录中的更改(相当于 在调用make之前运行cd代码)。您可以使用相同的方法 用于调用代码生成文件中的其他目标

    例如:

    clean:
       $(MAKE) -C $(CODE_DIR) clean
    
    my_target: my_subdirectory/my_prerequisite
            'my_subdirectory/my_prerequisite' > 'my_target'
    

    在一个配方中的for循环中进行子生成时,会出现各种问题。创建多个子目录的最佳方法如下:

    SUBDIRS := $(wildcard */.)
    
    all: $(SUBDIRS)
    $(SUBDIRS):
            $(MAKE) -C $@
    
    .PHONY: all $(SUBDIRS)
    
    include prorab.mk
    
    $(eval $(prorab-build-subdirs))
    
    (只是要指出这是GNU make specific;您没有提到对您正在使用的make版本的任何限制)

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

    TOPTARGETS := all clean
    
    SUBDIRS := $(wildcard */.)
    
    $(TOPTARGETS): $(SUBDIRS)
    $(SUBDIRS):
            $(MAKE) -C $@ $(MAKECMDGOALS)
    
    .PHONY: $(TOPTARGETS) $(SUBDIRS)
    

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

    关于github的一些信息:

    基本上,使用prorab调用子目录中的所有makefile如下所示:

    SUBDIRS := $(wildcard */.)
    
    all: $(SUBDIRS)
    $(SUBDIRS):
            $(MAKE) -C $@
    
    .PHONY: all $(SUBDIRS)
    
    include prorab.mk
    
    $(eval $(prorab-build-subdirs))
    

    由于我不知道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)
    

    在MadScientist的回答之后,为了使子目录中的所有单个目标从顶层可用(您需要定义
    SUBDIRS
    变量,以便使用以下代码片段–您可以使用MadScientist的回答):


    例如,使用上面的代码,您可以运行

    make foo/my_program
    

    此外,通过粘贴上面的代码,您甚至可以使用子目录中的单个目标作为顶层目标的先决条件。例如:

    clean:
       $(MAKE) -C $(CODE_DIR) clean
    
    my_target: my_subdirectory/my_prerequisite
            'my_subdirectory/my_prerequisite' > 'my_target'
    

    …在上面的示例中,从顶层启动
    make my_target
    将首先构建
    my_子目录/my_prerequisite
    程序,然后运行后者来构建
    my_target
    文件。

    这是另一种方法
    .PHONY
    是GNU特有的特性,可用于强制
    make
    递归到每个子目录中。但是,一些非GNU版本的
    make
    不支持
    .PHONY
    ,因此另一种选择是使用

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

    如果规则没有先决条件或配方,以及规则的目标 是一个不存在的文件,然后使想象这个目标已经存在 每当运行其规则时更新。这意味着所有目标 根据这一点,他们的食谱将始终运行

    举例说明:

    clean: FORCE
            rm $(objects)
    FORCE:
    
    在这里,目标“力”满足特殊条件,因此 依赖于它的目标清理被强制运行其配方。有 “FORCE”这个名字没有什么特别之处,但它通常是一个名字 用这种方式

    如您所见,以这种方式使用“FORCE”与使用 “虚假:干净”

    使用“.PHONY”更明确、更有效。然而,其他 make的版本不支持“.PHONY”;因此,"力"出现在许多领域 生成文件。见假目标

    下面是一个将
    make
    递归到每个子目录的最小示例,每个子目录大概都包含一个
    Makefile
    。如果只运行
    make
    ,则只处理第一个子目录,它是不确定的。您还可以运行
    make subdir1 subdir2…

    #注册项目根目录中的所有子目录。
    子项:=$(通配符*/)
    #将'make'递归到每个子目录中。
    $(细分曲面):强制
    $(MAKE)-C$@
    #没有先决条件和配方的目标,并且没有名为“FORCE”的文件。
    #'make'将始终运行此目标以及依赖它的任何其他目标。
    部队:
    
    下面是另一个顶级虚假目标的例子:
    all
    clean
    。请注意,从命令行通过
    $(MAKECMDGOALS)
    传递的
    all
    clean
    目标分别由每个子目录的
    all
    clean
    目标处理

    #注册项目根目录中的所有子目录。
    子项:=$(通配符*/)
    #顶级假目标。
    全部清除:$(细分曲面)强制
    #类似于:
    #.骗子:都是干净的
    #全部清洁:$(细分曲面)
    #GNU的.PHONY目标更有效,因为它显式声明非文件。
    #将'make'递归到每个子目录中
    #传递命令行中指定的目标(如果有)。
    $(细分曲面):强制
    $(MAKE)-C$@$(MAKECMDGOALS)
    #部队目标。
    部队:
    
    您还可以在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)
    

    实际上我不想在Makefile中有一个目录列表,如果我还想要干净的目标呢?为什么$(子目录)在.PHONY中?这是因为目录修改日期/时间无关紧要吗?我有这个问题,因为目录不是伪造的。PHONY应该用于假目标。
    。PHONY
    用于您希望在每次运行makefile时生成的目标,而不管它是否存在或是否过期