make使用总结(2)-Makefile介绍
之前已经提到了,make
使用Makefile
文件来描述工程中文件的组织关系以及gcc编译时的依赖关系。因此我们需要为工程项目编写一个或多个Makefile
文件。下面就介绍Makefile
相关的语法。
Makefile主要由显式规则
、隐晦规则
、变量定义
、指示命令
和注释
五个部分组成。下面将一一介绍。
注释
makefile使用#
作为注释符号。
显式规则与隐式规则
规则是make的核心。make规则的格式如下:
[目标] : [依赖]
[tab键] [shell命令]
规则告诉我们两件事:目标何时过期以及如何在目标过期时更新它们。目标
通常是要生成的文件名,如最终的可执行文件或者中间文件。依赖
用于指明目标何时过期,它可以是多个文件,当依赖中的某些文件发生变化时,则表明目标过期。而shell命令
则用于重新生成已经过期的目标。如下面的例子。目标
为main,依赖
为test1.c test2.c.当test1.c或test2.c发生变化时,则make会使用gcc -o main test1.c test2.c
来重新生成main.
main : test1.c test2.c
gcc -o main test1.c test2.c
make通过比较目标
文件最后修改的时间与依赖文件最后修改的时间来判定目标是否过期。当依赖修改的时间新于目标的时间时,则表明目标过期。
为了简化规则的编写。make使用%
来表示规则的通配符。如下面的%.o:%.c
可以匹配test.o:test.c
.
%.o:%.c
gcc -o -c -o $@ $<
为了方便编写Makefile,make定义了一些默认的规则,我们称之为隐式规则。如下面的规则,make在生成main时,首先需要获取test1.o 和test2.o,但test1.o和test2.o是中间文件。也需要生成,但用户并没有定义如生成test1.o和test2.o的规则。这是make就会使用默认的规则来查找test1.c
文件来生成test1.o
.
main : test1.o test2.o
gcc -o main test1.o test2.o
我们在Makefile文件中编写的规则都叫显式规则。为了避免麻烦,建议尽量时用显示规则来表明要用到的规则。
伪目标
上面提到目标
一般为要生成的文件名,但也可以是要执行的动作名。为了防止文件名与动作名重复造成的冲突。系统使用了伪目标
(.PHONY)来标识动作目标
.如下面的例程,目标名clean
使用.PHONY:clean
显式的标记成动作名。
.PHONY: clean
clean:
rm -f *.o *.d
shell命令
当要重新生成目标时,make会调用shell去执行每一条指令。但需要注意的是,即便在同一个目标下,每一条指令都是相互独立的。也就是说make会分别调用shell去执行每一条指令,而非使用一个shell进程按顺序将所有指令都执行一遍。有些状况下,用户希望能够使用cd命令来控制命令执行时所在的路径,比如cd到某个目录下,编译其中的源代码,此时必须在一行中写入多条指令。并用;
隔开。
静态模式
有时候使用静态模式规则
模式的规则,可以简化规则的编写。尤其规则存在多个目标,并且不同的目标可以根据目标文件的名字来自动构造出依赖文件。使用多目标可以使makefile文件变得简洁。语法如下:
<targets ...>: <target-pattern>:<prereq-patterns ...>
<commands>
targets
:定义了一系列的目标文件,可以有通配符。是目标的一个集合。
target-parrtern
:是指明了targets的模式,也就是目标集模式。
prereq-parrterns
:是目标的依赖模式。
使用例程如下:
objects = foo.o bar.o
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
等价于
foo.o : foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o
运行make,生成目标
在默认的方式下,我们只要在工程的根目录下输入make命令。make会在当前目录下寻找名叫Makefile
的文件。然后读取文件内容,对立即赋值的变量等进行展开,最后make会寻找文件中的第一个规则目标作为最终目标
。但下面的目标无法作为最终目标
- 伪目标无法作为最终目标。如
.PHONY
。 - 模式目标无法被认定为终极目标。如
%:
- include指定文件中的目标可以作为终极目标
找到最终目标后,它会根据此目标的规则,进行依赖分析,当依赖不存在或过时时,进一步递归的进行依赖的生成,直到最终目标的生成。
当我们多次执行make时,会涉及到目标的重建规则,规则如下:
- 目标文件不存在
- 依赖项的时间比目标文件要晚
- 目标为伪目标
其他
当我们用到库时,可以在依赖列表中使用-lNAME
链接共享库和静态库.依赖列表中的-lNMAE将被解析为名为libNAME.so或 libNAME.a文件。