参考文档
Makefile里面有什么
Makefile的格式就是(这是一个最简单的模板)
target ... : prerequisites ...
command
...
...
更为复杂的Makefile包含了五个东西: 显式规则 、 隐晦规则 、 变量定义 、 文件指 示和注释
其中,规则包括 目标 目标的依赖 和 命令
显示规则必须写入目标 目标的依赖 和 命令
隐晦规则,也可以成为隐含规则,有时只用写一个目标就可以, 依赖和命令由make推导
变量定义包括很多,下面有写
文件指示就是include,分为三块,下面有写
注释就是用 # 符号来注释掉一些字符
更为复杂的可以包括以下东西
1. 隐含规则
内建规则(预设置规则)
模式规则
后缀规则(老式风格)
2. 目标
1. 真·目标
2. 伪目标(所有的伪目标最好用.PHONY标识一下)
3. 命令
1. 命令的话可以是shell中的命令,默认,make会以UNIX的 标准Shell,也就是 /bin/sh 来执行命令
2. 命令中可以用||或&&等关系运算符
4. 变量
1. 环境变量
2. 键入make 命令带的参数
3. Makefile自带的默认变量
4. 自动化变量 (存在模式规则中)
5. 自己定义的变量
5. 函数
1. Makefile自带的函数
2. 自定义的函数
6. 条件判断(这个不是函数,因为函数都有自己的语法,if明显和函数使用的语法不一样,函数中有一个if函数和条件判断功能一致)
7. 文件指示
1. Makefile引用另一个Makefile
2. 指定Makefile中有效的部分
3. 定义一个多行的命令
Makefile的精髓
prerequisites中如果有一个以上的文件比target文件要新的话, command所定义的命令就会执行
如果一个目标文件A不存在,则可以把目标文件A看成为旧文件
Makefile的执行顺序
- 依次读取变量“MAKEFILES”定义的makefile文件列表//建议不要定义该变量
- 读取工作目录下的makefile文件(根据命名的查找顺序“GNUmakefile”,“makefile”,“Makefile”,首先找到那个就读取那个)
- 依次读取工作目录makefile文件中使用指示符“include”包含的文件
- 查找重建所有已读取的makefile文件的规则(如果存在一个目标是当前读取的某一个makefile文件,则执行此规则重建此makefile文件,完成以后从第一步开始重新执行)
- 初始化变量值并展开那些需要立即展开的变量和函数并根据预设条件确定执行分支
- 根据“终极目标”以及其他目标的依赖关系建立依赖关系链表
- 执行除“终极目标”以外的所有的目标的规则(规则中如果依赖文件中任一个文件的时间戳比目标文件新,则使用规则所定义的命令重建目标文件)
- 执行“终极目标”所在的规则
概念
目标 : 从第一列开始写,并且不是以 - 打头,并且不是以Tab键打头,并且不包含 = ,那么他就是目标.
命令 : 命令一般都是以Tab键为开头的.
规则链 : 目标及目标的依赖及命令
一般来说,每个源文件都应该对应于一个中间目标文件.
编译和链接
编译器只检查是否声明,不检查定义,例如extern.如果你在源文件中连声明都不写,肯定是有问题的
链接会去找定义
1/隐含规则
不写目标的规则,但是make确实按照预想的规则(依赖和命令)生成了目标,为什么?
那是因为make自动推导了目标的规则,为什么make 会自动推导出这些规则?
因为这些东西已经写到了make的源码里.
可以通过make -R 来取消对隐含规则中的变量的重定义
内建规则:
写到了make源码中.
可以通过make -r 取消内建规则
模式规则:
$(var) : %.o:%.c //如果var = a.o b.o 则该行被替换为 a.o b.o : a.c b.c
%.o : %.c //
$(var: %.o = %.c) //如果var = a.o b.o 则该行被替换为 a.c b.c
%.o : CFLAGS = -O //所有以 .o结尾的目标,规则链中的CFLAGS变量值为 -O
%的展开在命令运行时展开
后缀规则:
系统定义在目标.SUFFIXES 的依赖目标
make的参数 -r 或 -no-builtin-rules 也会使用得 默认的后缀列 表为 空 。 而变 量 SUFFIXE被用 来定义 默认的后缀列 表, 你可以用 .SUFFIXES 来改变后缀列 表, 但请不要改变变 量SUFFIXE 的值 。
libhello.a(hello.o):
会执行
cc -c -o hello.o hello.c
ar rv libhello.a hello.o
r - hello.o
rm hello.o
2/目标
目标 : 从第一列开始写,并且不是以 - 打头,并且不是以Tab键打头,并且不包含 = ,那么他就是目标.
其实也不能这么讲,还有include 还有ifeq 还有很多
真·目标
真目标实实在在的对应一个目标文件
伪目标
伪目标不对应任何文件,只是对应一个规则
伪目标最好用.PHONY标识,要不然伪目标文件存在的话,规则就不执行了
如果我目录中有clean,且没有依赖关系,则make判断这个文件是最新的,所以不需要执行下面的命令.
clean:
rm *.o temp
而下面可以忽略这个规则
.PHONY:clean
clean:
rm *.o temp
3/命令
make会以UNIX的标准Shell,也就是 /bin/sh 来执行命令
命令就是sh中可以执行的命令,支持脚本中所有的规则
减号放在命令的开头的意思是除了问题不要管,继续做
#for循环示例
dirs=xiaowang xiaohuang xiaohua xiaolei
all:
@for name in $(dirs); \
do \
echo $$name; \
done
结果
linux@ubuntu:~/test$ make
xiaowang
xiaohuang
xiaohua
xiaolei
4/变量
环境变量
也就是在shell中定义的变量
键入make 命令带的参数
也就是make后面带的
Makefile自带的默认变量(特殊变量)
像CURDIR这个变量就是Makefile自带的
.IGNORE
自己定义的变量
一般变量的定义
var = aaa bbb ccc
引用
$(var)
通配符在变量定义中的使用
var = *.o//表示就是找*.o的文件
如果想用通配符的特性的话
var = $(wildcard *.o)//wildcard是makefile的关键字
没有定义为空
= 前面的变量能使用后面的变量
:= 前面的变量不能使用后面的变量// 除了行首提到的,这个:= 和 =没有太大区别
?= 没定义,就定义
+= 追加
变量高级用法
$(foo:.o=.c)
$(foo:%.o=%.c)
foo变量中 .o文件结尾换成.c
传递的话,只能有命令行和export声明的变量才能传递
shell中声明的环境变量可以引入,但是可以被重写,重写的话用override
如果make -e,则不会被覆盖
重写
override同样是针对于系统环境传入的变 量, 或是make命令 行指定的变 量。
voerride A :=6
#目标变量
tar: A = 666 B=888
tar:
echo $(A)
echo $(B)
##目标变量
A =5
%.o : A = 6
.PHONY:a.o b.o
a.o:
echo $(A)
b.o:
echo $(A)
自动化变量(存在模式规则中,7个,且这7个分别可以与D结合,与F结合)
该变量会把模式中定义的一系列文件自动挨个取出,直至所有的符合模式的文件都取完.
只应出现在规则的命令中
$@ : 表示规则中的目 标文件集
$% : 当目标是函数库文件中,表示规则中的目标成员名.否则,表示为空
$< : 依赖目标中的第一个目标名字.若目标为%定义,则表示目标集中的一个一个取出
$? : 所有比目标新的依赖目标的集合
$^ : 所有的依赖目标的集合(不重复)
$+ : 所有的且可能重复的依赖目标的集合
$* : 目标模式中 % 及其之前的部分
与D结合表示目录部分$(@D) ... $(*D)
与F集合表示文件部分$(@F) ... $(*F)
注意:makefile中有$0 $1 的概念,但是这个概念是在函数中
$0是make,$1是make的第一个参数的说法是错误的
make是在读取Makefile时就计算条件 表达式的值, 并根据条件 表达式的值来选择语句.所以,你最好不要把自 动化变 量(如 $@ 等) 放入条件 表达式中,因为自动化变量是在运行时才有的。
5/函数
自己定义函数及引用
定义的话可以直接用变量定义一个一行的函数(带参或不带参)
也可以用define 来定义一个多行的函数(带参或不带参)
调用的语法
$(<function> <arguments>)
${<function> <arguments>}
参数间以 逗号 , 分隔, 而函数名和参数之间以“ 空格”分隔 。
自定义函数
#不带参
define name
echo hello1
echo hello2
endef
#带参数
dirs= ahua alei axia
define fun
@for one in $1;\
do\
echo $$one;\
done
endef
all:$(dirs)
$(call fun, $^)
ahua:
alei:
axia:
自带函数
$(foreach <var>,<list>,<text>)
all:
$(foreach N,a b c d,echo $(N))
$(if <condition>,<then-part>,<else-part>)
all:#这个一直是相等
$(if ($(A)=1),echo xiangdeng,echo budeng)
$(call <expression>, <parm1>, <parm2>, ..., <parmn>)
调用自己创建的函数
reverse = $(1) $(2)
foo = $(call reverse, a, b)
all:
@echo $(foo)
$(origin <variable>)
A =3
all:
echo $(origin b)
$(shell command)
$(error text)
$(warning test)
6/条件判断
条件判断
A = 1
all:
ifeq ($(A),1) ##或者ifeq "$(A)" "1" #或者ifneq "$(A)" "1"
echo xiangdeng
else
echo budeng
endif
注意: ifeq 后面必须有个空格
ifndef A ##ifdef A
else
endif
7/文件指示
include
include makefile.parm
include Kbuild.include
define
其实这个可以定义一个多行的命令
可以定义一个多行的函数
指定有效部分还没找到是怎么回事
make的执行参考
make -n 看命令执行顺序
make -i 忽视错误
其他
问题:关于那个隐含规则的概念还不是很清楚
makefile文件中调用make
会有如下log : make[1]
顶层Makefile中的make , 不管是否切换目录,都会有该Log生成
makefile 删除文件
包括什么文件
.PRECIOUS: %.i // 告诉 make 保留后缀为 .i 的中间文件
.SECONDARY: // make 将保留所有中间文件
- makefile 删除 .SUFFIXES 指定的文件 (后缀规则)
.SUFFIXES: .hack .win
一个用于应用程序的简单makefile
# 可自动处理 makefile 工程中 .c 对 .h 的依赖关系
CC = gcc
CFLAGS = -Wall -Werror
SRC = main.c
OBJ = $(SRC:.c=.o)
DEP = $(SRC:.c=.d)
EXECUTABLE = program
all: $(EXECUTABLE)
$(EXECUTABLE): $(OBJ)
$(CC) $(CFLAGS) $(OBJ) -o $@
-include $(DEP)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
$(CC) $(CFLAGS) -MM $< > $*.d
clean:
rm -f $(OBJ) $(DEP) $(EXECUTABLE)
.PHONY: all clean