# 用 make 进行宏编译
# 为什么要用 make
来看一个案例:执行文档里面包含了 4 个源码文件,分别是 main.c
、 haha.c
、 sin_value.c
、 cos_value.c
、
main.c
:主要目的是让用户输入角度数据与调用其他三个子程序haha.c
:输出一堆有的没有的信息sin_value.c
:计算使用者输入的角度(360)sin 数值cos_value.c
:计算使用者输入的角度(360)cos 数值
main.c
#include <stdio.h>
#define pi 3.14159
char name[15];
float angle;
int main(void)
{
printf ("\n\nPlease input your name: ");
scanf ("%s", &name );
printf ("\nPlease enter the degree angle (ex> 90): " );
scanf ("%f", &angle );
haha( name );
sin_value( angle );
cos_value( angle );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
haha.c
#include <stdio.h>
int haha(char name[15])
{
printf ("\n\nHi, Dear %s, nice to meet you.", name);
}
1
2
3
4
5
6
2
3
4
5
6
sin_value.c
#include <stdio.h>
#include <math.h>
#define pi 3.14159
float angle;
void sin_value(void)
{
float value;
value = sin ( angle / 180. * pi );
printf ("\nThe Sin is: %5.2f\n",value);
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
cos_value.c
#include <stdio.h>
#include <math.h>
#define pi 3.14159
float angle;
void cos_value(void)
{
float value;
value = cos ( angle / 180. * pi );
printf ("The Cos is: %5.2f\n",value);
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 1. 先进行目标文件的编译,最终会有 4 个 *.o 的文件出现
[root@study ~]# gcc -c main.c
[root@study ~]# gcc -c haha.c
[root@study ~]# gcc -c sin_value.c
[root@study ~]# gcc -c cos_value.c
# 2. 再执行连结称为执行文件,并加入 libm 的函数,产生 man 的执行文件
[root@study ~]# gcc -o man main.o haha.o sin_value.o cos_value.o -lm
# 3. 执行程序,比如输入姓名,360 度角的角度值来计算
[root@study ~]# ./man
Please input your name: mrcode # 输入姓名
Please enter the degree angle (ex> 90): 30 # 输入角度
Hi, Dear mrcode, nice to meet you.
The Sin is: 0.50
The Cos is: 0.87
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
可以看到编译指令就变得复杂起来了,如果要重新编译,上述流程还需要重新来一次,很麻烦
可以使用 make 工具简化我们的工作。先建立 makefile 的文件
# 1. 先编辑 makefile 规则文件,内容只要做出 man 这个执行文件
[root@study ~]# vim makefile
main: main.o haha.o sin_value.o cos_value.o
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
# 注意第 2 行数据,是按 tab 产生的空格
# 2.尝试使用 makefile 指定的规则进行编译行为
# 先把之前产生的 .o 文件删除
[root@study ~]# rm -f main *.o
[root@study ~]# make
cc -c -o main.o main.c
cc -c -o haha.o haha.c
cc -c -o sin_value.o sin_value.c
cc -c -o cos_value.o cos_value.c
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
# 此时 make 会读取 makefile 的内容,并根据内容直接编译相关的文件
# 3. 在不删除任何文件的情况下,重新执行一次编译的动作
[root@study ~]# make
make: `main' is up to date.
# 只会执行更新 update 的动作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
上面的执行,从命令行输出来看,获取你会觉得 shell script 也可以做到,的确是这样,但是 make 提供了增量编译的机制,不需要你自己去写那么复杂的流程判断了。好处如下:
- 简化编译时所需要下达的指令
- 若在编译完成之后,修改了某个源码文件,则 make 仅会针对被修改了的文件进行编译,其他的 object file 不会变动
- 最后可以依照相依性来更新(update)执行文件
下面针对 makefile 的语法来介绍
# makefile 的基本语法与变量
makefile 的语法多而复杂,可以参考 GUN 官网文档,这里仅做一些基本的规则,重点在于你接触源码的时候,不至于恐慌,基本规则如下
目标(target):目标文件1 目标文件2
<tab> gcc -o 欲建立的执行文件 目标文件1 目标文件2
1
2
2
在 makefile 中的规则基本上是:
#
代表批注<tab>
需要在命令行的第一个字符- 目标 target 与相依文件(目标文件)之间以
:
分割
对上上面那个示例,如果有两个以上的执行动作时,如何做?比如增加一个指令,直接清除所有的目标文件与执行文件
# 1. 编辑 makefile 来建议新的规则,此规则的目标为 clean
[root@study ~]# vim makefile
main: main.o haha.o sin_value.o cos_value.o
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
clean:
rm -rf main.o haha.o sin_value.o cos_value.o
# 2. 以新的目标 clean 测试执行
[root@study ~]# make clean
rm -rf main.o haha.o sin_value.o cos_value.o
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
如此一来,makefile 里就具有至少两个目标,可以单独执行,也可以如下一起组合执行
[root@study ~]# make clean main
rm -rf main.o haha.o sin_value.o cos_value.o
cc -c -o main.o main.c
cc -c -o haha.o haha.c
cc -c -o sin_value.o sin_value.c
cc -c -o cos_value.o cos_value.c
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
1
2
3
4
5
6
7
2
3
4
5
6
7
这个时候,你会发现 makefile 中的重复数据很多,可以通过变量来重构
[root@study ~]# vim makefile
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
main: ${OBJS}
gcc -o main ${OBJS} ${LIBS}
clean:
rm -rf main ${OBJS}
# 一定要注意这个 tab 键的语法,不能使用空格替代的!
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
与 bash shell script 的语法有点不太相同,变量的基本语法为:
- 变量与变量内容以
=
隔开,同时两边可以有空格 - 变量左边不可以有
<tab>
- 变量与变量内容在
=
两边不能有:
- 在习惯上,变量最好以大写字母为主
- 使用变量时,以
${变量}、$(变量)
使用 - 在该 shell 的环境变量是可以被套用的,例如提到的 CFLAGS 这个变量
- 在指令列模式也可以给予变量
由于 gcc 在编译的行为时,会主动读取 CFLAGS 这个环境变量,所以可以直接在 shell 定义出这个环境变量
CFLAGS="-Wall" make clean main
# 在 make 进行编译时,会读取 CFLAGS 的变量内容
# 还可以在 makefile 中定义这个变量
1
2
3
4
2
3
4
如果在指令列和 makefile 中都设置了 CFLAGS 变量,而且内容不同,那么哪一个会生效?
- make 指令列后加上的环境变量为优先
- makefile 里面指定的环境变量第 2
- shell 原本具有的环境变量第 3
此外:$@
这个特殊的变量,表示目前的目标(target),因此可以修改 makefile 为
[root@study ~]# vim makefile
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
main: ${OBJS}
gcc -o $@ ${OBJS} ${LIBS}
# 这里的 $@ 就表示是 main 这个字符
clean:
rm -rf main ${OBJS}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8