GNUmake(二)makefile介绍
文章目录
前言
我们需要一个文件来指导make如何运行,makefile正是这样的文件。makefile告诉make怎样去编译和链接一个程序。还可以通过一些定义去实现其它的功能。当make对程序进行重编译时,任何发生改动的C源文件需要被重新编译,任何引用了改动的头文件的C源程序需要被重新编译,每次对源文件的编译会产生相应的目标文件。当任意源文件被重新编译时,所有的目标文件需要被重新链接产生新的程序。本章使用一个包含8个C源文件和3个头文件的文本编辑器程序的例子,来说明一个简单的makefile文件的用法
一、规则初瞰(重点)
前面总是在说makefile指导文件的编译链接,那么到底怎么实现的呢?其实这些编译链接的过程就是通过makefile的规则来定义的。
makefile的规则包括三个部分。三个部分包括目标(target)、先决条件(prerequisites)和命令集(recipe)。具体格式见下图,其中省略号表示可以为一个或多个。
目标
目标名指定了该条规则的名字,可以是该条规则生成文件的名字,也可以是规则中一系列指令操作的名字。在终端中使用make [target]
,(target处换为相应目标名),可以执行该条规则。
先决条件
一个先决条件是一个生成目标的输入文件,一个目标的生成通常依赖多个输入文件。
命令集
命令集是make执行的操作,一个命令集可以包括一条或多条命令,这些命令可以写在一行或多行上。注意:命令集的每一行前都需要一个制表符作为前缀。如果有需要的话,可以通过修改.RECIPEPREFIX变量来改变命令前缀。
目标、先决条件和命令集的关系
当先决条件发生改变时,命令集中的命令作用在先决条件中的文件上,重新生成目标文件。(这一过程可以递归调用,比如先决条件的先决条件发生变化时,会先更新先决条件,然后更新当前目标文件)
规则类型
从用途来分,规则通常有两种类型。
1. 生成指定文件
hello.o : hello.c
gcc -o hello.o -c hello.c
当hello.c源文件改变或者hello.c依赖的文件改变时,执行命令集中的命令重新生成hello.o目标文件。
2. 执行一系列命令
clean :
rm -f *.o
当调用make clean时,删除当前目录下的所有.o文件。通常这种目标不作为其他目标的先决条件,规则中不包含先决条件,也没有目标文件生成。
makefile允许使用.PHONY
来指定一个伪目标
.PHONY : clean
clean :
rm -f *.o
指定伪目标的作用是,如果目录中存在与伪目标相同名字的文件,make仍会执行命令集中的命令(make的具体执行逻辑见第三节)。
makefile中除了规则还可以包括其他一些文本,但是一个简单的makefile文件可以只由规则组成。
二、一个简单的makefile
这里有一个makefile的例子,该makefile描述了一个可执行文件edit,edit依赖于8个目标文件,而这8个目标文件依赖于8个C源文件和3个头文件。
具体来说,所有的C源文件依赖defs.h
头文件,但只有定义了编辑命令的C源文件依赖于command.h
,只有较为底层的改变编辑器缓存空间的源文件依赖于buffer.h
头文件。
edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert .c defs .h buffer .h
cc -c insert.c
search.o : search.c defs .h buffer .h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit main.o kbd.o command.o display.o \
insert.o search.o files .o utils .o
代码中过长的行,使用了\
进行换行。换行前后实际为一行,第二行前可加任意空白符。
三、 make如何处理makefile
默认目标
默认情况下,make会处理makefile中的第一条规则,但是以.
开头的目标不会被作为默认目标。
例如:
hello : hello.o\
print.o
gcc -o hello hello.o print.o
hello.o : hello.c
gcc -c hello.c -o hello.o
print.o : print.c
gcc -c print.c -o print.o
clean :
rm -f *.o hello
此时调用make后默认会执行hello规则
.hello : hello.o\
print.o
gcc -o hello hello.o print.o
hello.o : hello.c
gcc -c hello.c -o hello.o
print.o : print.c
gcc -c print.c -o print.o
clean :
rm -f *.o hello
此时调用make后默认会执行hello.o规则
也可以通过.DEFAULT_GOAL
变量自己指定默认目标
注意只有显式指定的目标可以作为默认目标,诸如%.o : %.c
这种模式规则不能作为默认目标
处理过程
当调用make时,make会读取当前目录下的makefile文件,并处理第一条规则。以上面edit的makefile文件为例,make会处理重新链接edit
程序的规则。这里介绍三条原则。
- 若当前目录中不存在与当前处理的规则的目标名同名的文件时,当前规则的指令集被执行。
- 若存在同名文件,但是先决条件文件(或其依赖的文件)的更新时间晚于目标文件,即目标文件生成后,先决条件文件又发生了改变,则当前规则的指令集被执行。
- 先检查后执行,即从上至下检查到源头的更改文件后,从下向上依次执行。
比如,当我们更改了insert.c
文件并执行make,会重新编译insert文件,链接edit
程序
而当更改了command.h
文件并执行make,会重新编译kbd.o
, command.o
和 files.o
文件,然后链接edit
程序。
四、变量
edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
我们在定义edit规则时,在先决条件和命令集里,两次罗列了目标文件名,这很容易出错也不易于维护,所以我们可以通过定义变量来进行优化。
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
变量无需额外定义,可以通过=
对变量进行赋值,通过$()
或${}
引用变量,变量更多用法即注意细节见之后的章节。这里有一个大致印象即可。
五、隐式规则
当编译一个单独的C源文件时,我们不需要把C源文件全部打出来,可以让make来自动推断补全C源文件的依赖以及相关编译命令。这种缩略的规则称为隐式规则。
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h
.PHONY : clean
clean :
rm edit $(objects)
其中针对单一源文件的编译进行了相应的缺省。
六、makefile的另一种风格
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h
充分利用隐式规则,但是不够直观
七、 clean规则
.PHONY : clean
clean :
-rm edit $(objects)
命令前的-
表示执行时忽略出错。.PHONY
伪目标在之前介绍过,指定为伪目标后,即使目录中有同名文件,仍会执行命令。由于clean不是任何目标的先决条件,也不适合作为默认目标,所以调用clean时,需要键入make clean