一般在写较大型的项目时,使用一条或几条gcc命令编译生成可执行程序是远远不够的,而且当我们仅仅修改了其中一个文件的时候,需要重新人工输入命令以完成编译,这样不仅效率低下而且容易出错。
在Linux中,有一个用来维护程序模块关系和生成可执行程序的工具:make 它可以根据程序模块的修改情况重新编译生成中间代码或者最终的可执行程序。执行make命令,需要一个名为Makefile或makefile的文本文件,这个文本文件定义了整个项目的编译规则。它定义了模块间的依赖关系,指定文件的编译顺序以及编译所使用的命令。
Makefile的基本构成:
makefile文件的基本单元是规则
一条规则指定一个或多个目标文件,目标文件后面跟的是编译生成该目标文件所依赖的文件或模块(也称先决条件),最后是生成或更新目标文件所使用的命令。
规则的格式如下:
目标文件 分隔符 依赖文件
命令
1.目标文件是指最后需要生成的文件,依赖文件是指生成目标文件所需要的文件,也就是说在生成目标文件之前,先需要生成依赖文件;
2.分隔符一般都是冒号,其他分隔符很少使用;
3.需要注意的是,如果某一行是命令,那么它必须以一个Tab键开头(第2行的命令前边必须有个Tab键)。另外,如果某一行以Tab键开头,make就认为这一行是命令;
举一个简单的栗子:我现在写了一个hello.c的程序,我的目标是生成hello.out文件,生成hello.out文件需要hello.c文件:所以hello.out是目标文件,冒号是分隔符,hello.c是依赖文件,gcc hello.c -o hello.out是命令;
hello.out:hello.c
gcc hello.c -o hello.out
在依赖文件列表后加上分号,可以跟上命令:
hello.out:hello.c;gcc hello.c -o hello.out
make如何解释执行Makefile
现在看一个稍微复杂一点的栗子(以下文件假设都存在并且在一个目录下):
main:main.o server.o client.o
gcc main.o server.o client.o -o main
main.o:main.c head1.h head2.h define.h
gcc -c main.c
server.o:server.c head1.h
gcc -c server.c
client.o : client.c head2.h
gcc -c cleint.c
#This is a makefile
最后一行是注释,在Makefile文件中,如果某行以#开头,make就认为它是一行注释,因而就不会解析这行内容;
编写完makefile文件后,我们在命令行输入make后,make
首先会在当前目录下寻找名为Makefile
或makefile
的文件,找到Makefile文件后,在当前目录下寻找第1行当中的目标文件main,发现没有,就去寻找生成main文件所依赖的文件server.o与client.o,发现也没有,然后跳过第2行的编译命令(因为依赖文件都没有,怎么生成目标文件啊),定位到第3行,第3行当中的目标文件mian.o也没有,但它的依赖文件都可以找到,所以执行第4行的命令,生成目标文件main.o;继续往下,没有找到第5行的目标文件,但在当前目录下能找到它全部的依赖文件,所以执行第6行命令,生成目标文件server.o;之后的第7行,第8行与第5行第6行类似。然后make定义到最后一行,发现它是一个注释行,不予理睬。之后make回溯到第一行,此时第一行所有的依赖文件已经全部生成,于是执行第二行的编译命令,最后生成目标文件main。
Makefile的优势
当我们修改了其中的一些文件时,makefile的优势就体现出来了
现在假设我们修改了head1.h这个头文件,然后在当前目录下运行make,那么makefile又是如何执行的呢?
在这之前,我们要先搞清楚,如果目标文件的修改时间晚于依赖文件的时间,那么make就认为目标文件是最新的(如果目标文件的修改时间早于依赖文件的话,那就说明依赖文件被修改了呀,肯定要执行命令生成最新的目标文件呀)
同第一次一样,make现在当前目录寻找第1行的目标文件main,发现目标文件存在,然后寻找第1行的依赖文件,发现也存在,然后make开始比较它们的修改时间,发现目标文件main的修改时间要晚于3个依赖文件的,make就认为目标文件是最新的,就没有必要执行第2行的命令生成最新的目标文件了;
然后make定位到第3行,同理,目标文件与依赖文件都存在,make开始比较它们的修改时间,发现依赖文件head1.h的修改时间要晚于目标文件main.o,所以执行第4行的命令生成最新目标文件main.o;
之后到第5行,目标文件与依赖文件都存在,比较它们的修改时间,发现依赖文件head1.h的修改时间要晚于目标文件server.o,所以执行第6行的命令生成最新目标文件server.o;
第7行通过比较可以得知,目标文件是最新的,没有必要执行第8行的命令来生成最新的目标文件;
注释,不予理睬,回溯到第1行,发现依赖文件的修改时间要晚于目标文件的修改时间,所以执行第2行的命令生成最新的目标文件,回溯完毕,make终止。
但如果说,所有的文件都不需要更新,make就不会执行编译命令,它会在屏幕上输出类似以下的提示信息:
"main"是最新的
通常来说,make将Makefile文件中的第一个规则中的目标文件视为最终的目标文件(又称为最终目标文件)。如果第一条规则中有多个目标文件,make将这些目标文件中的第一个视为最终目标文件。通常最终目标文件是最后要编译要生成的可执行文件,其它规则中的目标文件都是为了产生最终目标文件而生成的中间文件。因此,在写Makefile时,一般把最后要生成的文件放在第一条规则的文件列表中
这周先总结一下makefile的简单用法,下周应该还会写一篇makefile的构成与变量。