Makefile入门教程
Makefile简介
很多大型项目的编译都是通过Makefile来组织各种库和代码之间的依赖关系。Makefile不仅可以用来编译项目还可以用来组织我们一些日常操作
Makefile是和make命令一起配合使用的,Makefile就像shell脚本一样,同时执行操作系统的命令
Makefile的好处就是能够使用一行命令来完成“自动编译”,编译整个工程我们要做的唯一事情就是在shell提示符下输入make命令,整个工程就会自动编译
make是命令工具,它解释makefile指令规则
Makefile基本格式如下:
target ... : prerequisites ...
command
...
...
- target:目标文件
- prerequisties:生成target所需要的文件或目标
- command:make需要执行的命令(任意的shell命令),makefile中的命令必须以缩进[tab]开头
Makefile的初级语法
规则主要有两个部分:依赖关系 和 生成目标的方法
语法有以下2种
target ... : prerequisites ...
command
...
或
target ... : prerequisites ; command command ...
*注* command太长, 可以用 "\" 作为换行符
规则中的通配符
- * :表示任意一个或多个字符
- ? :表示任意一个字符
- [...] :[abcd] 表示a,b,c,d中任意一个字符, [^abcd]表示除a,b,c,d以外的字符, [0-9]表示 0~9中任意一个数字
- ~ :表示用户的根目录
路径搜索
将源文件的路径明确在Makefile中,便于编译查找,Makefile中有个特殊的变量 VPATH 就是完成这个功能的。
如果在当前目录中没有找到相应文件或依赖的文件, Makefile 回到 VPATH 指定的路径中再去查找
VPATH 使用方法:
- vpath <directories> 当前目录中找不到文件时, 就从<directories>中搜索
- vpath <pattern> <directories> 符合<pattern>格式的文件, 就从<directories>中搜索
- vpath <pattern> 清除符合<pattern>格式的文件搜索路径
- vpath 清除所有已经设置好的文件路径
# 示例1 - 当前目录中找不到文件时, 按顺序从 src目录 ../parent-dir目录中查找文件 VPATH src:../parent-dir # 示例2 - .h结尾的文件都从 ./header 目录中查找 VPATH %.h ./header # 示例3 - 清除示例2中设置的规则 VPATH %.h # 示例4 - 清除所有VPATH的设置 VPATH
Makefileh中的变量
变量定义 (= or :=)
a = 11 22 b = $(a) 33 # 或者 a := 11 22 b := $(a) 33
其中 = 和 := 的区别在于,:= 只能使用前面定义好的变量,= 可以使用后面定义的变量
= 案例
# Makefile内容 a = $(b) 111 b = 222 333 all: @echo $(a) # bash中执行 make, 可以看出虽然 b 是在 a 之后定义的, 但可以提前使用 $ make 222 333 111
:= 案例
# Makefile内容 b := $(a) programC.o a := programA.o programB.o all: @echo $(b) # bash中执行 make, 可以看出 b 中的 $(a) 为空 $ make programC.o
变量替换
# Makefile内容 SRCS := programA.c programB.c programC.c OBJS := $(SRCS:%.c=%.o) all: @echo "SRCS: " $(SRCS) @echo "OBJS: " $(OBJS) # bash中运行make $ make SRCS: programA.c programB.c programC.c OBJS: programA.o programB.o programC.o
变量追加值 +=
# Makefile内容 SRCS := programA.c programB.c programC.c SRCS += programD.c all: @echo "SRCS: " $(SRCS) # bash中运行make $ make SRCS: programA.c programB.c programC.c programD.c
变量覆盖 override
使 Makefile中定义的变量 能够覆盖 make 命令参数中指定的变量
语法:
- override <variable> = <value>
- override <variable> := <value>
- override <variable> += <value>
# Makefile内容 (没有用override) SRCS := programA.c programB.c programC.c all: @echo "SRCS: " $(SRCS) # bash中运行make $ make SRCS=nothing SRCS: nothing ################################################# # Makefile内容 (用override) override SRCS := programA.c programB.c programC.c all: @echo "SRCS: " $(SRCS) # bash中运行make $ make SRCS=nothing SRCS: programA.c programB.c programC.c
目标变量
使变量的作用域仅限于这个目标(target),而不是对整个 Makefile 都有效
语法:
- <target :> <variable-assignment>
- <target :> override <variable-assignment> (override作用参见 变量覆盖的介绍)
# Makefile 内容 var_1 := programA.c programB.c programC.c target1: var_2 := programD.c target1: @echo "var_1:" $(var_1) @echo "var_2:" $(var_2) target2: @echo "var_1:" $(var_1) @echo "var_2:" $(var_2) # bash中执行make $ make target1 "var_1:" programA.c programB.c programC.c "var_2:" programD.c $ make target2 <-- target2中显示不了 $(TARGET1-SRCS) "var_1:" programA.c programB.c programC.c "var_2:"
Makefile 命令前缀
Makefile 中书写shell命令时可以加2种前缀 @ 和 -,或者不用前缀
3种格式的shell命令区别如下:
- 不用前缀 输出 执行的命令 以及 命令执行的结果,出错的话停止执行
- 前缀 @ 只输出命令执行的结果,出错的话停止执行
- 前缀 - 命令执行有错的话,忽略错误,继续执行
# Makefile 内容 (不用前缀) all: echo "没有前缀" cat this_file_not_exist echo "错误之后的命令" <-- 这条命令不会被执行 # bash中执行 make $ make echo "没有前缀" <-- 命令本身 没有前缀 <-- 命令结果 cat this_file_not_exist cat: this_file_not_exist: No such file or directory make: *** [all] Error 1 ########################################################### # Makefile 内容 (前缀 @) all: @echo "没有前缀" @cat this_file_not_exist @echo "错误之后的命令" <-- 这条命令不会被执行 # bash中执行 make $ make 没有前缀 <-- 只有命令执行的结果, 不显示命令本身 cat: this_file_not_exist: No such file or directory make: *** [all] Error 1 ########################################################### # Makefile 内容 (前缀 -) all: -echo "没有前缀" -cat this_file_not_exist -echo "错误之后的命令" <-- 这条命令会被执行 # bash中执行 make $ make echo "没有前缀" <-- 命令本身显示出来 没有前缀 <-- 命令执行结果显示出来 cat this_file_not_exist cat: this_file_not_exist: No such file or directory make: [all] Error 1 (ignored) echo "错误之后的命令" <-- 出错之后的命令也会显示 错误之后的命令 <-- 出错之后的命令也会执行
伪目标
伪目标并不是一个“目标(target)”,不像真正的目标那样会生成一个目标文件。
典型的伪目标是clean:用来清理编译过程中 中间文件,一般格式如下:
.PHONY: clean <-- 这句没有也行, 但是最好加上 clean: -rm -f *.o
引用其他的 Makefile
语法: include Makefile路径
# Makefile 内容 all: @echo "主 Makefile begin" @make other-all @echo "主 Makefile end" include ./other/Makefile # ./other/Makefile 内容 other-all: @echo "other makefile begin" @echo "other makefile end" # bash中执行 make $ ll total 20K -rw-r--r-- 1 wangyubin wangyubin 125 Sep 23 16:13 Makefile -rw-r--r-- 1 wangyubin wangyubin 11K Sep 23 16:15 makefile.org <-- 这个文件不用管 drwxr-xr-x 2 wangyubin wangyubin 4.0K Sep 23 16:11 other $ ll other/ total 4.0K -rw-r--r-- 1 wangyubin wangyubin 71 Sep 23 16:11 Makefile $ make 主 Makefile begin make[1]: Entering directory `/path/to/test/makefile' other makefile begin other makefile end make[1]: Leaving directory `/path/to/test/makefile' 主 Makefile end
查看C文件的依赖关系
写 Makefile 的时候,需要确定每个目标的依赖关系。
GNU提供一个机制可以查看C代码文件依赖那些文件,这样我们在写 Makefile 目标的时候就不用打开C源码来看其依赖那些文件了.
比如,下面命令显示在内核源码中 virt/kvm/kvm_main.c 中的依赖关系
$ cd virt/kvm/ $ gcc -MM kvm_main.c kvm_main.o: kvm_main.c iodev.h coalesced_mmio.h async_pf.h <-- 这句就可以加到 Makefile 中作为编译 kvm_main.o 的依赖关系
make 退出码
Makefile的退出码有以下3种:
- 0 表示成功执行
- 1 表示make命令出现了错误
- 2 使用了 "-q" 选项,并且make使得一些目标不需要更新
指定 Makefile,指定特定目标
默认执行 make 命令时,GNU make在当前目录下依次搜索下面3个文件 "GNUmakefile","makefile","Makefile",
找到对应文件之后,就开始执行此文件中的第一个目标(target). 如果找不到这3个文件就报错.
非默认情况下,可以在 make 命令中 -f 指定特定的 Makefile 和特定的 目标.
# Makefile文件名改为 MyMake, 内容 target1: @echo "target [1] begin" @echo "target [1] end" target2: @echo "target [2] begin" @echo "target [2] end" # bash 中执行 make $ make <-- 找不到默认的 Makefile make: *** No targets specified and no makefile found. Stop. $ make -f MyMake <-- 指定特定的Makefile target [1] begin target [1] end $ make -f MyMake target2 <-- 指定特定的目标(target) target [2] begin target [2] end
Makefile 隐含规则
这里只列一个和编译C相关的
编译C时,<n>.o 的目标会自动推导为 <n>.c
# Makefile 中 main : main.o gcc -o main main.o #会自动变为: main : main.o gcc -o main main.o main.o: main.c <-- main.o 这个目标是隐含生成的 gcc -c main.c
自动变量
Makefile 中很多时候通过自动变量来简化书写,各个自动变量的含义如下:
自动变量 |
含义 |
$@ | 目标集合 |
$% | 当目标是函数库文件时,表示其中的目标文件名 |
$< | 第一个依赖目标. 如果依赖目标是多个,逐个表示依赖目标 |
$? | 比目标新的依赖目标的集合 |
$^ | 所有依赖目标的集合,会去除重复的依赖目标 |
$+ | 所有依赖目标的集合,不会去除重复的依赖目标 |
$* | 这个是GNU make特有的,其它的make不一定支持 |
Makefile高级语法