没有输出的输入是不完整的

0%

make命令与MakeFile文件

前面三篇文章主要讲述了c++的编译过程,本篇文章主要讲述用Makefile文件来定义编译过程,用make命令来对项目进行构建,从而项目在编译过程中存在的各种依赖等可以程序化生成并保持最新版本,一次编写,永久受益。

make与Makefile是什么

make命令

源代码变成可执行文件的过程,叫做编译(compile);而对一整个项目而言,先编译这个文件还是先编译另外一个文件的过程叫做构建(build),make命令是一个GNU组织提供的一个构建工具,诞生于1977年,起初主要应用于c语言的项目,但是实际上,任何只要有某个文件有变化就要重新构建的项目,就可以用make来进行构建。

Makefile文件

make是一个构建的命令,而具体的规则都是写在MakeFile文件中的。

如何编写makefile文件

概述

Makefile文件由一系列的规则(rules)构成,每条规则的形式如下

1
2
<target> : <prerequisites>
[tab] <commands>

上面第一行冒号前面的部分,叫做"目标"(target),冒号后面的部分叫做"前置条件"(prerequisites);第二行必须由一个tab键起首,后面跟着"命令"(commands)。

"目标"是必需的,不可省略;"前置条件"和"命令"都是可选的,但是两者之中必须至少存在一个。

每条规则就明确两件事:构建目标的前置条件是什么,以及如何构建。

目标(target)

  1. 目标指的是具体要生成的文件名字,也可以是文件的列表。
  2. 目标也可以是某个操作的名字,这称作“伪目标”(phony target)

前置条件(prerequisites)

  1. 前置条件通常是一组文件名,中间用空格分隔。
  2. 前置条件指定了“目标”是否要重新构建的判断标准:只要有一个前置文件不存在,或者有过更新,那么目标就需要重新构建。

命令(commands)

  1. 命令表示如何构建或者更新目标文件,由一行或者多行的shell命令组成,他是构成“目标”的具体指令,他的运行结果通常就是生成目标文件。
  2. 每行命令之前必须有一个tab键。
  3. 需要注意,每行命令在一个单独的shell中执行,这些shell没有独立关系,如果需要,可以在换行符前加反斜杠转义。

如何使用make命令

有目标无条件

1
2
a.txt:
echo 'this is the a.txt' > a.txt

解释:目标是创建a.txt, 没有前置条件。具体的执行命令是将“this is the a.txt”这个字符串写入到a.txt中。

1
2
make t.txt //执行Makefile文件  
cat a.txt //查看a.txt文件内容,发现就是this is the a.txt。

有目标有条件

1
2
b.txt : a.txt
cat a.txt > b.txt

解释:目标是创建b.txt,前置条件是要有a.txt文件,具体命令是将a.txt的内容输出到b.txt中去。

伪目标无前置条件

1
2
clean :
rm *.txt

解释:目标是clean,没有前置条件,具体命令是将所有的txt文件删除。
这里要说明一下,如果当前目录下也有一个叫做clean的文件,那么上述命令便不会执行了,所以我们可以声明这个命令是一个“伪目标”,也就是说变成了如下的样子

1
2
3
.PHONY: clean
clean :
rm *.txt

阶段小结

按照前面部分的介绍,我们可以得到如下内容的Makefile文件。

1
2
3
4
5
6
7
8
9
a.txt:
echo 'this is the a.txt' > a.txt

b.txt : a.txt
cat a.txt > b.txt

.PHONY: clean
clean :
rm *.txt

其实这个时候我们如果直接执行make命令,那么会默认执行第一条规则,也就是说,对于上面的Makefile文件,

1
make

1
make a.txt

是等价的。

前置条件不符

还是在之前的Makefile的基础上继续修改。这个时候我们想创建c.txt,但是我们当前没有d.txt

1
2
c.txt : d.txt
echo 'test no prerequists with c.txt' >c.txt

这个时候执行make c.txt命令,肯定会报错,报错信息如下

1
make: *** No rule to make target `d.txt', needed by `c.txt'.  Stop.

MakeFile语法

注释

1
2
3
4
5
# 这是注释
.PHONY :print
print :
# 这也是注释
echo 'hello world' #这还是注释

通过如下命令执行

1
make print

结果如下

1
2
3
4
➜  maketest git:(dev) ✗ make print
# 这也是注释
echo 'hello world' #这还是注释
hello world

大家会发现,print对应的命令也打印出来了,这就涉及了回声这个概念

回声(echoing)

正常情况下,make会打印每条命令,然后再执行,这就叫做回声(echoing)。
所以上面print执行的时候,会将命令的内容也打印出来,然后才是真正的执行结果。
在命令的前面加上@,就可以关闭回声

1
2
3
4

.PHONY :printnoecho
printnoecho :
@echo 'hello world'

输出结果就不会有命令内容,而仅仅剩下执行结果了。

1
2
➜  maketest git:(dev) ✗ make printnoecho
hello world

自定义赋值

1
2
3
txt = 'hello world'
printxt:
@echo $(txt)

执行

1
make printxt

输出结果

1
hello world

编译多个文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ALL  =  d.txt e.txt \
f.txt

all : $(ALL)
echo 'all'


d.txt : a.txt
cat a.txt > d.txt

e.txt : d.txt
cat d.txt > e.txt

f.txt : d.txt e.txt
cat d.txt e.txt > f.txt

然后执行命令

1
make all

会发现d.txt,e.txt,f.txt都会得到创建。

1
2
3
4
5
6
➜  maketest git:(dev) ✗ make all
cat a.txt > d.txt
cat d.txt > e.txt
cat d.txt e.txt > f.txt
echo 'all'
all

自动变量

1
2
3
$@ : 当前目标
$< : 指代第一个前置条件
$^ : 指代所有的前置条件

暂时先写这些,以后遇到什么问题再做补充。

本文的代码文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
a.txt:
echo 'this is the a.txt' > a.txt

b.txt : a.txt
cat a.txt > b.txt

.PHONY: clean
clean :
rm *.txt

c.txt : d.txt
echo 'test no prerequists with c.txt' >c.txt


# 这是注释
.PHONY :print
print :
# 这也是注释
echo 'hello world' #这还是注释


.PHONY :printnoecho
printnoecho :
@echo 'hello world'


compile : %.cpp
g++ -o %.cpp %.cpp


txt = 'hello world'
printxt:
@echo $(txt)

printcc:
$(CC) -o test test.cpp


ALL = d.txt e.txt \
f.txt

all : $(ALL)
echo 'all'


d.txt : a.txt
cat a.txt > d.txt

e.txt : d.txt
cat d.txt > e.txt

f.txt : d.txt e.txt
cat d.txt e.txt > f.txt

文章参考

  1. Make 命令教程-阮一峰
  2. makefile文件编写
  3. Makefile文件编写