Makefile基础学习
Makefile基础学习
理论知识
- makefile关系到了整个工程的编译规则。一个工程中的源文件不计其数,并且按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个 Shell脚本一样,其中也可以执行操作系统的命令。
- makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
书写规则和语法
- 规则包含两个部分,一个是依赖关系,一个是生成目标的方法。
makefile写法为:
targets : prerequisites
command
...
或是这样
targets : prerequisites ; command
command
...
targets是文件名,以空格分开,可以使用通配符。一般来说,我们的目标基本上是一个文件,但也有可能是多个文件。
command是命令行,如果其不与“targets:prerequisites”在一行,那么,必须以[Tab键]开头,如果和prerequisites在一行,那么可以用分号做为分隔。(见上)
prerequisites也就是目标所依赖的文件(或依赖目标)。
实例学习
- 参考GCC基础知识学习中『一个实例:-I参数的使用』,写出编译例子中vi编辑代码的makefile,编译出来的目标文件为testmymath, 只用显式规则就可以。
首先,我们可以vim Makefile
或者vim makefile
来新建打开一个makefile。
我们先得到几个文件的依赖关系:testmymath依赖五个.o文件,五个.o文件分别依赖相应的.c文件和head.h文件。
第一步要写出最终目标:
testmymath: main.o add.o sub.o mul.o div.o //最终目标文件:依赖文件
gcc main.o add.o sub.o mul.o div.o -o testmymath//以tab键开头,写出命令行,即得到testmymath的命令
接下来是各个.o文件的生成(规则同上):
main.o: main.c head.h
gcc -c main.c
add.o: add.c head.h
gcc -c add.c
sub.o: sub.c head.h
gcc -c sub.c
mul.o: mul.c head.h
gcc -c mul.c
div.o: div.c head.h
gcc -c div.c
最后就可以完整的得到makefile文件啦!
testmymath: main.o add.o sub.o mul.o div.o
gcc main.o add.o sub.o mul.o div.o -o testmymath
main.o: main.c head.h
gcc -c main.c
add.o: add.c head.h
gcc -c add.c
sub.o: sub.c head.h
gcc -c sub.c
mul.o: mul.c head.h
gcc -c mul.c
div.o: div.c head.h
gcc -c div.c
makefile中使用变量
- 在上面的例子中,我们可以看到[.o]文件的字符串被重复了两次,如果我们的工程需要加入一个新的[.o]文件,那么我们需要在两个地方加。当然,我们的makefile并不复杂,所以在两个地方加也不累,但如果makefile变得复杂,那么我们就有可能会忘掉一个需要加入的地方,而导致编译失败。所以,为了makefile的易维护,在makefile中我们可以使用++变量++。makefile的变量也就是一个字符串,理解成C语言中的宏可能会更好。
比如,我们声明任意一变量名,叫objects, OBJECTS, objs, OBJS, obj, 或OBJ,只要能够表示obj文件即可。我们在makefile起始处按如下定义此变量:
objects = main.o add.o sub.o mul.o div.o
于是,我们就可以很方便地在我们的makefile中以“$(objects)”的方式来使用这个变量了,于是我们的改良版makefile变为如下:
objects = main.o add.o sub.o mul.o div.o
testmymath : $(objects)
gcc -o testmymath $(objects)
main.o: main.c head.h
gcc -c main.c
add.o: add.c head.h
gcc -c add.c
sub.o: sub.c head.h
gcc -c sub.c
mul.o: mul.c head.h
gcc -c mul.c
div.o: div.c head.h
gcc -c div.c
make是如何工作的
- 在默认的方式下,也就是我们只输入make命令。那么,
- make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
- 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“testmymath”这个文件,并把这个文件作为最终的目标文件。
- 如果testmymath文件不存在,或是testmymath所依赖的后面的 .o 文件的文件修改时间要比testmymath这个文件新,那么,他就会执行后面所定义的命令来生成testmymath这个文件。
- 如果testmymath所依赖的.o文件也不存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。(这有点像一个堆栈的过程)
- 当然,你的C文件和H文件是存在的啦,于是make会生成 .o 文件,然后再用 .o 文件生成make的终极任务,也就是执行文件testmymath了。
- 我们还可以在makefile中写一条clean的命令来一次性删除所有.o文件和testmymath,以便进行重新编译:
clean:
rm testmymath *.o
像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,因此,我们可以显式要make执行,即命令make clean
。
如果使用变量,也可以写为:
clean:
rm testmymath $(objects)
这样我们可以得到一个比较完整的makefile:
objects = main.o add.o sub.o mul.o div.o
testmymath : $(objects)
gcc -o testmymath $(objects)
main.o: main.c head.h
gcc -c main.c
add.o: add.c head.h
gcc -c add.c
sub.o: sub.c head.h
gcc -c sub.c
mul.o: mul.c head.h
gcc -c mul.c
div.o: div.c head.h
gcc -c div.c
clean:
rm testmymath $(objects)
接下来我们测试一下写的makefile,这里建议大家到head.h所在的目录下使用mv head.h /src所在的路径
,也就是把头文件和.c文件放到一个目录下。因为如果不在一个目录下,会出现make找不到头文件不成功的情况。
- 我们make一下,可以看到make在自动执行了,并产生了.o文件和 testmymath。
- 也可以使用
make clean
一下子删除了想删除的文件。
- 那么如果不把头文件放到src下,该怎么做呢?
- 我们可以在makefile中加一句:
CFLAGS = -I/(头文件所在路径)
然后在相应执行语句中加入$(CFLAGS)即可,如:
main.o: main.c head.h
gcc $(CFLAGS) -c main.c