前面三篇文章主要讲述了c++的编译过程,本篇文章主要讲述用Makefile文件来定义编译过程,用make命令来对项目进行构建,从而项目在编译过程中存在的各种依赖等可以程序化生成并保持最新版本,一次编写,永久受益。
make与Makefile是什么
make命令
源代码变成可执行文件的过程,叫做编译(compile);而对一整个项目而言,先编译这个文件还是先编译另外一个文件的过程叫做构建(build),make命令是一个GNU组织提供的一个构建工具,诞生于1977年,起初主要应用于c语言的项目,但是实际上,任何只要有某个文件有变化就要重新构建的项目,就可以用make来进行构建。
Makefile文件
make是一个构建的命令,而具体的规则都是写在MakeFile文件中的。
如何编写makefile文件
概述
Makefile文件由一系列的规则(rules)构成,每条规则的形式如下
1 | <target> : <prerequisites> |
上面第一行冒号前面的部分,叫做"目标"(target),冒号后面的部分叫做"前置条件"(prerequisites);第二行必须由一个tab键起首,后面跟着"命令"(commands)。
"目标"是必需的,不可省略;"前置条件"和"命令"都是可选的,但是两者之中必须至少存在一个。
每条规则就明确两件事:构建目标的前置条件是什么,以及如何构建。
目标(target)
- 目标指的是具体要生成的文件名字,也可以是文件的列表。
- 目标也可以是某个操作的名字,这称作“伪目标”(phony target)
前置条件(prerequisites)
- 前置条件通常是一组文件名,中间用空格分隔。
- 前置条件指定了“目标”是否要重新构建的判断标准:只要有一个前置文件不存在,或者有过更新,那么目标就需要重新构建。
命令(commands)
- 命令表示如何构建或者更新目标文件,由一行或者多行的shell命令组成,他是构成“目标”的具体指令,他的运行结果通常就是生成目标文件。
- 每行命令之前必须有一个tab键。
- 需要注意,每行命令在一个单独的shell中执行,这些shell没有独立关系,如果需要,可以在换行符前加反斜杠转义。
如何使用make命令
有目标无条件
1 | a.txt: |
解释:目标是创建a.txt, 没有前置条件。具体的执行命令是将“this is the a.txt”这个字符串写入到a.txt中。
1 | make t.txt //执行Makefile文件 |
有目标有条件
1 | b.txt : a.txt |
解释:目标是创建b.txt,前置条件是要有a.txt文件,具体命令是将a.txt的内容输出到b.txt中去。
伪目标无前置条件
1 | clean : |
解释:目标是clean,没有前置条件,具体命令是将所有的txt文件删除。
这里要说明一下,如果当前目录下也有一个叫做clean的文件,那么上述命令便不会执行了,所以我们可以声明这个命令是一个“伪目标”,也就是说变成了如下的样子
1 | .PHONY: clean |
阶段小结
按照前面部分的介绍,我们可以得到如下内容的Makefile文件。
1 | a.txt: |
其实这个时候我们如果直接执行make命令,那么会默认执行第一条规则,也就是说,对于上面的Makefile文件,
1 | make |
和
1 | make a.txt |
是等价的。
前置条件不符
还是在之前的Makefile的基础上继续修改。这个时候我们想创建c.txt,但是我们当前没有d.txt
1 | c.txt : d.txt |
这个时候执行make c.txt命令,肯定会报错,报错信息如下
1 | make: *** No rule to make target `d.txt', needed by `c.txt'. Stop. |
MakeFile语法
注释
1 | # 这是注释 |
通过如下命令执行
1 | make print |
结果如下
1 | ➜ maketest git:(dev) ✗ make print |
大家会发现,print对应的命令也打印出来了,这就涉及了回声这个概念
回声(echoing)
正常情况下,make会打印每条命令,然后再执行,这就叫做回声(echoing)。
所以上面print执行的时候,会将命令的内容也打印出来,然后才是真正的执行结果。
在命令的前面加上@,就可以关闭回声
1 |
|
输出结果就不会有命令内容,而仅仅剩下执行结果了。
1 | ➜ maketest git:(dev) ✗ make printnoecho |
自定义赋值
1 | txt = 'hello world' |
执行
1 | make printxt |
输出结果
1 | hello world |
编译多个文件
1 | ALL = d.txt e.txt \ |
然后执行命令
1 | make all |
会发现d.txt,e.txt,f.txt都会得到创建。
1 | ➜ maketest git:(dev) ✗ make all |
自动变量
1 | $@ : 当前目标 |
暂时先写这些,以后遇到什么问题再做补充。
本文的代码文件
1 | a.txt: |