我刚刚介绍了makefile(用于c ++程序),我有三个简单的问题。假设我在functions.h中存储了一些类定义,其定义在functions.cpp中定义,并在main.cpp中实现。我的简易makefile是:
CXX=g++
main: functions.o main.o
${CXX} -o main student.o main.o
clean:
rm -f *.o main
我的想法是,当我调用make时,默认为main并查找.o文件。如果它们不存在,那么makefile会自动创建.o文件,然后执行链接到main。首先,当我们可以简单地做什么时,为什么还要烦恼呢?
${CXX} -o main functions.cpp main.cpp
我开始研究宏,然后找到了重新实现makefile的方法:
main: functions.o main.o
${CXX} $^ -o $@
我假设$ ^和$ @自动填写主文件和目标文件?如何获得这些信息?
我的最后一个问题是为什么订单会改变?我的原始makefile的格式为g ++ -o [name] [dependents],而带有宏的那个有g ++ [name] -o [dependents]。我尝试切换顺序,但然后makefile停止工作。切换为什么?
答案 0 :(得分:3)
你这里有很多问题。我会尝试为你解答。
首先,为什么我们只需要做到这一点就烦扰所有这些
$ {CXX} -o main functions.cpp main.cpp
是的,你也可以这样做。有时将编译阶段与链接阶段分开会更方便,但您不必这样做。
如何知道获取此信息?
$ @扩展为正在调用的规则的名称。在这种情况下,规则的名称为main
。 $ ^扩展为所有先决条件的名称(规则行上:
之后的所有内容。)在这种情况下,$ ^扩展为functions.o main.o
。如果展开规则并展开宏,则会得到:
main: functions.o main.o
${CXX} functions.o main.o -o main
我的最后一个问题是为什么订单会改变?
如果您将命令编写为:
,则无关紧要${CXX} functions.o main.o -o main
或作为:
${CXX} -o main functions.o main.o
它们是等价的。现在您可能会发现您误解了宏是如何扩展的。
GNU Make manual解释了所有这些内容,这些内容既繁琐,又非常有用。
答案 1 :(得分:0)
1a上。你的假设是正确的。 GNU make正在执行内置规则以将.c文件转换为.o文件。它运行您的显式规则以将.o文件链接在一起。
1b中。为什么要采取两个步骤(一个用于编译,另一个用于链接)?它们是不同的步骤,恰好得到了g ++编译器的支持。这是g ++提供的便利。但是有很多方法可以创建.o文件。有些可能来自.c文件,有些来自.cpp文件,还有一些可能来自另一种语言,如Fortran。在所有这些情况下,编译步骤是不同的,但链接步骤是类似的。因此,将构建分为两个步骤有助于管理这些差异。
正确,这些是自动变量:https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html $ @将替换为规则的目标,$ ^将替换为规则的所有先决条件列表。
命令略有不同。你的第一个是" g ++ -o main functions.cpp main.cpp"你的第二个是" g ++ functions.o main.o -o main"。第一个命令是两个步骤(编译和链接),第二个命令只是链接。通常,命令行参数的顺序对于像g ++这样的工具并不重要。但是,我认为在这种情况下,链接,.o文件需要在输出文件选项之前。
答案 2 :(得分:0)
在IMO的其他答案中没有充分解决的一个问题是为什么单独编译和链接。对于像这样的小项目来说,真的并不重要。但对于大型项目而言,差异可能非常显着。
请注意
${CXX} -o main functions.cpp main.cpp
将始终编译两个源文件,将它们链接在一起,并丢弃目标文件(.o)。相比之下,
${CXX} -o main functions.o main.o
只会链接目标文件。 Make将检测目标文件何时过期并重新编译。未过期的目标文件不需要重新编译。
编译步骤可能需要很长时间。特别是对于带有大量模板代码的C ++。相比之下,即使对于大型项目,链接通常也非常快。
如果在大型项目中修改了一个源文件,则无需重新编译所有其他源文件。通过在makefile中分离编译和链接,可以确保make
只编译您更改的一个源文件,从而在开发周期中节省大量时间。