【问题标题】:recursive makefile not building递归makefile未构建
【发布时间】:2013-03-21 17:26:38
【问题描述】:

我在不同的目录中有一堆 C 文件,我的递归 Makefile 出现make: nothing to be done for 'all' 错误;但是,如果我调整依赖项,我可以让它工作......但我不明白为什么我必须这样做。

这是我原来的 Makefile:

APP_DIRS=rescoco ressys resvm

.PHONY: all

all: $(APP_DIRS)

$(APP_DIRS):
    $(MAKE) --directory $@

clean:
    $(RM) *~

现在,如果我将我的线路:.PHONY 更改为 .PHONY: all $(APP_DIRS),它构建得很好。

另一种可能性是,如果我将行:$(APP_DIRS): 更改为 $(APP_DIRS): clean,它构建得很好。

(注意:删除 .PHONY 目标不会改变任何内容)


那么这里发生了什么? Makefile 是否试图告诉我我没有正确列出依赖项?我在想make 会做类似的事情:

  • 构建.PHONY我首先要构建all
  • 构建all我首先要构建$(APP_DIRS)
  • $(APP_DIRS) 没有先决条件,因此请为此执行命令(这将导致递归 make 运行)。

显然我错了;但为什么?


仅供参考,如果重要的话,我的文件结构如下:

Makefile       #top level makefile as seen above
/rescoco
    rescoco.c
    Makefile   #builds rescoco src and moves archive to ../lib directory
/ressys
    ressys.c
    Makefile   #same as above but for ressys
/resvm
    resvm.c
    Makefile   #same as above but for resvm
/lib

而我的构建命令就是make。当我使用make -nmake -n all 运行时,我根本没有输出:

:~/proj$ make -n all
make: Nothing to be done for 'all'.
:~/proj$ 

【问题讨论】:

  • 你用的是什么命令?全部搞定?
  • 运行 make -n 看看到底发生了什么。我还使用变量打印来确保一切正常。
  • @75inchpianist - 只是make,但make all 给出了相同的结果。 (见编辑)
  • 好吧,我想我一秒搞错了
  • @Mellowcandle - make -n 什么都不转储。这是一个非常简单的makefile,转储不多。我尝试添加$(warning $(APP_DIRS)) 作为构建all 的步骤,它只是报告了rescoco ressys resvm,这是我所期望的......关于转储的任何其他建议?

标签: c recursion makefile


【解决方案1】:

首先你应该知道的事情:

  • 如果您将目录作为依赖项,make 将考虑构建目标(即执行此类目录目标的配方),前提是目录的修改时间戳得到更新。 仅当您在目录中添加新文件而不是在目录中修改文件时才会发生这种情况。在子目录中添加文件不会更改目录的时间戳。
  • PHONY 目标应在执行此类目标时使用,但不会创建具有目标名称的文件。换句话说,无论文件是否已存在,您都希望 make 执行规则。

所以你的 Makefile 基本上只告诉这个:

  • 要构建目标all,我需要构建$(APP_DIRS)。由于 all 是 PHONY 目标,我将始终执行 all 的配方。
  • $(APP_DIRS) 不是 PHONY 目标并且没有任何依赖关系。所以*只有当 $(APP_DIRS) 不存在(即文件或目录)时,我才会执行配方,否则我不会为这个目标做任何事情。
  • clean 没有先决条件,也不是PHONY,因此我希望仅在 make 显式调用时(从命令行或其他 Makefile)执行此规则。另外clean 不是PHONY,所以我希望配方在执行后创建一个名为clean 的文件(这对您的情况不正确)

因此将.PHONY 行更改为:

.PHONY: all $(APP_DIRS)

使 Makefile 始终执行 $(APP_DIRS) 的配方。

因此,如果您希望 make 始终遍历所有 $(APP_DIRS) 目录并对其再次调用 make,则需要将 $(APP_DIRS) 添加到 .PHONY,这使得 $(APP_DIRS) 成为 PHONY 目标,并执行配方,而不管文件/目录的时间戳(如果存在)。

对于您的特定用例,我认为这是您应该使用的 Makefile:

APP_DIRS=rescoco ressys resvm

.PHONY: all clean $(APP_DIRS)

all: $(APP_DIRS)

$(APP_DIRS):
    $(MAKE) --directory $@

clean:
    $(RM) *~

奖励:

  • $(APP_DIRS): 更改为$(APP_DIRS): clean 意味着$(APP_DIRS) 依赖于clean 目标。
  • 虽然clean 没有被标记为PHONY,但是make 在当前目录中看不到一个名为clean 的文件。所以它继续并尝试执行clean 的配方。
  • 由于构建了 $(APP_DIRS) 的依赖项(即clean),这使得 Makefile 执行构建 $(APP_DIRS) 的配方。

这给我们带来了一个有趣的观察结果: - 任何依赖于 PHONY 目标的目标总是会被重建(即会执行配方)。

使用这个简单的 Makefile:

all: target1

target1: target2
    @echo "$@"
    @touch $@

target2: target3
    @echo "$@"
    @touch $@

target3:
    @echo "$@"

.PHONY: all target3

我第一次运行make,我看到了这个输出:

target3
target2
target1

在此之后,将创建文件 target1target2。即使那样,如果我再次运行make,我会看到输出:

target3
target2
target1

如您所见,PHONY 依赖项向上传播,而不是反向传播。 target2 被重建只是因为 target3 是假冒,target1 被重建只是因为 target2 被重建。

【讨论】:

  • +1 好的,这很有意义。我知道如果某个东西被列为.PHONY,它将始终被构建,我认为这意味着如果我将all 列为.PHONY,它及其所有依赖项将始终被构建...但这不会传播。对吗?
  • @Mike - PHONY 依赖项会沿依赖项链向上传播,而不是相反。看看我最后添加的示例。
【解决方案2】:

您正在使用目录列表定义一个名为“APP_DIRS”的变量。没关系。

然后你做

$(APP_DIRS): make blah blah, 本质上等同于rescoco ressys resvm: make blah blah

这显然是无效的。

所以你需要假装你的 $(APP_DIRS) 是一个变量,而不是一个目标名称,这似乎是你使用它的原因。

话虽如此,想想为什么 .PHONY: all $(APP_DIRS) 有效

【讨论】:

    【解决方案3】:

    这是你可以做到的,如果你不想要它,只需删除通配符。
    make wildcard subdirectory targets

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多