原文链接:Makefile的编写总结
发布时间:2015-09-01 22:10:24
在Linux做开发,工程的编译输出,需要自己编写Makefile来构建,下面笔者根据自己的实践,做一些总结,本文并不是什么教程,只是笔者觉得一些重要的东西在此记录下来。
一、基本格式
1 | target : depend |
target 是要生成的目标文件,可以是可执行文件、.o、.a、*.so等文件,当然也可以一个伪目标。
depend 是依赖文件,可以是源文件,头文件,*.o文件,当然也可以是一个伪依赖。
command 是执行命令,即如何编译依赖文件生成目标文件。值得注意的是command前面必须是一个tab键。
eg : main.c depend.c depend.h 生成可执行文件main
1 | main:main.o depend.o |
二、变量
Makefile中可以自定义变量,有的地方也称之为宏,变量名尽量用数字、字母和下划线命名,并且对大小写敏感。
变量可以用来代表一个文件名列表、编译选项列表、程序运行的选项参数列表、搜索源文件的目录列表、编译输出的目录列表等。
eg:
1 | CC = gcc |
引用一个变量是这样:$(VarName),例如:$(CC) , $(CFLAG)
笔者的使用习惯,定义变量全都是大写,显然这和C语言的宏定义一样,称之为宏也不为怪。
这里的赋值号前后可以有空格,这与Shell脚本不同,Shell中定义变量,赋值号前后不能有空格。
改写上面的Makefile:
1 | CC = gcc |
三、特殊变量
- $@ 表示目标对象
- $^ 表示所有依赖对象
- $< 表示第一个依赖对象
- VPATH 指定源文件的搜索路径,它和linux下环境变量类似
- vpath 和VPATH类似,但是比VPATH更灵活
继续改写上面的Makefile:
1 | CC = gcc |
四、项目案例
1 | Project |
如上图所示,工程项目Project下有四个目录–Bin,Buld,Lib,Source
- Source 是源代码目录,其下又有三个子目录Src1,Src2,Src3,在实际项目中,源文件一般按功能模块放在不同的目录下,便于管理。
- Lib 目录是库目录,根据笔者的经验,这样的目录放第三方的库,如果是开源的库,源代码可以放入其中,编译成一个库文件,链接程序时用到。因为第三方的开源库很少需要修改,编译成库文件后,就可以一直使用了,所以要把他们单独放在Lib文件目录里。
- Build 目录是编译构建目录,Makefile放在里面,编译时生成的中间文件*.gch,*.o都会在这里,不会散落在源代码目录里,这个目录很有必要。
- Bin 是生成的二进制程序目录,当然这个目录其实软件运行环境目录,这个目录是要部署到机器上的,一个可执行程序的运行,其实是有很多依赖的,比如可能要读取xml配置文件,可能要依赖某个文件创建进程通信的Key值,在这里我简化了它,Bin目录下没有其他子目录,只存放可执行文件。
假设本工程最终生成可执行文件main,依赖Source下的所有源文件,并且依赖Lib下的liblog.a libsqlite.a
Makefile1:
1 | CC = gcc |
说明:
- Makefile文件是在Build目录中,因此Makefile的工作路径就是这个路径,定义其他路径时都是相对于Makefile的工作路径,当然执行Makefile时也必须在这个路径
- 开头定义了几个变量,这样做的好处是使Makefile更容易修改维护
- VPATH定义了源文件寻找目录,以冒号间隔,Make会自动到这几个目录寻找源文件
- 定义OBJ用的字符
\表示行连接符 - all 是一个伪目标,执行命令仅仅是把生成的可执行程序main拷贝到Bin目录,和下面的clean类似
- %.o:%.c 是Make的一种自动推导方式,表示一个.o文件会依赖一个对应的.c文件,本例中Make会自动到当前目录或者VPATH中寻找依赖Demo1.c Demo2.c Demo3.c
五、常用函数
Makefile里也有函数的,有函数定义和调用的语法格式,但是笔者只使用过内置函数,没有使用过自定义函数。
1、wildcard
通配符函数,按照匹配模式生成一个以空格间隔的列表。
语法格式:$(wildcard pattern1 pattern2 ...) ,参数以空格隔开。
例如:SRC = $(wildcard *.c ./foo/*.c) 表示把当前目录下的.c文件和子目录foo的.c文件以列表赋值给SRC。
2、patsubst
模式替换,以特定模式替换字符串。
语法格式:$(patsubst from, to, str)
1 | SRC = Demo1.c Demo2.c Demo3.c |
此时OBJ展开:OBJ = Demo1.o Demo2.o Demo3.o
3、subst
字符串替换,暴力的把一个字符串替换成另一个字符串。
语法格式:$(subst from, to, str)
4、filter
过滤函数,保留符合模式的字符串。
语法格式:$(filter pattern1 pattern2 ..., text)
1 | src = a.c b.c a.h c.sh d.o |
此时csrc展开:csrc = a.c b.c a.h
5、filter-out
过滤函数,保留不符合模式的,与filter功能相反,语法一样。
Makefile2(用函数改写):
1 | CC = gcc |
Makefile2 比 Makefile1 更简洁通用,但当工程需要生成多个可执行程序时,Makefile1 中 OBJ 变量的定义更灵活清晰。
由于笔者的水平有限,出错在所难免,恳请读者拍砖指正,谢谢阅读。