刚开始学习,若有错误处请指出,共同进步。
QQ 群 号:513683159
书本群文件中有。
概念
Makefile的作用
描述整个工程的编译、连接等规则(即描述工程所有文件的编译顺序、编译规则。有自己的书写格式【也是一种编程语言】)。
包括:
1.哪些源文件需要编译及如何编译。
2.需要创建哪些库文件及如何创建这些库文件。
3.如何产生想要的可执行文件。
故编写好Makefile文件,就能实现一行命令完成“自动化编译”(在shell提示符下输入make命令,整个工程自动编译,极大提高效率)。
make是什么?
一个命令工具,解释Makefile中的指令(规则)。
常见名词及其意义
编译
高级语言书写的代码转为机器可识别的机器指令。
只要语法正确、定义正确编译器就可编译出中间目标文件。
如:"foo.c"的目标文件“foo.o”(通常一个源文件对应一个目标文件)
链接
将多.o文件 或 .o文件和库文件 链接成可被操作系统执行的可执行程序。 (链接器不检查函数所在的源文件,只检查所有.o文件中的定义的符号。)
详细:
将.o文件中使用的函数和其他.o或库文件中的相关符号进行合并,对所有文件中的符号进行重新安排(重定位),并连接系统相关文件(程序启动文件等)最终生成可执行程序(Linux环境下,可执行文件的格式为“ELF”格式)。
链接过程使用GNU的“ld”工具。
静态库(文档文件【Archive File】)
是多个.o文件的集合。
Linux中静态库文件的后缀为:“.a”。
静态库中的各个成员(.o文件)没有特殊的存在格式,仅仅是一个.o文件的集合。
使用“ar”工具维护和管理静态库。
共享库
也是多个.o文件的集合,但这些.o文件按一种特殊方法生成。Linux中,共享库文件格式通常为 “ELF” 或 “.so” 格式。
共享库已具备可执行条件。
模块中各个成员的地址(变量引用和函数调用)都是相对地址。
使用共享库的程序在运行时,共享库被动态加载到内存并和主程序在内存中进行连接。
多个可执行程序可共享库文件的代码段(多个程序可共享的使用库中的某一个模块,共享代码,不共享数据)
共享库的成员对象可被执行(由libdl.so提供支持)
应用
若工程中若干源文件修改以后,需根据修改来更新可执行文件或库文件。这时你就需要重新make一下,make会自动根据修改情况完成源文件的对应.o文件的更新、库文件的更新、最终的可执行程序的更新。
make 通过比较对应文件(规则的目标和依赖,)的最后修改时间,来决定哪些文件需要更新、哪些文件不需要更新。对需要更新的文件 make 就执行数据库中所记录的相应命令(在make 读取Makefile 以后会建立一个编译过程的描述数据库。此数据库中记录了所有各个文件之间的相互关系,以及它们的关系描述)来重建它,对于不需要重建的文件 make 什么也不做。
即:
1.所有的源文件没有被编译过,对各个C源文件进行编译并进行链接,生成最后的可执行程序
2.每个上次执行make后修改过的C源代码本文在本次执行make时将会被重新编译
3.头文件在上次执行make之后被修改,则所有包含此头文件的C源文件在本次执行make时将会被重新编译。
后两种只将修改过的源文件重新编译生成.o文件,没修改文件不进行任何工作。
Makefile规则介绍
组成
TARGET… : PREREQUISITES…
COMMAND
…
….
target:规则的目标。
通常是最后需要生成文件名 或 为实现这个目的而必需的中间过程文件名。也可是一个make执行的动作名,如:“clean”(伪目标)
prerequisites:规则的依赖。(可没有,如:clean)
生成规则目标所需的文件名列表,通常一个目标依赖一个或多个文件。
command:规则命令行。
规则所要执行的动作(任意的shell命令或可在shell下执行的程序),限定make执行这条规则所需的动作。
一个规则可有多个命令行,每条命令行占一行。(注:每个命令行须以【Tab】字符开始,【Tab】字符告诉make此行是个命令行,但make程序会把出现在第一条规则之后的所有【Tab】字符开始的行都作为命令行来处理。)
Makefile概念
在Makefile 中“规则”就是描述在什么情况下、如何重建规则的目标文件,通常规则中包括了目标的依赖关系(目标的依赖文件)和重建目标的命令。make执行重建目标的命令,来创建或者重建规则的目标(此目标文件也可以是触发这个规则的上一个规则中的依赖文件)规则包含了文件之间的依赖关系和更新此规则目标所需要的命令。
示例
3个头文件和8个C文件组成,Makefile内容如下:
解释
①书写:可将一个较长行用反斜线()来分解为多行,使的Makefile书写清晰、容易阅读理解。(注:反斜线之后不能有空格)
②# 表示:注释
③目标:
1、最后生成的可执行文件名:edit(第一个目标,终极目标)。
所以首先处理的是“edit”的所有依赖文件。
三种情况:
1>目标文件“edit”不存在,则执行规则以创建目标“edit”。
2>目标文件“edit”存在,其依赖文件中有一个或者多个文件比它“更新”(不够新),则根据规则重新链接生成“edit”。
3>目标文件“edit”存在,它比它的任何一个依赖文件都“更新”,则什么也不做。
2、那些.o文件
三种情况:
1>目标.o文件不存在,使用其描述规则创建它;
2>目标.o文件存在,目标.o文件所依赖的.c源文件、.h文件中的任何一个比目标.o文件“更新”(在上一次 make之后被修改)。则根据规则重新编译生成它;
3>目标.o文件存在,目标.o文件比它的任何一个依赖文件(的.c源文件、.h文件)“更新”(它的依赖文件在上一次make之后没有被修改),则什么也不做。
之所以会被执行,是因被终极目标的依赖列表中,若不是则不会被执行,除非明确指定执行这个规则(如make clean)。
④依赖:
1、冒号后的那些.c文件和.h文件。
③④所有.o文件即是依赖(相对执行程序edit)又是目标(相对.c和.h文件)。
⑤命令:
“cc-c main.c”、“cc -c kdb.c”…
⑥目标“clean”不是一个文件,仅代表执行一个动作的标识。
正常不需要执行这个规则所定义的动作,故“clean”没有出现在其他任何规则的依赖中。执行make时,所指定动作不会执行,除非特别指定。
目的:执行所定义的命令。
没有任何依赖只有执行动作的目标称为“伪目标”
若main.c或defs.h被修改后,再次make会导致?
“main.o”会被更新(其他的.o文件不会被更新),同时“main.o”的更新会导致“edit”被更新。
意外?
若在更新(创建)终极目标的过程中,任意规则出错,则会报错并退出(make不做任何错误检查,即若想正确编译必须要给Makefile提供正确的依赖关系和执行命令)。
程序进阶一:(变量)
上面的程序发现在“edit”目标中出现了两次一样的.o文件(第一次:作为依赖文件列表。第二次:规则命令行“cc”的参数列表),意味着若想增加则需要添加两个地方。这边我们可以用变量来替代,则只需要修改一次即可。
在Makefile文件中添加一行:
变量:objects(变量名自己取),代表了所有.o文件列表。若想使用该变量时:使用“$(objects)”来表示,故程序可变更为:
程序进阶二:(自动推导规则)
自动推导规则:
在使用make编译.c源文件时,编译.c源文件规则的命令可以不用明确给出。这是因为make本身存在一个默认的规则,能够自动完成对.c文件的编译并生成对应的.o文件。它执行命令“cc -c”来编译.c源文件。在Makefile中我们只需要给出需要重建的目标文件名(一个.o文件),make会自动为这个.o文件寻找合适的依赖文件(对应的.c文件。对应是指:文件名除后缀外,其余都相同的两个文件),而且使用正确的命令来重建这个目标文件。
即:省略描述.c文件和.o文件依赖关系的规则,只需给出特定规则描述(.o目标所需的.h文件),程序修改如下:
程序进阶三:(另类风格)
根据依赖而不是目标对规则进行分组,实现如下:
仔细对比一下与上面程序的区别,可以明显发现:因为这边的依赖头文件就三个,目标则很多。所以讲写法改成时候根据依赖而不是根据目标对规则的分株(做法不值得借鉴,后期维护非常痛苦)
书写规则建议方式:单目标,多依赖。(避免多目标,单依赖【上面这种】)
清除工作目录过程文件
其实仔细观察的程序的时,大家一定会发现clean(清楚当前目录中编译过程中产生的临时文件【edit和那些.o文件】,目的是对目标创建或更新程序)上也发生了变化,从
变为:
这两个实现有两点不同:
1.通过“.PHONY”特殊目标将“clean”目标声明为伪目标。避免当磁盘上存在一个名为“clean”文件时,目标“clean”所在规则的命令无法执行(参考4.6 Makefile伪目标一节)。
2.在命令行之前使用“-”,意思是忽略命令“rm”的执行错误(参考5.4命令的错误一节 )。