makefile
-
makefile:整个工程的编译规则和流程【自动化编译】
-
组成:显式规则、隐式规则(自动补全某些依赖关系)、变量定义、文件指示(makefile引用makefile,makefile部分生效)和注释(shell注释:#)
-
入门结构:
- edit : main.o fun.o
cc -o edit main.o fun.o
main.o : main.c header.h
cc -c main.c
fun.o : fun.c header.h
cc -c fun.c
clean :【没有依赖关系,故成为一个指令】
rm edit main.o fun.o
- edit : main.o fun.o
-
makefile基本语法 & make 特性:
- <target_file>[object/target]: <prerequisites_file>[dependencies]
<command_line/recipe>【前有Tab键】
object依赖于文件dependencies,由后者生成前者的命令为command_line;
每当dependencies更新[target_file最后修改日期早于dependencies,或target_file不存在]时,重新生成object - make 在当前目录寻找makefile并执行其编译指令
- 理论上,可以在一个makefile中定义和编译无关的命令,如make clean 清除所有生成文件
- make 以makefile中第一个没有通配符的目标文件作为最终目标文件,一层又一层地去找文件的依赖关系,自上而下更新[后序遍历],直到最终编译出最终目标文件
- 环境变量MAKECMDGOALS 会存放你所指定的最终目标文件
- make执行时会将所有生效command_line及其结果打印出来
- make -s or make --silent 只执行不显示命令
- make -n or make --just-print 只显示命令不执行
- 每个command默认是在一个新的进程(shell)中执行,接续符(;)将多个命令在同一个shell中执行,实现命令间的承接关系
- 指定文件为makefile:make -f filename or make --file filename
- make -d directory:切换到directory目录下再执行make
- 全局忽视错误:make -i or make --ignore-errors
[如果某规则(生成object对应的command_line内容)中某条命令错误,跳过该命令]- make -k or make --keep-going [如果某规则(生成object对应的command_line内容)中某条命令错误,跳过该规则]
- make -w :执行依赖文件含有的生成命令【-wn可以查看依赖文件对应的命令】
- make -debug=:输出make的调试信息
- a:all所有的信息 [-debug=a --> -d]
- v:verbose较为详细的信息
- b:basic基本信息
- make -j :编译调用内核数目
- <target_file>[object/target]: <prerequisites_file>[dependencies]
-
伪目标文件
- all 编译所有的目标文件
- clean 删除所有被make创建的文件。
- install 安装已编译好的程序,其实就是把目标执行文件拷贝到指定路径中去。
- print 这个伪目标的功能是列出修改过的源文件。
- tar 把源程序打包备份为一个tar文件。
- dist 把tar文件压成Z文件或是gz文件
- TAGS 更新所有的目标,以备完整地重编译使用
- check和test 一般用来测试makefile的流程。
-
变量 [类似宏,直接替代]时
-
定义 obj = obj.o main.o
-
取值 (obj)【注:(obj)【注:(obj)【注:−−make−−>--make-->−−make−−>】
-
obj = .o [通配符并不会在变量定义处展开文件,而是直接将*.o代入command,如果*.o匹配不到任何的对象,变量就相当于文件’*.o’]
- 解决策略:obj = ${wildcard .o}[当.o匹配不到任何的对象时,变量为空]
- 细节:make使用的是懒惰策略,如果变量出现在command_line中,那么仅当这条command_line将被执行,变量才会在其内部展开
-
用变量定义变量 foo=$(bar) bar=var
-
定义一个变量,其值是一个空格:
- nullstring :=
space := $(nullstring) # end of the line
<用一个Empty变量来标明变量的值开始了,而后面采用“#”注释符来表示变量定义的终止,之间一个空格>
反例:dir := /foo/bar # directory [错误,宏为‘/foo/bar ’] - 替换变量中的共有的部分:$(var:a=b)
- 直接替换:bar := $(foo:.o=.c)
- 匹配替换(静态模式):bar := $(foo:%.o=%.c)
- 追加后缀:
- objects += another.o --> objects := $(objects) another.o
【如果变量之前没有定义过,那么,“+=”会自动变成“=”】
- objects += another.o --> objects := $(objects) another.o
- nullstring :=
-
复杂例子:
x = $(y)
y = z
z = Hello
a := ((((x)) --> ((((y)) --> Hellofirst_second = Hello
a = first
b = second
all = (((a_$b) --> Hello -
override = ,在makefile文件中重写覆盖命令行带入的变量或是系统环境变量
- 如果系统环境变量与makefile内部定义的变量重名,默认取makefile内部变量【类比局部全局变量】
- 如果默认取环境变量:make -e【如定义环境变量CFLAGS,即可使用统一编译参数】
-
<target…> : (override) 定义某个目标文件的局部变量,可覆写全局变量
- 例:prog.o : CFLAGS = -g 例: %.o : CFLAGS = -o
-
-
命令包(类似函数,本质是宏):
- 定义:define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef - 执行:$(run-yacc)
- 定义:define run-yacc
-
= := ?=
- 一次性赋值 := [防止循环引用]
x := foo
y := $(x) bar
x := later --> y = foo bar - 绑定赋值 =
x = foo
y = $(x) bar
x = later --> y = later bar - 条件赋值 ?= [类三目运算符]
FOO ?= bar 如果FOO没有被定义过,那么变量FOO的值就是“bar”
- 一次性赋值 := [防止循环引用]
-
优化结构
- objects = main.o kbd.o command.o display.o
edit : $(objects)
cc -o edit $(objects)
main.o : defs.h [GNU_make会自动推导file.o的依赖file.c,可省略]
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
.PHONY : clean 【表示clean是一个伪目标文件】
clean : [make视角下的依赖文件名与目标文件名只是标记符,不关心是否是文件]
-rm edit $(objects) ['-'表示忽略错误,继续执行]
- objects = main.o kbd.o command.o display.o
-
makefile引用
- include [a.make] [.mk] [$(file)] 将include_makefile文件内容在本makefile文件中展开 [makefile支持通配字符?[…]]
- 寻找include_file时默认地址为makefile当前目录及子目录 or /usr/include
- make -I [include_file_path] 可指定目录
- -include a.make [忽视include错误,不报错继续执行]
-
文件查找
- 路径前缀:VPATH=src:…/headers [路径间用:分割,make会依次在当前目录、VPATH目录中寻找目标文件和依赖文件]
- 关键字:vpath 为符合模式的文件指定搜索目录,make会按照vpath语句的先后顺序来执行搜索
- 关于include的高级特性:
- 当include_file不存在[找不到]:以include_file为名查找目标文件(object_file),并执行 [找不到object_file就报错]
- 更进一步,如果 object_file的command_line 创建了include_file:将创建成功的include_file包含进当前makefile
- 当include_file存在,将include_file包含进当前makefile
- 更进一步,如果还有同名的object_file,比较object_file与dependencies的时间先后,来决定是否执行command_line
- 当include_file不存在[找不到]:以include_file为名查找目标文件(object_file),并执行 [找不到object_file就报错]
-
多目标文件:
- bigoutput littleoutput : text.g
generate text.g -(substoutput,,(subst output,,(substoutput,,@) > @[′@ ['@[′'表示调用makefile_subst函数]
等价于: bigoutput : text.g
generate text.g -big > bigoutput
littleoutput : text.g
generate text.g -little > littleoutput
- bigoutput littleoutput : text.g
-
模式规则:
-
变量和函数在目标文件名和依赖文件名处的展开发生在make载入Makefile时,而模式规则中的%则发生在运行时
%.o : %.c
$(CC) -c $(CFLAGS) $< -o $@ [CFLAGS表示编译时所用的-options] -
静态模式规则:<targets …>: : <prereq-patterns …>
objects = foo.o bar.o
all: $(objects) [最终目标文件]
$(objects): %.o: %.c [用模式为%.c的依赖文件生成_%.o的目标文件]
$(CC) -c $(CFLAGS) $^ -o $@$(filter %.o,$(files)): %.o: %.c [调用filter函数过滤] $(CC) -c $(CFLAGS) $^ -o $@
-
${OBJ:%.c=%.o} 将OBJ中所有.o后缀文件替换成.c后缀文件
-
gcc -MM main.c :输出源文件中包含的头文件(不含库头文件)
main.o: main.c header.h
-
-
特殊字符
- ‘-’:删除、创建文件如果碰到文件不存在或者已经创建,那么希望忽略掉这个错误,继续执行
- ‘@’:不显示command_line,只打印结果
- 自动变量:
- $@:表示当前命令的目标文件
- $<:表示第一个依赖文件 $^:表示所有依赖文件
- $?:表示比目标还要新的依赖文件列表
- $:表示引用makefile定义变量的值
- $$:表示引用shell命令中定义的变量的值
- 例子:__build_one_by_one:
$(Q)set -e;
for i in $(MAKECMDGOALS); do
$(MAKE) -f $(srctree)/Makefile KaTeX parse error: Can't use function '$' in math mode at position 65: …交由Unix-shell处理,$̲i会被展开为i宏的定义,而i会展为$i(在shell看来相当于取i的值),可以看做对i捆绑两次,所以需要解绑两次
-
makefile嵌套结构:
- 主目录makefile管控子目录makefile
- subsystem:
$(MAKE) -C subdir or cd subdir && $(MAKE)
总控makefile传递变量到子级makefile:export <variable …>【总控makefile传递的变量相当于子级makefile的环境变量】
如果传递所有变量,只需要一个export即可
-
条件判断
- (可为ifeq;ifneq;ifdef;ifndef)【ifeq判断两个字符串是否相等,ifdef判断某个变量是否值为空】
【注:ifdef只是测试一个变量是否直接定义为空,其并不会逐级扩展】
【如:bar = foo = $(bar) -->ifdef(bar)==false,ifdef(foo)==true】
else
endif - 例如:根据CC变量是否等于gcc,决定生成命令
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) (normallibs)endif【注:make在读取Makefile时就计算条件表达式的值,并根据条件表达式的值来选择语句,而自动化变量是在运行时才有确切含义,因此不要把自动化变量(如(normal_libs) endif 【注:make在读取Makefile时就计算条件表达式的值,并根据条件表达式的值来选择语句,而自动化变量是在运行时才有确切含义,因此不要把自动化变量(如(normallibs)endif【注:make在读取Makefile时就计算条件表达式的值,并根据条件表达式的值来选择语句,而自动化变量是在运行时才有确切含义,因此不要把自动化变量(如@等)放入条件表达式中】
- (可为ifeq;ifneq;ifdef;ifndef)【ifeq判断两个字符串是否相等,ifdef判断某个变量是否值为空】
-
函数
-
${ } or $( )
-
字符串函数【参数是字符串或者字符串集(字符串间由空格隔开,本质也是字符串)】
- 例如:
comma:= ,
empty:=
space:= $(empty) $(empty) #定义空格
str:= a b c
str2:= $(subst (space),(space),(space),(comma),$(str)) #replace all the space in str with comma- $(subst ,,
) 字符串text中的from子串替换为to - $(patsubst ,,
) 字符串text中pattern格式的子串替换为to
- $(subst ,,
- $(strip ):去掉字串中开头和结尾的空字符
- $(findstring ,):在字串中查找字串;如果找到,那么返回,否则返回空字符串
- $(filter
- 例如:
-