我们提到过 make 的功能是可以简化编译过程里面所下达的指令,同时还具有很多很多方便的功能,那么下面咱们就来试试使用 make 简化下达编译指令的流程吧!
为什么要使用make
先来想象一个案例,假设我的执行档里面包含了四个源代码文件,分别是 main.c
、haha.c
、sin_value.c
、cos_value.c
这四个文件,这四个文件的目的是:
main.c
:主要的目的是让用户输入角度数据与调用其他三支子程序;haha.c
:输出一堆讯息而已;sin_value.c
:计算使用者输入的角度(360) sin 数值;cos_value.c
:计算使用者输入的角度(360)cos 数值;
由于这四个文件是有关联的,并且还用到数学函式在里面,所以如果你想要让这个程序运行,就需要这样编译:
1、先生成目标文件:
[root@localhost test3]# gcc -c main.c
[root@localhost test3]# gcc -c haha.c
[root@localhost test3]# gcc -c sin_value.c
[root@localhost test3]# gcc -c cos_value.c
2、在进行链接成为可执行程序文件,并加入依赖的 libm
的数学函式包:
[root@localhost test3]# gcc -o main.out main.o haha.o sin_value.o cos_value.o -lm
3、本程序的执行结果,必须输入姓名、360 度角的角度值来计算:
[root@localhost test3]# ./main.out
Please input your name: zze
Please enter the degree angle (ex> 90): 30
Hi, Dear zze, nice to meet you.
The Sin is: 0.50
The Cos is: 0.87
可以看到,编译的过程需要进行好多动作啊!而且如果要重新编译,则上述的流程得要重新来一遍,光是找出这些指令就够烦人的了!如果可以的话, 能不能一个步骤就给他完成上面所有的动作呢?那就利用 make 这个工具吧!
1、先试看看在这个目录下建立一个名为 makefile
的文件,内容如下:
main: main.o haha.o sin_value.o cos_value.o
gcc -o main.out main.o haha.o sin_value.o cos_value.o -lm
注意:第二行的
gcc
之前是 tab 按键产生的空格噢!
2、删除之前生成的目标文件以及可执行程序文件,尝试使用 makefile
完成编译操作:
[root@localhost test3]# rm -rf *.out *.o
[root@localhost test3]# 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.out main.o haha.o sin_value.o cos_value.o -lm
3、在不删除任何文件的情况下,重新执行一次编译操作:
[root@localhost test3]# make
gcc -o main.out main.o haha.o sin_value.o cos_value.o -lm
看到了吧!是否很方便呢?第二遍编译操作因为源代码文件没有变化所以只是进行了链接的操作而已。
或许你会说:如果我建立一个 shell script 来将上面的所有动作都集结在起,不是具有同样的效果吗?效果当然不一样,以上面的测试为例,我们仅写出 main
需要的目标文件,结果 make 会主动的去判断每个目标文件相关的源代码文件,并直接予以编译,最后再直接进行连结的动作!真的是很方便啊!此外,如果我们更动过某些源代码文件,则 make 也可以主动的判断哪一个源代码文件对应的目标文件有更新过,并仅更新该文件,如此一来,将可大大的节省很多编译的时间呢!要知道,某些程序在进行编译的行为时,会消耗很多的 CPU 资源呢!所以说,make 有这些好处:
- 简化编译时所需要下达的指令;
- 若在编译完成之后,修改了某个原始码文件,则make仅会针对被修改了的文件进行编译,其他的 objectfile 不会被更动;
- 最后可以依照相依性来更新(update)执行档。
既然 make 有这么多的优点,那么我们当然就得好好的了解一下 make 这个令人关心的家伙!而 make 里面最需要注意的大概就是那个规则文件,也就是 makefile 这个文件的语法啦!所以下面我们就针对 makefile 的语法来加以介绍。
makefile的基本语法与变量
makefile 的语法可是相当的多而复杂的,这里仅介绍一些基本的规则:
<目标(target)>: <目标文件1> <目标文件2>
<tab> gcc -0 <欲建立的执行文件> <目标文件1> <目标文件2>
那个目标(target)就是我们想要建立的信息,而目标文件就是具有相关性的 object files,那建立执行文件的语法就是以 tab 按键开头的那一-行!注意噢,命令行必须要以 tab 按键作为开头才行!他的规则基本上是这样的:
- 在 makefile 当中的 # 代表批注;
- tab 需要在命令行(例如
gcc
这个编译程序指令)的第一个字符; - 目标(target)与目标文件之间需以
:
隔开;
同样的,我们以刚刚上一个小节的案例进一步说明,如果我想要有两个以上的执行动作时,例如下达一个指令就直接清除掉所有的目标文件与执行文件,该如何制作呢?
1、先编辑 makefile 来建立新的规则,此规则的目标名称为 clean
:
main: main.o haha.o sin_value.o cos_value.o
gcc -o main.out main.o haha.o sin_value.o cos_value.o -lm
clean:
rm -f main.out main.o haha.o sin_value.o cos_value.o
2、测试新的目标动作:
[root@localhost test3]# make clean
rm -f main.out main.o haha.o sin_value.o cos_value.o
如此一来,我们的 makefile
里面就具有至少两个目标,分别是 main
与 clean
,如果我们想要建立 main
的话,输入make main
,如果想要清除指定文件,输入 make clean
即可!而如果想要先清除目标文件再编译 main
这个程序的话,就可以这样输入:make clean main
,如下所示:
[root@localhost test3]# make clean main
rm -f main.out 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.out main.o haha.o sin_value.o cos_value.o -lm
这样就很清楚了吧!但是,你是否会觉得,咦!makefile
里面怎么重复的数据这么多啊!没错!所以我们可以再借由 shell script 那时学到的变量来简化 makefile
噢。
上述 makefile
可修改为如下:
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
main: ${OBJS}
gcc -o main.out ${OBJS} ${LIBS}
clean:
rm -rf main.out ${OBJS}
与 bash shell script 的语法有点不太相同,变量的基本语法为:
- 变量与变量内容以
=
隔开,同时两边可以具有空格; - 变量左边不可以有
<tab>
,例如上面范例的第一行LIBS
左边不可以是<tab>
; - 变量与变量内容在
=
两边不能具有:
; - 在习惯上,变量最好是以大写字母为主;
- 运用变量时,以
${变量}
或$(变量)
使用; - 在该 shell 的环境变量是可以被套用的,例如提到的
CFLAGS
这个变量; - 在指令列模式也可以定义变量;
由于 gcc 在进行编译的行为时,会主动的去读取 CFLAGS
这个环境变量,所以,你可以直接在 shell 定义出这个环境变量,也可以在 makefile
文件里面去定义,更可以在指令列当中定义它。
使用
gcc
进行编译操作时,可添加-O
选项对生成的目标文件进行优化,可添加-Wall
选项让编译更严谨的显示警告信息,像这些非必要的参数通常称为旗标(FLAGS),因为我们使用的是 C 语言编写程序,所以有时候也会简称这些旗标为CFLAGS
。
例如:
[root@localhost test4]# CFLAGS='-Wall' make clean main
在使用
make
进行编译的时候,会去取用CFLAGS
变量的内容。
也可以这样:
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
CFLAGS = '-Wall'
main: ${OBJS}
gcc -o main.out ${OBJS} ${LIBS}
clean:
rm -rf main.out ${OBJS}
可以利用指令列进行环境变量的输入,也可以在文件内直接指定环境变量,那万一这个 CFLAGS
的内容在指令列与 makefile 里面并不相同时,以那个方式输入的为主?环境变量取用的规则是这样的:
- make 指令列后面加上的环境变量为优先;
- makefile 里面指定的环境变量第二;
- shell 原本具有的环境变量第三;
此外,还有一些特殊的变量需要了解的哦:
$@
:代表目前的目标(target);
所以我也可以将 makefile 改成:
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
CFLAGS = '-Wall'
main: ${OBJS}
gcc -o $@ ${OBJS} ${LIBS}
clean:
rm -rf $@ ${OBJS}
评论区