make工程管理器
由成百上千个文件构成的项目,如果其中只有一个或少数几个文件进行了修改,按照之前所学的gcc编译工具,就不得不把这所有的文件重新编译一遍,因为编译器并不知道哪些文件是最近更新的
所以人们就希望有一个工程管理器能够自动识别更新了的文件代码
实际上,make工程管理器也就是个“自动编译管理器”,这里的自动是指它能够根据文件时间戳自动发现更新过的文件而减少编译的工作量,同时,它通过读入makefile文件的内容来执行大量的编译工作。用户只需编写一次简单的编译语句就可以了。
## makefile基本结构
makefile是make读入的惟一配置文件,因此下面讲述makefile的编写规则。在一个makefile中通常包含如下内容:
- 需要由make工具创建的目标体(target),通常是目标文件或可执行文件;
- 要创建的目标体所依赖的文件(dependency_file)
- 创建每个目标体时需要运行的命令(command),这一行必须以制表符(Tab键)开头
它的格式为
target : dependency_files
command /*该行必须以Tab键开头*/
例如,有两个文件分别为hello.c和hello.h,创建的目标体为hello.o,执行的命令为gcc编译指令:gcc -c hello.c ,那么,对应的makefile就可以写为:
#The simplest example
hello.o : hello.c hello.h
gcc -c hello.c -o hello.o
接着就可以使用make了。使用make的格式为:make target ,这样make 就会自动读入makefile(也可以是首字母大写的Makefile)并执行对应target的command语句,并会找到相应的依赖文件。如下所示:
[root@localhost makefile]# **make hello.o**
**gcc -c hello.c -o hello.o**
[root@localhost makefile]# ls
hello.c hello.h **hello.o** makefile
可以看到,makefile执行了“hello.o”对应的命令语句,并生成了“hello.o”目标体
**注意:在makefile中的每一个command前必须有“Tab符”,否则在运行make命令时会出错。**
**makefile变量**
上面示例的makefile在实际中是几乎不存在的,因为它过于简单,仅包含两个文件和一个命令,在这种情况下完全不必要编写makefile而只需在shell中直接输入即可,在实际中使用的makefile往往是包含很多的文件和命令的,这也是makefile产生的原因。下面就给出稍微复杂一些的makefile讲解。
```c
david : kang.o yul.o
gcc kang.o bar.o -o myprog
kang.o : kang.c kang.h head.h
gcc -Wall -O -g -c kang.c -o kang.o
yul.o : bar.c head.h
gcc -Wall -O -g -c yul.c -o yul.o
```
在这个makefile中有3个目标体(target),分别为david、kang.o 、yul.o,其中第一个目标体的依赖文件就是后两个目标体。如果用户使用命令“make david”,则make管理器就是找到david目标体开始执行。
这时,make会自动检查相关文件的时间戳。首先,在检查“kang.o”、“yul.o”和“david”3个文件的时间戳之前,它会向下查找那些把“kang.o”或“yul.o”作为目标文件的时间戳。比如,“kang.o”的依赖文件为“kang.c”、“kang.h”、“head.h”。如果这些文件中任何一个的时间戳比“kang.o”新,则命令”gcc -Wall -O -g -c kang.c -o kang.o“将会执行,从而更新文件”kang.o“。在更新完”kang.o“或”yul.o“之后,make会检查最初的”kang.o“、”yul.o“和”david“3个文件,只要文件”kang.o“或”yul.o“中的至少有一个文件的时间戳比”david“新,则第二行命令就会被执行。这样,make就完成了自动检查时间戳的工作,开始执行编译工作。这也就是make工作的基本流程。
接下来,为了进一步简化编辑和维护makefile,make允许在makefile中创建和使用变量。**变量是在makefile中定义的名字,用来代替一个文本字符串,该文本字符串称为该变量的值。**
**在makefile中的变量定义有两种方式:一种是递归展开方式,另一种是简单方式。**
递归展开方式定义的变量是在引用该变量时进行替换的,即如果该变量包含了对其他变量的引用,则在引用该变量时一次性将内嵌的变量全部展开,虽然这种类型的变量能够很好地完成用户的指令,但是它也有严重的缺点,如不能在变量后追加内容(因为语句CFLAGS:=$(CFLAGS) -O在变量扩展过程中可能导致无穷循环)。~~但是我并没有看出这个语句是怎么在变量后追加内容的。莫非它的意思是嵌套定义自己就是在变量后追加内容?~~
为了避免上述问题,简单扩展型变量的值在定义处展开,并且只展开一次,因此,它不包含任何对其他变量的引用,从而消除变量的嵌套引用。~~读到这,可以理解上面追加的意思应该是与简单扩展相反的,即展开多次,包括对变量自身的展开。~~
递归展开方式的定义格式为:VAR=var
简单扩展方式的定义格式为:VAR:=var
make中的变量使用均使用的格式为:4上面的美元符号(VAR),~~这里csdn将美元符号自动识别为指令不显示~~
注意:
1、变量名**是**不包括“:”、“#”、“=”以及结尾空格的任何**字符串**。同时,变量名中应只包含**字母、数字以及下划线**,因为其他符号可能在将来被赋予特别的含义。
2、变量名大小写敏感。
3、推荐在makefile内部使用**小写字母作为变量名**,预留**大写字母**作为控制隐含规则**参数**或用户重载命令选项**参数**的变量名。