在Makefile必备组件中包含头文件的原因

问题描述 投票:0回答:2

我正在学习This制作教程。在这里,他们在先决条件中提到了头文件。这真的有必要吗?我运行了没有头的相同代码,它起作用了。例如,我有以下代码

funcs.h

#ifndef FUNCS_H
#define FUNCS_H

int add(int a, int b);

#endif

funcs.cpp

#include "funcs.h"

int add(int a, int b){
    return a+b;
}

main.cpp

#include<iostream>
#include "funcs.h"


int main(){
    std::cout << add(1,2) << "\n";
}

Makefile

add : main.o funcs.o
    g++ -o add main.o funcs.o

main.o : main.cpp funcs.h
    g++ -c main.cpp

funcs.o : funcs.cpp funcs.h
    g++ -c funcs.cpp

clean :
    rm add main.o funcs.o

即使我从funcs.omain.o目标中删除头文件,它仍然有效。那么,还有其他原因为什么要放置头文件?

c++ makefile
2个回答
4
投票

makefile中冒号后面的文件名是目标的依赖项。 make检查它们,如果有任何依赖关系比目标更新,则使用下一行的命令重新构建目标。

因此,当从依赖项中删除头文件时,您当然仍然可以构建并运行,但是如果仅更改头文件中的内容,make将无法正确地重新构建所有目标。


0
投票

@@ Rene对您的问题有正确的答案,但是我想指出,最佳实践是自动生成标头依赖项。否则,中型和大型构建系统将变得很难维护。例如,如果第三方在您的main.cpp中添加了行#include "foo.h",则很有可能无法更新Makefile使其匹配。如果foo.h依赖于另一个头,它将变得更加复杂。更好的做法是让makefile和编译器自动生成依赖项。下面的示例将更清晰地构建您的示例:

SRCS := main.cpp funcs.cpp
OBJS := $(SRCS:%.cpp=%.o)
DEPS := $(OBJS:%.o=%.d)

DEPFLAGS = -MMD -MP

add: $(OBJS)
    $(CC) -o $@ $^

$(OBJS) : %.o : %.cpp
    $(CXX) $(DEPFLAGS) -c $<

-include $(DEPS)

clean:
    rm -f $(OBJS) $(DEPS) add

由于您正在运行教程,所以我假设您是新手,因此我将逐步介绍一下:

首先,将变量SRCS设置为所有必需的源。然后使用模式替换(描述为OBJS)基于SRCS设置here

一旦完成,目标add就会依赖于所有OBJS。我在链接配方中使用了automatic variables $@$^来表示目标以及所有依赖关系的列表。再次这样做是为了避免有人更新一件事,而忘记更新其他地方。

A static pattern rule然后用于生成$(OBJS)中的任何文件。这就是乐趣所在。在编译器中传递了一些额外的标志(假定使用GCC编译器),这些标志将导致编译器与.o文件一起生成.d文件。这些都涉及(您可以在here中找到确切的说明...).d文件类似于:

 main.o: main.cpp funcs.h

 funcs.h:

注意,这是一个微型makefile,仅列出依赖项。这样就可以从主makefile中将其包含进来,这是下一行发生的情况。您得到:

 -include $(DEPS)

此处的-在这里很重要-这意味着如果.d文件不存在(第一次迭代中将不存在),则不要包含它。如果确实存在,则将其读入,就像它是内联键入一样。请注意,在第一次迭代中,.o文件也将不存在,因此无论如何都将构建目标,因此列出依赖关系是没有意义的。在随后的迭代中,您的目标将读取当前构建版本所依赖的依赖项,如果这些依赖项发生任何更改,它将知道当前对象已过时,然后重新构建它。

传入的标志是-MMD,它告诉make构建依赖关系,而-MP可以保护您是否在两次构建之间删除头文件。

最后,您需要清除所有生成的依赖项以及生成的对象。

如果您想更深入一点,可以参考Auto-Dependency Generation,其中有一些我没有提到的技巧(例如将deps保留在自己的目录中,等等。

© www.soinside.com 2019 - 2024. All rights reserved.