3.Makefile的书写规则
- 总述:规则包含 依赖关系 和生成目标的方法 两部分
- 若第一条规则的目标很多个,则第一个目标会成为make最终的目标
3.1规则举例
foo.o: foo.c defs.h #foo模块
gcc -c -g foo.c
- foo.o文件依赖于 foo.c 和 defs.h ,若foo.c 或defs.h文件日期比foo.c 新,或者foo.o 不存在,则依赖关系产生
- gcc -c -g foo.c 告诉了make 怎么生成 foo.o ,foo.c里include了defs.h
3.2 规则的语法 - 格式
target : prerequisites (推荐写法)
command
或
target : prerequisites ; command
command
3.3规则总使用通配符
-
通配符 : “ * ”, “ ? ” ,“ […] ”
-
“ ~ ” 在文件名中有特殊的用途
1.若是“ ~/test ”,表示当前用户的 $HOME 目录下的 test 目录 2.若是“ ~hchen/test ” 表示用户 hchen 的宿主目录下的 test 目录 3.若用户没有宿主目录,则“ ~ ”根据 环境变量 " HOME "决定 window ,MS-DOS)
-
“ * ” 代替一系列后缀为 .x 的文件 用法 " *.c "
“ \ * ”代表真实字符串" * "
objects = *.o # object 的值就是等于 " *.o " ,不展开 *.o
objects : = $(wildcard *.o ) # objects 等于所有 .o 文件的集合 , 展开 *.o
3.4文件搜寻
- 特殊变量 VPATH
- 搜寻当前目录是最高优先级
- 若找不到,然后去所定义的目录去寻找
- 目录之间由 “ : ” 分隔,如 VPATH = src : …/headers
- < \pattern > 需要包含 “ % ” 字符,%.h 匹配 所有以 " .h" 结尾的文件
- <\pattern> 指定了要搜索的文件集
- <\directories>指定了文件集的搜索的目录
- 如 vpath %.h …/headers, 在…/headers的目录搜索所有“.h”结尾的文件
1. vpath <pattern> <directoriees> 为符合模式<pattern>的文件指定搜索目录<directories>
2. vpath <pattern> 清除符合模式<pattern>的文件的搜索目录
3. vpath 清除所有已被设置爱好的文件搜索目录
3.5伪目标
- 伪目标只是一个标签,不可与文件名重名
- .PHONY : clean //明确表明 clean是一个伪目标
- 可把伪目标理解为一个执行依赖文件的总开关,但是不会生成目标执行文件
- 如下面例子
all : prog1 prog2 prog3 #all是一个伪目标,不会生成执行文件 all #只会执行后面的依赖文件 .PHONY : all #声明了all是一个伪目标 prog1 : prog1.o utils.o gcc -o prog1 prog1.o utils.o prog2 : prog2.o gcc -o prog2 prog2.o prog3 : prog3.o sort.o utils.o gcc -o prog3 prog3.o sort.o utils.o
.PHONY : cleanall cleanobj cleandiff #声明都是伪目标
cleanall : cleanobj cleandiff #make cleanall
rm program
cleanobj : #make cleanobj
rm *.o
cleandiff : #make cleandiff
rm *.diff
3.6多目标
- 自动化变量 “ $@ ” ,表示目前规则中的所有生成目标的集合
- 自动化变量 “ $< ” ,表示目前规则中的依赖目标集
# $@ = bigoutput littleoutput # $@ 类似数组,依次取出目标
bigoutput littleoutput : text.g
generate text.g -$(subst output, $@) > $@
等价于
bigoutput : text.g
generate text.g -big > bigoutput
littleoutput : text.g
generate text.g -little > littleoutput
3.7静态模式
- 格式:
<targets...> : <target-pattern> : <prereq-patterns...>
<command>
objects = foo.o bar.o
all : $(objects) # 伪目标
$(objects) : %.o : %.c #从object中%.o获得 foo.o ,bar.o ,
# 依赖目标 %.c 获得 %.o 并改为 foo.c bar.c
$(CC) -c $(CFLAGS) $< -o $@
等价于
foo.o : foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o
files = foo.elc bar.o lose.o
$(filter %.o ,$(files) ) : %.o : %.c #使用过滤函数filter
# 在files 里过滤 .o 文件
$(CC) -c $(CFLAGS) $< -o $@
$(filter %.elc ,$(files) ) : %.elc : %el
emacs -f batch-byte-compile $<
3.8自动生成依赖性
- gcc -M main.c “-M”选项自动寻找源文件中包含的头文件,并生成依赖关系,GNU则为“ -MM ”
- 为每一个源文件的自动生成的依赖文件放到一个文件中,为每一个“name.c”的文件生成一个“name.d”的Makefile文件 [.d] 文件中存放着对应[.c]文件的依赖关系
%d.c : %.c
#清除所有的[.d]目标
@set -e ; rm -f $@ ; \
$(CC) -M $(CPPFLAGS) $< >$@. #将[.c]文件 生成对应的 [.d] 文件
; \ #$@后面有个“ . ”,最后生成 name.d.12345随机编号文件
sed 's,$*\.o[ :]*,\1.0 $@ : ,g' < $@. #用sed命令做了替换
> $@ ; \
rm -f $@
-
main.o : main.c def.h —>变成 main.o main.d : main.c defs.h
-
[.d]文件会自动更新了,并可自动生成
-
例子
-
include 是以按来载入文件,最载入的[.d]文件中的目标会成为默认目标
sources = foo.c bar.c
include $(sources: .c = .d)
#将sources的[.c] 文件替换成[.d]文件
4.Makefile书写命令
4.1 显示命令
- 在执行命令行前 加 “ @ ” 使make不会打印命令执行信息
- make -n/–just-print 只显示命令,不会执行命令
- make -s/–slient 全面禁止命令的显示
4.2 命令执行
- make 完成后 会回到当前Makefile所在的目录
exec:
cd /home/hchen #切换到目标目录并返回执行下一条命令
pwd #打印Makefile所在的绝对路径
exec:
cd /home/hchen ; pwd #用;写在同一行, 切换到目标目录并打印目标绝对路径
- .
4.3 命令出错 - 每当命令运行完后,make会检测每个命令的返回码
- 返回成功,make执行下一条命令
- 命令出错(命令退出码非零),make中止当前执行规则
有可能导致所有规则被终止
- 有时候,命令的出错并不表示有错误的
假如mkdir命令,创建的目录如果已经存在会导致出错,如果不希望终止命令执行,就忽略命令的出错,在Makefile 的命令行前加一个减号“ - ”
clean:
-rm -rf *.o
- 全局忽略命令出错
make -i 或make --ignore-errors
- 有一条命令出错就终止当前规则的执行,但是继续执行下一规则
make -k 或 make --keep-going
4.4 嵌套执行make
- 对不同功能的源文件进行目录分类
- 在每个目录下中创建该目录的Makefile
- 对模块编译和分段编译有非常大的好处
- 例子:加入有一个子目录叫 subdir ,这个目录下有个 Makefile 文件,来指明
这个目录下的文件的编译规则,那么总控的Makefile 写成:
总控Makefile
意思:先进入suddir目录,然后执行make命令、
$(MAKE)是 make [commend]...的总和,方便管理维护
subsystem:
cd subdir && $(MAKE)
等价于
subsystem:
$(MAKE) -C subdir
- 由上可知总控Makefile 的变量可以传递到下级的目录subdir里的Makefile
- 以上不会覆盖下层的Makefile中所定义的变量,除非指定“ -e ”参数、
> export <variable...> 传递变量到下级的Makefile中
> unexport <variable...> 不想传递变量到下级的Makefile中
- 例子
- export +变量名,是用来传递变量的
- export 不加变量名表示传递所有变量
> 例子1:
export variable = value
等价于
variable = value
export variable
等价于
export variable := value
等价于
variable := value
export variable
> 例子2:
export variable += value
等价于
variable += value
export variable
- MAKEFILES 是一个系统级的变量,始终传递变量
- 在make 后加参数 -C、-f、-h、-o 、-W ,这些参数不会往下传递
make -w 或make --print-directory 在工作时会显示进入工作目录
make -C “-w”会被自动打开
make -s “-w”总是失效
4.5 定义命令包
- 定义一个命令包
define run-yacc # define 一个名字为run-yacc的命令包
yacc $(firstword $^) # 运行yacc程序,将 $^文件 总是生成y.tab.c文件
mv y.tab.c $@ # 将y.tab.c文件改名为 $@ 文件
endef # 结束包的编译
如
foo.c:foo.y # $@ = foo.c ,$^ = foo.y
$(run-yacc) # 执行命令包 run-yacc
# yacc程序将foo.y生成y.foo.c文件
# 执行mv命令 将y.foo.c文件修改名为 foo.c