不可否认CMake的好处,但是项目一旦大了,手写的 Makefile 查错优势明显。
整理稿
,整理过去我学习makefile的点点滴滴;记录稿
, 记录一些书写技巧;
如果有模板,可以根据模板迅速修改出,你需要的;
我一般不提倡采用一码多用,而是专门的代码有专门的用处。
语法
注意事项
执行规则,书写格式等详细语法略(没有必要记录,请查相关手册),不过要注意的是:
- makefile 常用的符号
- makefile 常用函数
例如:1
2
3
4
5# 列出工作目录下所有以“.cpp”结尾的文件,以空格分隔,将文件列表赋给变量SOURCE
SOURCE := $(wildcard *.cpp)
# 调用patsubst函数,生成与源文件对应的“.o”文件列表
OBJS := $(patsubst %.cpp, %.o, $(SOURCE))
模板
简单版本
仅仅为了测试,一般也就单个文件,所以根本不必要写 makefile.
一般要验证某个代码块,语法特性,我一般设置alias
, 直接看代码:
标准版本
标准版本混合了上面的功能:(多个可执行文件,静态、动态库)
大神写的比较精炼,但是针对不同的目标类型还是要做一些变量定义或者修改。
(已经完全满足我的日常需求了)
1 | ############################################################################### |
编译器参数说明
- CROSS_COMPILE:交叉编译器前缀
- OPTIMIZE:关于优化的编译参数
- WARNINGS:关于warning的编译参数
- DEFS: 关于宏定义的编译参数
- EXTRA_CFLAGS:其它的编译参数
$(OPTIMIZE) $(WARNINGS) $(DEFS) $(EXTRA_CFLAGS)
共同构成了传给gcc的编译参数。
源文件参数说明
- INC_DIR:头文件目录
- SRC_DIR:源文件目录(当前目录.是被默认包含的)
- OBJ_DIR:object文件的输出目录
- EXTRA_SRC:源文件列表
- EXCLUDE_FILES:exclude文件列表
- SUFFIX:源文件的后缀名
- TARGET:最终的目标程序名
- TARGET_TYPE:目标程序的类型, ar/so/app
Makefile 的行为解释:
在
$(SRC_DIR)
定义的每个目录中查找后缀为$(SUFFIX)
的文件,并加上$(EXTRA_SRC)
中的文件,然后排除掉$(EXCLUDE_FILES)
中的文件,获得本工程定义的源文件列表。对于每一个源文件,编译生成的一个.o文件和一个.d文件(依赖文件),放在$(OBJ_DIR)目录下。最终生成的目标文件为$(TARGET)。
此 Makefile 已经充分考虑到文件之间的依赖关系,即如果某个头文件发生改变,当运行make的时候,所有依赖于它的源文件都将被重新编译。
当然一般也做成快捷链接:1
2## make related
alias mkc='cp /Users/merlin/.merlins/mkc ./makefile && echo "done"'
btw: androidmk工程中都是采用这种类似的,只不过每个子目录的 makefile 判断逻辑少一点儿。
工程级版本
一般我很少涉及这一类,工程级别版本都是在公司的源码基础上进行小修小补。
(大的工程版本写起来容易出错,一般也是在公司模板的基础上定制)
具体可以参考下面的链接: 快速实现工程makefile的简单通用模板
调试
这方面最简单的方式,也是我常采用的,就是加打印: @echo "xxxx"
.
当然, make -n
只打印不执行,一定程度上也能帮忙,但是和具体的真正调用命令编译还是有区别的,这里只能看流程,不能看出真正编译时会遇到的问题。(其他还有一些要在 makefile 里添加命令的方式,难记,而且不实用;除非特别复杂的编译问题,否则不建议使用,或者上面方法足够了)
参考:
大神的源码