Make命令教程
代码变成可执行文件,叫做编译,先编译这个,还是先编译那个(即编译的顺序安排),叫做构建(build)。make就是最常用的构建(相当于批处理)工具,主要用于C语言的项目,只要某个文件有变化,都要重新构建项目。
Make的概念
make的意思就是“制作”,make命令就是要做出某个文件,比如,要做出文件a.txt,就可以执行下面的命令。
1 | make a.txt |
但是,如果真的输入这个命令,并不会有任何作用,因为make命令本身并不知道如何作出a.txt,需要有人告诉它,如何调用其他命令完成这个目标。
比如,假设文件a.txt依赖于b.txt和c.txt,是两个文件连接(cat)的产物,那么,make命令需要知道下面的规则。
1 | a.txt: b.txt c.txt #表示a依赖于b和c |
也就是说,make a.txt这条命令的背后,实际上分为两步:第一步,确认b.txt和c.txt必须已经存在,第二步使用cat命令将这两个文件合并,输出为新文件。
像这样的规则,都写在一个叫做Makefile的文件中,Make命令依赖于这个文件进行构建,可使用命令指定规则的文件名,如下,指定为rules.txt文件作为规则。
1 | make -f rules.txt |
Makefile文件的格式
概述
Makefile文件由一系列规则(rules)构成,每条规则的形式如下:
1 | <target>: <prerequisites> # target叫做目标,后面的叫做前置条件 |
target是必须的,不可省略,前置条件和命令两个必须存在一个。
每条规则实际就是在明确两件事:构建目标的前置条件是什么,以及如何构建
目标(target)
一个目标就构成一条规则,目标通常是文件名,指明Make命令所要构建的对象,比如上文中的a.txt,目标可以是一个文件名,也可以是多个空格分隔的文件名。除了文件名,目标还可以是某个操作的名字,这称为“伪目标”
1 | clean: |
上面代码的目标是clean,它不是文件名,而是一个操作的名字,属于“伪目标”,作用是删除相应的文件。但是,如果当前目录中已经存在一个名为clean的文件,make clean
就不会被执行,为避免这种情况,可以明确声明clean是伪目标,写法如下:
1 | .PHONY: clean |
因为声明为伪目标后,make就不会去检查是否存在一个叫做clean的文件,而是每次运行都执行对应的命令。
如果make 命令运行时没有指定目标,默认会执行Makefile文件的第一个目标(make
)
前置条件(prerequistites)
前置条件通常是一组文件名,之间用空格分隔。它指定了“目标”是否重新构建的判断标准:只要有一个前置文件不存在,或者有过更新(前置文件的last-modification时间戳比目标的时间戳新),“目标”就需要重新构建。
一个例子如下,构建result.txt的前置条件是source.txt,如果当前目录中,source.txt已经存在,那么make result.txt
就可以正常运行,否则,必须再写一条规则来生成source.txt。
1 | result.txt: source.txt |
无source的情况生成其如下:
1 | source.txt: |
命令(commands)
命令(commands)表示如何更新目标文件,由一行或多行的shell命令组成,它是构建“目标”的具体指令,它的运行结果通常是生成目标文件。
每行命令之前必须有tab键,如果想用其他键,可以用内置变量.RECIPEPREFIX声明。
1 | .RECIPEREFIX = > #使用>来代替tab |
需要注意的是,每行命令在一个单独的shell中执行,这些shell之间没有继承关系,即行与行之间的shell变量不能传递,一个例子如下:
1 | var-lost: |
改进方法是将其写在一行,中间用分号分隔或者用反斜杠转义。
1 | var-kept: |
还有一个方法是加上.ONSHELL:
命令。
1 | .ONSHELL |
Makefile文件的语法
注释
井号(#)在Makefile中表示注释
回声(echoing)
正常情况下,make会打印每条命令,然后再执行,这就叫回声(echoing),在命令前加上@,就可以关闭会上。
判断和循环
makefile只用Bash语法,完成判断和循环。
1 | ifeq ($(CC), gcc) |
上面代码判断当前编译器是否是gcc,然后指定不同的库文件。
又一个例子,如下:
1 | LIST = one two three |
上面的运行结果如下:
1 | one |