为啥在 makefile 中不生成依赖项?

Posted

技术标签:

【中文标题】为啥在 makefile 中不生成依赖项?【英文标题】:Why aren't dependencies generated inside makefiles?为什么在 makefile 中不生成依赖项? 【发布时间】:2015-04-19 04:54:41 【问题描述】:

我最近学习了如何使用 Makefile,发现 GCC/G++ 会为你生成依赖:

$ g++ -MM file.cpp
file.o: file.cpp file.h

然后我认为显而易见的做法是使用它直接在文件中生成依赖项而不创建依赖项文件:

CXX = g++

SRCS = $(wildcard src/*.cpp)
OBJS = $(SRCS:.cpp=.o)
OCT = $(CXX -MM $(SRCS))
OBJDIR = obj
CPPFLAGS = -Wall -I/usr/local/include -L/usr/local/lib -lGLEW -lglfw -lGL

.PHONY: all
all: $(OBJS)
    $(CXX) $(CPPFLAGS) $(OBJS) -o output

$(OCT)

.PHONY: clean
clean:
    rm -f obj/*

出于某种原因,我从未见过其他人这样做;他们总是生成一个依赖文件。这个系统有问题吗?在我的情况下是的 - 对象不会转到 OBJDIR,它们会转到源文件的位置。我相信这可以解决。如果有人知道我该如何解决这个问题以及为什么通常会生成依赖文件,请告诉我。

【问题讨论】:

您的方式(至少部分)编译文件两次。 @CarlNorum 怎么样?我只看到一个编译 - $(OCT) 编译。 是的,但是您也需要将它们编译为目标文件,对吗?也许我忘记了-MM 做了什么。 它还需要在每次 make 调用的所有源文件上运行预处理,这对于您没有进行任何逻辑上需要重建的更改的情况可能是不可取的。 @CarlNorum 是的,我通常在与实际编译相同的 cmd 中使用我的。到那时,您已经确定需要重新编译该源文件;现在是更新 deps 的好时机,因为过时的源文件可能已经修改了该源文件的 #include 列表。我同意你的评价。 【参考方案1】:

嗯,人们不这样做的第一个原因是它不可能做到:如果你试图让你的建议在现实生活中发挥作用,你就会看到。例如,您的示例根本没有做任何事情。这个:

OCT = $(CXX -MM $(SRCS))

(我假设您的意思是$($(CXX) -MM $(SRCS)),但无论哪种方式都无关紧要)正在将对名为CXX -MM $(SRCS)的make变量的引用放入变量OCT:您可能认为它正在使用shell 命令调用语法$(...) 但这是一个makefile,而不是一个shell 脚本。所以当你写的时候:

$(OCT)

试图查找显然不存在的 make 变量,因此它扩展为空字符串并且什么也没有发生。如果你真的尝试通过触摸标题等来测试你的 makefile,你会发现没有任何东西被重建。

你怎么能做到这一点?你不能这样做。您可以像这样更改变量分配:

OCT = $(shell $(CXX) -MM $(SRCS))

这实际上会运行编译器,这会让你朝着正确的方向前进,但是 shell 函数的结果会将所有换行符更改为空格,所以这样:

$(OCT)

将在一行中扩展为编译器命令的整个输出,并且由于它包含多个冒号,因此会出现语法错误。

您所能做的就是将编译器的输出重定向到一个文件,并使用 make 的 include 功能来包含该文件。但是现在你基本上回到了suggested in the GNU make manual 的场景,除了你的版本效率较低,因为正如上面的 cmets 所指出的,你正在为所有源文件重新生成 all 头文件。运行 make 的时间,而不是只为实际更改的文件重新生成头信息。

有更好/更有效的方法来生成标头,例如the one used by most GNU packages。

【讨论】:

以上是关于为啥在 makefile 中不生成依赖项?的主要内容,如果未能解决你的问题,请参考以下文章

为啥linux下要configure然后make make install

从 MSVC 输出生成 Makefile 依赖项

Makefile 依赖项不适用于虚假目标

如何通过自动生成的makefile 看各个文件的依赖关系

从MSVC输出生成Makefile依赖项

makefile工作方式