2-Makefile初级语法
Makefile规则
1、规则语法
规则主要有2部分: 依赖关系和生成目标的方法。
语法有以下2种:
target ... : prerequisites ...
command
...
或者:
target ... : prerequisites ; command
command
...
【温馨提示】command太长,可以用 "\" 作为换行符。
2、规则中的通配符
* :: 表示任意一个或多个字符
? :: 表示任意一个字符
[...] :: ex. [abcd] 表示a,b,c,d中任意一个字符,[^abcd]表示除a,b,c,d以外的字符,[0-9]表示 0~9中任意一个数字
~ :: 表示用户的home目录
3、路径搜索
当一个Makefile中涉及到大量源文件时(这些源文件和Makefile极有可能不在同一个目录中),这时,最好将源文件的路径明确在Makefile中,便于编译时查找。Makefile中有个特殊的变量VPATH就是完成这个功能的。
指定了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
Makefile中的变量
1、变量定义(= or :=)
OBJS = programA.o programB.o OBJS-ADD = $(OBJS) programC.o # 或者 OBJS := programA.o programB.o OBJS-ADD := $(OBJS) programC.o
其中 = 和 := 的区别在于,:=只能使用前面定义好的变量, =可以使用后面定义的变量。
测试 =
# Makefile内容 OBJS2 = $(OBJS1) programC.o OBJS1 = programA.o programB.o all: @echo $(OBJS2) # bash中执行make,可以看出虽然OBJS1是在OBJS2之后定义的,但在OBJS2中可以提前使用 $ make programA.o programB.o programC.o
测试 :=
# Makefile内容 OBJS2 := $(OBJS1) programC.o OBJS1 := programA.o programB.o all: @echo $(OBJS2) # bash中执行 make, 可以看出 OBJS2 中的 $(OBJS1) 为空 $ make programC.o
2、变量替换
# 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
3、变量追加值 +=
# 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
4、变量覆盖 override
作用是使Makefile中定义的变量能够覆盖make命令参数中指定的变量。
语法:
override <variable> = <value> override <variable> := <value> override <variable> += <value>
【范例代码】使用override
# 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
Makefile命令前缀
Makefile中书写shell命令时可以加2种前缀@和-,或者不用前缀。
3种格式的shell命令区别如下:
1、不用前缀 :: 输出执行的命令以及命令执行的结果,出错的话停止执行
2、前缀 @ :: 只输出命令执行的结果,出错的话停止执行
3、前缀 - :: 命令执行有错的话,忽略错误, 继续执行
【范例代码】
# 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)",不像真正的目标那样会生成一个目标文件.
典型的伪目标是Makefile中用来清理编译过程中中间文件的clean伪目标,一般格式如下:
.PHONY: clean <-- 这句没有也行, 但是最好加上 clean: -rm -f *.o
引用其他的Makefile
语法:include <filename> (filename 可以包含通配符和路径)
【范例代码】
# 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命令中指定特定的Makefile和特定的目标。
# Makefile文件名改为 MyMake, 内容 target1: @echo "target [1] begin" @echo "target [1] end" target2: @echo "target [2] begin" @echo "target [2] end" # bash 中执行 make $ ls Makefile $ mv Makefile MyMake $ ls MyMake $ 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
make参数介绍
make的参数有很多,可以通过make -h去查看,下面只介绍几个我认为比较有用的。
参数 |
含义 |
--debug[=<options>] | 输出make的调试信息, options 可以是 a, b, v |
-j --jobs | 同时运行的命令的个数, 也就是多线程执行 Makefile |
-r --no-builtin-rules | 禁止使用任何隐含规则 |
-R --no-builtin-variabes | 禁止使用任何作用于变量上的隐含规则 |
-B --always-make | 假设所有目标都有更新, 即强制重编译 |
Makefile隐含规则
这里只列一个和编译C相关的。编译C时,.o的目标会自动推导为.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
隐含规则中的命令变量和命令参数变量
1、命令变量,书写Makefile可以直接写shell时用这些变量
下面只列出一些C相关的:
变量名 |
含义 |
RM | rm -f |
AR | ar |
CC | cc |
CXX | g++ |
【范例代码】
# Makefile 内容 all: @echo $(RM) @echo $(AR) @echo $(CC) @echo $(CXX) # bash 中执行make, 显示各个变量的值 $ make rm -f ar cc g++
2、命令参数变量
变量名 |
含义 |
ARFLAGS | AR命令的参数 |
CFLAGS | C语言编译器的参数 |
CXXFLAGS | C++语言编译器的参数 |
【范例代码】以CFLAGS为例
# test.c 内容 #include <stdio.h> int main(int argc, char *argv[]) { printf ("Hello Makefile\n"); return 0; } # Makefile 内容 test: test.o $(CC) -o test test.o # bash 中用 make 来测试 $ ll total 24K -rw-r--r-- 1 wangyubin wangyubin 69 Sep 23 17:31 Makefile -rw-r--r-- 1 wangyubin wangyubin 14K Sep 23 19:51 makefile.org <-- 请忽略这个文件 -rw-r--r-- 1 wangyubin wangyubin 392 Sep 23 17:31 test.c $ make cc -c -o test.o test.c cc -o test test.o <-- 这个是自动推导的 $ rm -f test test.o $ make CFLAGS=-Wall <-- 命令中加的编译器参数自动追加入下面的编译中了 cc -Wall -c -o test.o test.c cc -o test test.o
3、自动变量
Makefile中很多时候通过自动变量来简化书写,各个自动变量的含义如下:
自动变量 |
含义 |
$@ | 目标集合 |
$% | 当目标是函数库文件时, 表示其中的目标文件名 |
$< | 第一个依赖目标. 如果依赖目标是多个, 逐个表示依赖目标 |
$? | 比目标新的依赖目标的集合 |
$^ | 所有依赖目标的集合, 会去除重复的依赖目标 |
$+ | 所有依赖目标的集合, 不会去除重复的依赖目标 |
$* | 这个是GNU make特有的, 其它的make不一定支持 |