今天把make和写makefile的一些非常常用的方法和技巧整理了一下,算是一个旋风式入门了吧,自己的一些小项目按这个方法写makefile是很容易的,make也算是一个学完可以一劳永逸的好工具了。
旋风入门
基础的makefile例子:
main.c是主程序文件,里面的功能是由stack.c和maze.c两个程序中的函数组成,
main.h中定义了几个常量和一个类型,main.c,stack.c,maze.c都要用到。
|
|
每条格式如下:
|
|
目标和先决条件的关系是:欲更新目标,必须先更新它的所有条件;所有条件中只要有一个条件被更新了,目标也必须随之被更新。
注意:命令列表中的每条命令必须以一个Tab开头,注意不能用空格代替这个Tab。对于Makefile中每个以Tab开头的命令,make会启动一个Shell进程去执行它。
make 会自动选择那些受影响的源文件重新编译,不受影响的源文件不会重新编译。
clean例子:
|
|
使用:
|
|
附注:
如果文件夹下存在clean的文件,则会扰乱使用make clean
,想不被文件名所扰乱make命令的使用,可以将clean声明成一个伪目标:
|
这样,完整clean例子:
|
|
不会被同名文件夹所干扰。
PS:clean用来清除目标是一个约定俗称的名字,在所有软件项目中makefile类似的约定俗称的名字还有:
- all, 执行主要编译工作,通常作为缺省目标
- install,执行编译后的安装工作,把可执行文件,配置文件,文档等分别复制到不同安装目录。
升级提高
Makefile有很多灵活的写法,可以写得更简洁,同时减少人为出错的可能。
几种常用写法认识
前提知识:
拆
12main.o: main.c main.h stack.h maze.hgcc -c main.c可以拆成两句:
1234main.o: main.h stack.h maze.hmain.o: main.c main.h stack.h maze.hgcc -c main.c规定:一个目标拆成多条,但只能有其中一条允许带命令列表。
由拆我们可以将前面的简单例子写成以下形式:
|
|
并且我们可以将提出来的几条规则删去,写成以下形式:
|
|
为什么main.o,stack.o,maze.o三个目标编译命令都删去了,怎么还能编译呢?
因为利用了make的机制,make会尝试在内建的隐含规则数据库中查找合适的规则。(具体隐含规则不展开聊了,可以通过make -p
查看,个人建议,还是能省的不要省,要不错了都不知道为什么)
这个例子比较常用,也很方便写,所以举出来,建议使用。
另外一种写法
之前makefile都是以目标为中心写依赖逻辑的,可以换种写法,以条件为中心,makefile只要写明白依赖关系既可,所以上面的例子还可以这么写:
|
|
加入变量
一个简单例子了解变量:
|
|
执行make命令会打印出Huh?
变量在实际运用中:
|
|
CFLAGES常用来定义一些编译选项,例如-o,-g等。
CPPFLAGES 常用来定义一些预处理选项,如-D,-I。
12 >g++ -I.>
>
用来标志包含目录位置,非常有用!
关于预处理中-D:
源代码里面如果这样是定义的:
#ifdef MACRONAME
//可选代码
#endif那在makefile里面
gcc -D MACRONAME=MACRODEF
或者
gcc -D MACRONAME这样就定义了预处理宏,编译的时候可选代码就会被编译进去了。
对于GCC编译器,有如下选项:
-D macro=string,等价于在头文件中定义:#define macro string。例如:-D TRUE=true,等价于:#define TRUE true
-D macro,等价于在头文件中定义:#define macro 1,实际上也达到了定义:#define macro的目的。例如:-D LINUX,等价于:#define LINUX 1(与#define LINUX作用类似)。
–define-macro macro=string与-D macro=string作用相同。
自动变量
常用的自动变量有:
- $@:表示规则中的目标
- $<:表示规则中第一个条件
- $?:表示规则中所有比目标新的条件,组成一个列表,以空格分隔。
- $^:表示规则中的所有条件,组成一个列表,以空格分隔,如果这个列表中有重复的项则将其消除。
例如:
|
|
可以改写成:
|
|
自动处理头文件的依赖关系
现在我们的makefile可以写成如下:
|
|
按照惯例,用all做缺省目标。
现在唯一的麻烦就是,写目标依赖关系的时候还要查看源代码,去一个个分析它们的依赖于哪些头文件,这个过程很容易出错,一是因为有的头文件包含在另一个头文件中,在写规则时很容易遗漏,二是如果以后修改源代码改变了依赖关系,很可能忘记修改Makefile规则。
因为我们知道源代码中包含了目标文件和头文件之间的依赖关系,这种依赖关系是以#include形式描述的,我们只要想办法把源代码中的依赖关系信息抽取出来自动转换成makefile中的规则即可。
第一步,用gcc的-M选项自动分析目标文件和源文件的依赖关系,输出:
(以一个工程的例子为输出结果,主要看实现过程)
|
|
输出不仅包含我们编写的文件,还包含了用到的系统头文件一大堆,系统头文件不需要和我们程序一起维护,我们可以用-MM
选项,输出只包含我们自己编写的头文件。
|
|
这样就可以方便知道文件的依赖关系而不用去一个个代码查看了。
其它make常用命令
如果某次编译想加入调试选项而不是每次都要加入-g,可以在命令行中定义CFLAGS变量而不需要修改makefile:
|
|