在makefile中使用g++和-MMD自动生成依赖

Posted

技术标签:

【中文标题】在makefile中使用g++和-MMD自动生成依赖【英文标题】:Using g++ with -MMD in makefile to automatically generate dependencies 【发布时间】:2012-08-05 00:34:36 【问题描述】:

我知道以下 makefile 将使预处理器自动生成依赖项(在 .d 文件中)并将它们包含在 makefile 中(因为我的课程笔记是这样说的),因此它们不必自动维护。 -MMD 标志对此负责。我不明白的是: .d 文件是在什么时候生成的?甚至没有任何使用$CXXFLAGS 的命令。大概,像$CXX $CXXFLAGS -c x.C -o x.o 这样的命令会被make 自动推导出每个目标文件,但是如果这些是生成.d 文件的命令,我们不是已经过了知道xo 的依赖关系的地步吗,哟如果我们只通过执行生成这些 .o 文件的命令来了解它们,那么 zo 可能是相关的吗? (假设有 .h 文件,如果让其自行推断规则或其他东西,makefile 将忽略这些文件。)

CXX = g++                     # compiler
CXXFLAGS = -g -Wall -MMD      # compiler flags
OBJECTS = x.o y.o z.o         # object files forming executable
DEPENDS = $OBJECTS:.o=.d    # substitutes ".o" with ".d"
EXEC = a.out                  # executable name

$EXEC : $OBJECTS          # link step
    $CXX $OBJECTS -o $EXEC

-include $DEPENDS           # copies files x.d, y.d, z.d (if they exist)

【问题讨论】:

您的最后一行-include 将文件复制到确切的位置? 【参考方案1】:

据推测,像$CXX $CXXFLAGS -c x.C -o x.o 这样的命令将由 make 自动为每个目标文件推导出来,但如果这些是生成 .d 文件的命令,我们不是已经过了了解依赖关系的地步吗?如果我们只通过执行生成这些 .o 文件的命令来了解 xo、yo 和 zo,它们可能是相关的?

你说得对。第一次运行 Makefile 时,依赖项不存在。

但这无关紧要 - 仅当 .o 文件已经存在并且您更改了 .h 文件时才需要依赖信息。第一次运行 Make 时,无论如何都需要构建所有 .o 文件,同时生成 .d 文件。

之后,.d 文件会给出依赖信息。如果一个头文件改变了,依赖信息会告诉 Make 哪些 .o 文件需要重建。如果源文件发生变化,总是需要重新构建.o,同时会生成更新的依赖信息。

【讨论】:

【参考方案2】:

如果您想知道背后的 makefile 正在做什么,请使用 -p 标志,并将输出重定向到一个文件,因为它有很多东西。

make -p foo > bar 将转储make foo 的所有变量值和规则,然后查看bar 将显示为隐式规则运行的命令。在您的情况下,它会向您显示.cpp.o%.o: %.cpp(奇怪的是它们都在那里)规则将调用$(COMPILE.cpp),它解析为$(COMPILE.cc),它解析为$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c。它有一个有趣的属性,您可以将内容添加到 CXXFLAGS 或 CPPFLAGS 以将它们添加到您的 .cpp.o 编译中,但不是 CFLAGS,它仅由 .c.o 规则使用,这有点道理,因为您可能想要您的 C 文件和 C++ 文件的处理方式不同(CC 和 CXX 通常也设置为不同的编译器)。

【讨论】:

【参考方案3】:

是的,你是对的,如果你删除了依赖文件,但将目标文件保留在原处,那么 make 将使用不完整的依赖信息运行,并且可能无法重新构建标头已更改的目标文件。

Make 也可能拒绝构建 - 当依赖文件引用您已删除的标头时会发生这种情况(显然不再从任何其他来源引用)。由于 Make 在编译之前不知道如何重建依赖文件,它所能做的就是报告缺少的依赖项(然后显而易见的操作是删除依赖项文件,导致上面的第一个条件)。

对此的唯一答案是纪律:删除依赖文件时,始终删除目标文件。您可以使用clean 目标来帮助解决此问题。

clean::
        $(RM) *.o *.d

或者另外,告诉Make如何创建依赖文件:

%.dep: %.cc
        $(CXX) -MM $(CPPFLAGS) $< | sed -e 's,\($*\)\.o[ :]*,\1.o $@: ,g' > $@

sed 命令确保依赖项本身依赖于与目标文件相同的源)。

【讨论】:

实际上,在 GCC 文档中还有另一个“答案”:“-MP:此选项指示 CPP 为除主文件之外的每个依赖项添加虚假目标,从而导致每个依赖项都依赖什么都没有。如果您删除头文件而不更新 Makefile 以匹配,这些虚拟规则可以解决 make 给出的错误。 (在为您搜索时,我刚刚找到了我正在寻找的东西,这是我在第一次通过时忽略的 -MT 参数。谢谢,我猜。:))

以上是关于在makefile中使用g++和-MMD自动生成依赖的主要内容,如果未能解决你的问题,请参考以下文章

从 MSVC 输出生成 Makefile 依赖项

从MSVC输出生成Makefile依赖项

gcc -MMD 如何写入 .d 文件?

是否可以在 Makefile 的单次传递中使用 gcc/g++/nvcc 自动依赖项 -M 而无需将依赖项保存到文件中?

如何使用CMAKE生成makefile文件

Makefile 只修改了.h头文件,编译为什么不起作用?