最近在学linux相关的操作,接触到makefile文件,在此做个笔记
刚接触linux时,关于gcc编译器只会用形如 gcc hello.c
的命令来对代码进行编译(实际上这个命令是一键完成上面四个编译过程的),对整个编译过程还不了解,以至于后面在接触makefile的编写时出问题。现在先来看一看gcc的基础知识:
如上图所示,c程序的编译过程分为以上四个步骤,这四个步骤在c基础中也学过,但过于理论导致一知半解:
使用命令:gcc -E hello.c –o hello.i
,得到预处理后的文件,这里的-o是重命名的意思。如下
可以用vim打开hello.i文件,可看到源代码本只有几行,经过预处理后得到几百行。
使用命令:gcc -S hello.i -o hello.s
,生成汇编文件。
用vim打开可见:
使用命令:gcc -c hello.s -o hello.o
,生成二进制文件,但还不能直接运行,这里-c命令是将代码转换为二进制语言,不进行链接。
关于-c这个命令,参考gcc 和 gcc -c有什么区别呢
使用命令:gcc hello.o
。尽管3中得到了二进制文件,但还是不能直接执行,需要通过链接,得到a.out文件,才能运行。
了解了上述gcc编译过程之后,接下来就可学习makefile了。
先给出测试例子–三个.c文件:
main.c :
#include
extern void fun1();
extern void fun2();
int main()
{
printf("main\n");
fun1();
fun2();
return 0;
}
fun1.c:
#include
void fun1()
{
printf("fun1()\n");
}
fun2.c:
#include
void fun1()
{
printf("fun2()\n");
}
单纯的在shell界面用gcc,可以做到对以上三个文件的一起编译,如下图:
但是,假如我只对其中的某一个文件进行修改,再次编译时仍然需要对以上三个文件都重新编译,当文件数特别多的时候,这将做很多重复工作,花很大时间。
1 target: prerequisites
2 commands //注意这里为Tab缩进
翻译一下就是
目标文件 :依赖项
命令
**即是用依赖项生成对应的目标文件,而依赖项是通过下面的命令生成的。
先看最简单的一种情形**,新建一个名为makefile文件,输入以下内容:
再在终端输入make后得到以下结果:
可看出,这种makefile文件同只有gcc的一样,是对所有的文件进行编译,特别耗时。
通过拆分,现在想,修改哪个文件,就只编译哪个文件,未修改的不参加编译。如下图,第一个目标cpp为终极目标,下面的为子目标。当我们想利用依赖项生成目标cpp时,会先查找后面的依赖项main.o ,没有就会向下查找对应的规则生成main.o依赖项。后面同理。即makefile向下检索,子目标是用来生成终极目标的依赖项,然后依次往上执行命令。
终端执行后可看到其执行顺序为:
这时修改fun2.c文件中一点点内容,重新make,可看到只是编译了fun2.c文件。原因在于
makefile更新目标文件是比较时间的,即比较依赖项和目标文件的修改时间,依赖文件修改时间新于目标文件,则更新目标文件。
以fun2.c为例,如上图,fun2.c变化前,fun2.o生成时间是晚于fun2.c; fun2.c变化后,通过比较修改时间,发现fun2.o的时间是早于fun2.c,这时说明fun2.c已修改,就需要执行上图所示命令生成新的子目标文件fun2.o,即重新编译fun2.c文件。而其他的文件对应的.c和.o时间无变化,故不需要重新编译。参考视频:C++ linux 服务器开发基础—makefile工作原理
上述makefile代码冗余还很严重,可以通过变量简化该代码,具体的可见视频。