Makefile Tutorial
需要创建一个名为makefile的文件
基础知识
1. Makefile 基本格式
target: prerequistites
command
...中文描述就是
目标文件: 生成目标文件所需要的依赖文件(可以有多个,以空格隔开)
生成目标文件所需要的命令(注意前面是一个Tab键,不是四个空格)举例
要生成名字为main的可执行文件,所需要的依赖文件就是main.o。而利用main.o生成main的命令就是gcc main.o -o main
要生成名字为main.o的可执行文件,所需要的依赖文件就是main.c。而利用main.c生成main.o文件的命令就是gcc -c main.c -o main.o
main: main.o
gcc main.o -o main
main.o: main.c
gcc -c main.c -o main.o注意,生成目标文件的依赖文件可以有多个,以空格隔开。只要其中一个依赖文件有所改动,我们就认为目标文件也需要改动,即时你只是加了一行注释而已。再次执行make all命令的时候,生成目标文件的命令就会被执行。
相反,如果所有的依赖文件都没有被改动,则可以认为目标文件也不需要改动,所以就不会执行生成目标文件的命令。这样在一个工程只改动某一部分时,再次编译的时候可以加快速度。
2. Makefile 变量
Makefile支持定义变量,但没有传统编程语言的类型概念。一共有四种基本赋值格式:
=是最基本的赋值
:=是覆盖之前的值
?=是如果没有被赋值过就赋予等号后面的值
+=是添加等号后面的值
=赋值最终会从全局角度来看变量的最终值是什么,如果只是简单地赋值,后面不涉及对变量的再次修改,则可以使用,否则建议使用 := 赋值
# a,b的值都是456,因为=赋值会从全局来看,变量最终的值就是变量的值
a = 123
b = a
a = 456
# a的最终值是456,b的值是123, := 赋值只看本条语句之前变量的值,建议使用这种格式的赋值语句
a = 123
b = a
a = 456
# a如果之前没有定义,就会被赋值123
a ?= 123
# a 后面会加上456, a的值就是: 123 456
a := 123
a += 456获取变量需要使用$(),Makefile中变量的使用类似于C/C++语言中的宏定义, 比如
obj := main.o
main: $(obj)
gcc $(obj) -o main关于常用隐含变量
Makefile中有一些隐含变量,可以分为两类,一类是关于编译器命令的变量,另一类是编译器参数的变量。这些变量都有默认值(不同平台应该不一样,这里就不详细叙述了),个人觉得如果我们要使用这些变量最好再显示定义一次。
另外,看到别人使用这些变量时,就应该能理解别人为什么这样做
CC := gcc
CFLAGS := -o
main: main.o
$(CC) main.o $(CFLAGS) main关于编译命令相关的变量:
- CC : C语言编译程序
- CXX: C++语言编译程序
关于编译器参数相关的变量:
- CFLAGS:C语言编译器参数
- CXXFLAGS:C++语言编译器参数
我们可以参照这种规则来书写我们的makefile中的目标:
- all:这个伪目标是所有目标的目标,其功能一般是编译所有的目标。
- clean:这个伪目标功能是删除所有被make创建的文件。
- install:这个伪目标功能是安装已编译好的程序,其实就是把目标执行文件拷贝到指定的目标中去。
- print:这个伪目标的功能是例出改变过的源文件。
- tar:这个伪目标功能是把源程序打包备份。也就是一个tar文件。
- dist:这个伪目标功能是创建一个压缩文件,一般是把tar文件压成Z文件。或是gz文件。
- TAGS:这个伪目标功能是更新所有的目标,以备完整地重编译使用。
- check和test:这两个伪目标一般用来测试makefile的流程。
如果你要书写这种功能,最好使用这种名字命名你的目标,这样规范一些,规范的好处就是——不用解释,大家都明白。
检查规则
有时候,我们不想让我们的makefile中的规则执行起来,我们只想检查一下我们的命令,或是执行的序列。于是我们可以使用make命令的下述参数:
-n, --just-print, --dry-run, --recon 不执行参数,这些参数只是打印命令,不管目标是否更新,把规则和连带规则下的命令打印出来,但不执行,这些参数对于我们调试makefile很有用处。
-t, --touch 这个参数的意思就是把目标文件的时间更新,但不更改目标文件。也就是说,make假装编译目标,但不是真正的编译目标,只是把目标变成已编译过的状态。
-q, --question 这个参数的行为是找目标的意思,也就是说,如果目标存在,那么其什么也不会输出,当然也不会执行编译,如果目标不存在,其会打印出一条出错信息。
-W <file>, --what-if=<file>, --assume-new=<file>, --new-file=<file>
这个参数需要指定一个文件。一般是是源文件(或依赖文件),Make会根据规则推导来运行依赖于这个文件的命令,一般来说,可以和“-n”参数一同使用,来查看这个依赖文件所发生的规则命令。
另外一个很有意思的用法是结合-p 和-v 来输出makefile被执行时的信息。
Reference
陈皓《跟我一起写Makefile》:https://github.com/seisman/how-to-write-makefile
秋叶依剑