1.Makefile 基本格式
目标列表:参数列表(依赖列表)
TAB 执行命令
执行条件:如果参数列表中有一个或者多个文件比目标列表中文件新的话,就会执行命令,
当目标列表中的目标不存在的时候,也会执行对应的命令
注意命令列表一定要以 tab 键进行开头,否则执行时会报错:
*** missing separator. 没有以 TAB键开头
2. Makefile 基本语法/概念
使用 # 号作为注释的方法
Makefile 文件名:
大多是 Makefile 支持 makefile 和 Makefile 这两个文件名
引用其他 Makefile
引用其他 Makefile 的方法(形式如下):
include a.mk #引用单个 Makefile
include a.mk b.mk c.mk #包含其他 Makefile
include *.mk #使用通配符包含其他Makefile
include $(Bar) #使用变量包含其他Makefile
如果文件没有找到的话,make 会生成一条警告信息,但是不会产生致命的错误,它会继续载入其他文件,一旦完成 makefile 的读取,make 会重试这些没有找到,或者是不能读取的文件,如果还是不行,make 才会出现一条致命信息
如果要求 makefile 忽略文件无法找到的问题,可以使用下面的形式:
-include a.mk
伪目标
举例: .PHONY:ALL (注意 PHONY 后面要加上冒号)
表示的是不会生成 ALL 这个目标文件,但是要执行 ALL 下面的命令,Makefile 中的第一个目标会被设置为默认目标,也就是直接执行 make 命令不加参数执行生成的目标
注意在实际中也可以将伪目标定义为变量的形式:
.PHONY := $(PHONY)
$(PHONY) := ALL Romfs modules
同样伪目标也可以成为依赖:
clean: cleanobj cleandiff
-rm program
cleanobj:
-rm -rf *.o
cleandiff:
-rm -rf *.diff
多目标
一个makefile 单元可以产生多个目标,比如:
bigoutput littleouptut : text.g
generate text.g -$(subst output,, $@) > $@
等同于下面的代码:
bigoutput: text.g
generate text.g -big > bigoutput
littleoutput:text.g
generate text.g -little > littleoutput
3. Makefile 中的变量
基本定义
变量名可以使用字母,数字或者下划线组成:
var := val1
var = val1
var ?= val1
= 左侧的值是变量名,右侧的值是变量内容,右边的值不一定要是已经定义的值
: = 左侧的值是变量名,右侧的值是变量内容,右边的值不一定要是已经定义的值
?= 如果变量没有被定义,那么定义的值就是变量后面的值,如果变量已经被定义,那么现在的值将会什么都不做
变量还有一种形式是 += 操作,如果变量之前有被定义过,那么会自动继承前一次的变量 比如:
variable := value
variable += more
等同于
variable := value
variable := value $(varibale)
变量的使用举例如下:
edit : file1.o file2.o file3.o file4.o
cc -o edit file1.o file2.o file3.o file4.o
需要在两个地方都写上 file1.o file2.0 file3.o file4.o 由此引出了使用变量的方法,变量名通常叫
objects,OBJECTS,objs,OBJS,obj 或者是 OBJ
object = file1.o file2.o file3.o file4.o
edit :$(object)
cc -o edit $(object)
变量可以组合使用:
# 变量可以组合起来使用
subdir = $(currentdir)/include
currentdir_t := $(currentdir)_t
变量的引用形式
$(var) 或者 ${var} $$(表示真实的 \$\$ 字符)
变量的使用范围:可以在目标,依赖或者命令中
$(object) : def.h
cc -o $@ ......
target : $(object)
cc -o tareget
环境变量
Makefile 中可以使用环境变量的值,但是如果 Makefile 中重新定义,那么会使用 Makefile 中定义的值,使用 make -e 选项,会默认使用环境变量中的值
规则变量
我们可以只针对某个规则设置一个变量,该变量只在某个规则中生效,比如:
eg:
prog: CFLAG = -g 不管全局的 CFLAG 是什么 在 prog 这个规则中 CFLAG都是 -g
prog : prog.o foo.o bar.o
$(CC) $(CFLAGS) prog.o foo.o bar.o
prog.o : prog.c
$(CC) $(CFLAGS) prog.c
foo.o : foo.c
$(CC) $(CFLAGS) foo.c
bar.o : bar.c
$(CC) $(CFLAGS) bar.c
自动化变量
Makefile 中根据自动推导的规则自动推导出来的变量:
$< 依赖项目中第一个依赖项目的名称,注意是和 $^ 有区别,并且是一个个取出来的(往左第一个)
$@ 目标的集合,可以包含多行(@ 引申为 aim 表示目标)
$^ 所有依赖项目的集合,以空格作为分隔符 (帽子底下都是)
$+ 等同于 $^ 只是不去除重复的依赖目标
target: pre
CC -o ......
$@ 等同于 target
$< 等同于 pre
命令行传入的变量
Makefile 支持从命令行中传入变量
多行变量
Makefile 中支持将多行变量链接在一起,类似于函数的功能:
define MSTAR_LOGO
@echo "# __ __ _ ";
@echo "#| \/ |___| |_ __ _ _ __ ";
@echo "#| |\/| / __| __/ _\` | '__|";
@echo "#| | | \__ \ || (_| | | ";
@echo "#|_| |_|___/\__\__,_|_| ";
@echo " ";
endef
.PHONY : clean
clean:
@pwd
@cd ~;pwd #注意变量的生效域
@pwd
$(MSTAR_LOGO) # we use multi line var here
@echo "See the environment variable $(PATH)"
@echo "We now in clean command"
4. Makefile 中的命令
显示命令
makefile 中的命令 @echo compile module now
如果前面不加上@ 执行时就会显示,显示了两遍
echo compile module now
compile module now
命令前面加上 @ 就只会显示一遍
命令执行
clean :
cd /home/tony.chen
pwd // 上面一句话不会起到作用,任然会打印 makefile 的 目录
如果命令像这样分行,就不会产生任何作用,需要改成下面的形式:
clean: cd /home/tony.chen;pwd // 会打印 /home/tony.chen
命令出错码
每个命令执行完成之后,makefile 都会检测命令的返回值,如果返回值为成功,makefile 将会继续执行下一条指令,如果命令的返回状态码的值为非0,则会终止当前的规则,使用 $? 判断上一条命令的返回值
但是某些命令的错误行为应该是被忽略的,比如 mkdir 或者 rm -rf 这时可以使用命令:
-rm -rf ./*.o 来使用,Makefile 会默认忽略这个规则的错误
Makefile 的出错码 $? :
0: 表示执行成功
1:运行过程中出现任何错误都返回1
2:不太常用
嵌套执行
subsystem:
cd subdir&&$(MAKE)
subsystem:
$(MAKE) -c subdir
定义 MAKE 变量的含义是,也许我们的 make 需要一些参数,所以定义成一个变量比较容易维护,这两个例子都是进入 subdir,然后执行 make 命令
变量的传递
上面这种多个 Makefile 嵌套的写法我们称为总控 Makefile,它可以将变量传递到下层的 makefile 中,传递变量到下层 Makefile 中的方法如下:
export var
unexport var
eg:
export var = value1
export var := value1
export var += value1
如果要传递所有的变量,只需要一个 export 就可以了,表示传递所有的变量。
需要注意的是,有 shell 和 MAKEFLAGS 这两个变量总是会向下传递,如果不想传递该参数,可以使用下面的形式:
subsystem:
cd subdir && $(MAKE) MAKEFLAGS=
补充一个有用的参数 -w ,使用 make -w 执行,可以看到命令进入该目录到离开该目录的过程,如果使用 -C 命令则默认会打开该选项。