跟我一起写 Makefile - 陈皓
书写规则
一、规则举例
foo.o : foo.c defs.h # foo模块
cc -c -g foo.c
二、规则语法
targets : prerequisites
command
#一行过长 /
#多行合并 ;
三、通配符
~
:$HOME
目录*
:任意长度字符?
[...]
%
四、文件搜寻
- VPATH = src:../headers
- vpath
五、伪目标
.PHONY: clean
clean:
rm *.o temp
六、多目标
$@
:目标的集合- $(subst output, $@):subst(outout, $@)返回”目标”去掉”output”的部分
七、静态模式
<targets>: <target-pattern>: <prereq-patterns>
<commands>
$<
:依赖的集合- $(filter %.o, $(files)):filter(%.o, $(files))返回”$(files)”中符合”%.o”的部分
八、自动生成依赖性
-M
:自动寻找源文件包含的头文件生成依赖文件
.d
%.d: %.c @set -e; rm -f $@; / $(CC) -M $(CPPFLAGS) $< > $@. ; / sed 's,/($*/)/.o[ :]*,/1.o $@ : ,g' < $@. > $@ ; / rm -f $@.
包含依赖文件
sources = foo.c bar.c include $(sources:.c=.d) #将变量sources以.c结尾替换为.d结尾
书写命令
一、显示命令
@<command>
:命令不会在终端上显示make -n
、make --just-print
:只显示命令,但不执行命令make -s
、make -silent
:全面禁止命令的显示
二、命令执行
三、命令出错
-<command>
:若命令执行出错,继续执行make -i
、make --ignore-errors
:忽略所有错误
四、嵌套执行
总控Makefile
subsystem: cd subdir && $(MAKE) # 等价于 subsystem: $(MAKE) -C subdir # $(MAKE):make及其参数 # subdir:次级目录
传递变量到下级Makefile
export <variable ...> unexport <variable ...>
指定传递参数
subsystem: cd subdir && $(MAKE) MAKEFLAGS # -w、--print-directory:目录变化时输出信息 # -C:默认打开-w # -s:关闭-w
五、定义命令包
定义
define run-yacc yacc $(firstword $^) mv y.tab.c $@ endef
调用
foo.c : foo.y $(run-yacc)
使用变量
一、变量基础
- 使用变量:
$<var>
、$(<var>)
、${<var>}
;使用$:$$
二、变量中的变量
访问后定义的变量
=
foo = $(bar) bar = $(ugh) ugh = Huh all: echo $(foo) #输出为""Huh",makefile允许访问后定义的变量
仅访问已定义的变量
:=
x := foo y := $(x) bar x := later
条件定义
?=
FOO ?= bar #若FOO未定义则初始化未bar,若已定义则跳过
三、变量的替换
sources = foo.c bar.c
include $(sources:.c=.d) #将变量sources以.c结尾替换为.d结尾
四、 把变量的值再当成变量
x = y
y = z
a := $($(x))
五、追加变量值
- +=
objects = main.o foo.o bar.o utils.o
objects += another.o
- override 指示符
override <variable> = <value>
override <variable> := <value>
override <variable> += <more text>
override define foo
bar
endef
六、多行变量
define two-lines-var
echo foo
echo $(bar)
endef
七、环境变量
-e
参数:系统环境变量覆盖定义变量。export
:向下层Makefile传递变量。
八、目标变量
<target ...> : <variable-assignment> //目标使用的var为局部变量
<target ...> : overide <variable-assignment>
九、模式变量
<pattern ...> : <variable-assignment> // %.o 即所有目标使用此变量
<pattern ...> : override <variable-assignment>
条件判断
一、语法
ifeq (<arg1>, <arg2>)
ifeq '<arg1>' '<arg2>'
ifeq "<arg1>" "<arg2>"
ifeq "<arg1>" '<arg2>'
ifeq '<arg1>' "<arg2>"
ifneq (<arg1>, <arg2>)
ifneq '<arg1>' '<arg2>'
ifneq "<arg1>" "<arg2>"
ifneq "<arg1>" '<arg2>'
ifneq '<arg1>' "<arg2>"
ifdef <variable-name> //测试变量是否有值
ifndef <variable-name>
使用函数
一、函数的调用
$(<function> <arguments>)
${<function> <arguments>}
二、字符串处理
- 替换
$(subst <from>,<to>,<text>)
:把字串中的字符串替换成。 - 模式替换
$(patsubst <pattern>,<replacement>,<text>)
:查找中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式,如果匹配的话,则以替换。 - 清除
$(strip <string>)
:去掉字串中开头和结尾的空字符。 - 查找
$(findstring <find>,<in>)
:在字串中查找字串。 - 过滤
$(filter <pattern...>,<text>)
:以模式过滤字符串中的单词,保留符合模式的单词。可以有多个模式。 - 反过滤
$(filter-out <pattern...>,<text>)
:以模式过滤字符串中的单词,去除符合模式的单词。可以有多个模式。 - 排序
$(sort <list>)
:给字符串中的单词排序(升序)。 - 取单词
$(word <n>,<text>)
:取字符串中第个单词。(从一开始) - 取单词串
$(wordlist <s>,<e>,<text>)
:从字符串中取从开始到的单词串。和是一个数字。 - 单词统计
$(words <text>)
:统计中字符串中的单词个数。 - 首单词
$(firstword <text>)
:取字符串中的第一个单词。
三、文件名操作
- 取目录
$(dir <names...>)
:从文件名序列中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”。 - 取文件
$(notdir <names...>)
:从文件名序列中取出非目录部分。非目录部分是指最后一个反斜杠(“/”)之后的部分。 - 取后缀
$(suffix <names...>)
:从文件名序列中取出各个文件名的后缀。 - 取前缀
$(basename <names...>)
:从文件名序列中取出各个文件名的前缀部分。 - 加后缀
$(addsuffix <suffix>,<names...>)
:把后缀加到中的每个单词后面。 - 加前缀
$(addprefix <prefix>,<names...>)
:把前缀加到中的每个单词前面。 - 连接
$(join <list1>,<list2>)
:把中的单词对应地加到的单词后面。如果的单词个数要比的多,那么,中的多出来的单词将保持原样。如果的单词个数要比多,那么,多出来的单词将被复制到中。
四、foreach 函数
$(foreach <var>,<list>,<text>)
:把参数中的单词逐一取出放到参数所指定的变量中,然后再执行所包含的表达式。names := a b c d files := $(foreach n,$(names),$(n).o) //执行结果a.o b.o c.o d.o
五、if 函数
$(if <condition>,<then-part>)
、$(if <condition>,<then-part>,<else-part>)
六、call函数
$(call <expression>,<parm1>,<parm2>,<parm3>...)
:为表达式的各参数赋值
七、origin函数
$(origin <variable>)
返回值:- “undefined” 如果从来没有定义过,origin函数返回这个值“undefined”。
- “default” 如果是一个默认的定义,比如“CC”这个变量。
- “environment” 如果是一个环境变量,并且当Makefile被执行时,“-e”参数没有被打开。
- “file” 如果这个变量被定义在Makefile中。
- “command line” 如果这个变量是被命令行定义的。
- “override” 如果是被override指示符重新定义的。
- “automatic” 如果是一个命令运行中的自动化变量。
八、shell函数
$(shell cat foo)
九、控制make
- 产生错误信息:
$(error <text ...>)
,make停止 - 产生警告信息:
$(warning <text ...>)
,make继续
make运行
一、make的退出码
- 0 —— 表示成功执行。
- 1 —— 如果make运行时出现任何错误,其返回1。
- 2 —— 如果你使用了make的“-q”选项,并且make使得一些目标不需要更新,那么返回2。
二、指定Makefile
- 查找顺序:“GNUmakefile”、“makefile”和“Makefile”
- 指定文件:make –f
三、指定目标
- 默认目标:makefile中的第一个目标
- 指定目标:make
四、检查规则
“-n” “–just-print” “–dry-run” “–recon”
- 不执行参数,这些参数只是打印命令,不管目标是否更新,把规则和连带规则下的命令打印出来,但不执行,这些参数对于我们调试makefile很有用处。
- “-t” “–touch”
- 这个参数的意思就是把目标文件的时间更新,但不更改目标文件。也就是说,make假装编译目标,但不是真正的编译目标,只是把目标变成已编译过的状态。
“-q” “–question”
- 这个参数的行为是找目标的意思,也就是说,如果目标存在,那么其什么也不会输出,当然也不会执行编译,如果目标不存在,其会打印出一条出错信息。
“-W ” “–what-if=” “–assume-new=” “–new-file=”
- 这个参数需要指定一个文件。一般是是源文件(或依赖文件),Make会根据规则推导来运行依赖于这个文件的命令,一般来说,可以和“-n”参数一同使用,来查看这个依赖文件所发生的规则命令。
五、make的参数
- “-b” “-m” 这两个参数的作用是忽略和其它版本make的兼容性。
- “-B” “–always-make” 认为所有的目标都需要更新(重编译)。
- “-C
” “–directory= ” 指定读取makefile的目录。如果有多个“-C”参数,make的解释是后面的路径以前面的作为相对路径,并以最后的目录作为被指定目录。如:“make –C ~hchen/test –C prog”等价于“make –C ~hchen/test/prog”。 “—debug[=]” 输出make的调试信息。它有几种不同的级别可供选择,如果没有参数,那就是输出最简单的调试信息。下面是的取值:
a —— 也就是all,输出所有的调试信息。(会非常的多)
b —— 也就是basic,只输出简单的调试信息。即输出不需要重编译的目标。
v —— 也就是verbose,在b选项的级别之上。输出的信息包括哪个makefile被解析,不需要被重编译的依赖文件(或是依赖目标)等。
i —— 也就是implicit,输出所以的隐含规则。
j —— 也就是jobs,输出执行规则中命令的详细信息,如命令的PID、返回码等。
m —— 也就是makefile,输出make读取makefile,更新makefile,执行makefile的信息。“-d” 相当于“–debug=a”。
- “-e” “–environment-overrides” 指明环境变量的值覆盖makefile中定义的变量的值。
- “-f=” “–file=” “–makefile=” 指定需要执行的makefile。
- “-h” “–help” 显示帮助信息。
- “-i” “–ignore-errors” 在执行时忽略所有的错误。
- “-I
” “–include-dir= ” 指定一个被包含makefile的搜索目标。可以使用多个“-I”参数来指定多个目录。 - “-j []” “–jobs[=]” 指同时运行命令的个数。如果没有这个参数,make运行命令时能运行多少就运行多少。如果有一个以上的“-j”参数,那么仅最后一个“-j”才是有效的。(注意这个参数在MS-DOS中是无用的)
- “-k” “–keep-going” 出错也不停止运行。如果生成一个目标失败了,那么依赖于其上的目标就不会被执行了。
- “-l ” “–load-average[=
隐含规则
一、使用隐含规则
foo : foo.o bar.o
cc –o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)
//自动推倒出
foo.o : foo.c
cc –c foo.c $(CFLAGS)
bar.o : bar.c
cc –c bar.c $(CFLAGS)
二、隐含规则一览
1、编译C程序的隐含规则。
“.o”的目标的依赖目标会自动推导为“.c”,并且其生成命令是“$(CC) –c $(CPPFLAGS) $(CFLAGS)”
2、编译C++程序的隐含规则。
“.o”的目标的依赖目标会自动推导为“.cc”或是“.C”,并且其生成命令是“$(CXX) –c $(CPPFLAGS) $(CFLAGS)”。(建议使用“.cc”作为C++源文件的后缀,而不是“.C”)
3、编译Pascal程序的隐含规则。“.o”的目标的依赖目标会自动推导为“.p”,并且其生成命令是“$(PC) –c $(PFLAGS)”。
4、编译Fortran/Ratfor程序的隐含规则。“.o”的目标的依赖目标会自动推导为“.r”或“.F”或“.f”,并且其生成命令是:
“.f” “$(FC) –c
(FFLAGS)”“.F”“$(FC)–c$(FFLAGS)
(
F
F
L
A
G
S
)
”
“
.
F
”
“
$
(
F
C
)
–
c
$
(
F
F
L
A
G
S
)
(CPPFLAGS)”
“.f” “$(FC) –c $(FFLAGS) $(RFLAGS)”
5、预处理Fortran/Ratfor程序的隐含规则。
“.f”的目标的依赖目标会自动推导为“.r”或“.F”。这个规则只是转换Ratfor或有预处理的Fortran程序到一个标准的Fortran程序。其使用的命令是:
“.F” “$(FC) –F $(CPPFLAGS)
(FFLAGS)”“.r”“$(FC)–F$(FFLAGS)
(
F
F
L
A
G
S
)
”
“
.
r
”
“
$
(
F
C
)
–
F
$
(
F
F
L
A
G
S
)
(RFLAGS)”
6、编译Modula-2程序的隐含规则。
“.sym”的目标的依赖目标会自动推导为“.def”,并且其生成命令是:“
(M2C)
(
M
2
C
)
(M2FLAGS) $(DEFFLAGS)”。“
三、隐含规则使用的变量
1、关于命令的变量。
AR
函数库打包程序。默认命令是“ar”。
AS
汇编语言编译程序。默认命令是“as”。
CC
C语言编译程序。默认命令是“cc”。
CXX
C++语言编译程序。默认命令是“g++”。
CO
从 RCS文件中扩展文件程序。默认命令是“co”。
CPP
C程序的预处理器(输出是标准输出设备)。默认命令是“$(CC) –E”。
FC
Fortran 和 Ratfor 的编译器和预处理程序。默认命令是“f77”。
GET
从SCCS文件中扩展文件的程序。默认命令是“get”。
LEX
Lex方法分析器程序(针对于C或Ratfor)。默认命令是“lex”。
PC
Pascal语言编译程序。默认命令是“pc”。
YACC
Yacc文法分析器(针对于C程序)。默认命令是“yacc”。
YACCR
Yacc文法分析器(针对于Ratfor程序)。默认命令是“yacc –r”。
MAKEINFO
转换Texinfo源文件(.texi)到Info文件程序。默认命令是“makeinfo”。
TEX
从TeX源文件创建TeX DVI文件的程序。默认命令是“tex”。
TEXI2DVI
从Texinfo源文件创建军TeX DVI 文件的程序。默认命令是“texi2dvi”。
WEAVE
转换Web到TeX的程序。默认命令是“weave”。
CWEAVE
转换C Web 到 TeX的程序。默认命令是“cweave”。
TANGLE
转换Web到Pascal语言的程序。默认命令是“tangle”。
CTANGLE
转换C Web 到 C。默认命令是“ctangle”。
RM
删除文件命令。默认命令是“rm –f”。
2、关于命令参数的变量
下面的这些变量都是相关上面的命令的参数。如果没有指明其默认值,那么其默认值都是空。
ARFLAGS
函数库打包程序AR命令的参数。默认值是“rv”。
ASFLAGS
汇编语言编译器参数。(当明显地调用“.s”或“.S”文件时)。
CFLAGS
c语言编译器参数。
CXXFLAGS
C++语言编译器参数。
COFLAGS
RCS命令参数。
CPPFLAGS
C预处理器参数。( C 和 Fortran 编译器也会用到)。
FFLAGS
Fortran语言编译器参数。
GFLAGS
SCCS “get”程序参数。
LDFLAGS
链接器参数。(如:“ld”)
LFLAGS
Lex文法分析器参数。
PFLAGS
Pascal语言编译器参数。
RFLAGS
Ratfor 程序的Fortran 编译器参数。
YFLAGS
Yacc文法分析器参数。
四、隐含规则链
- 在“隐含规则链”中,禁止同一个目标出现两次或两次以上,这样一来,就可防止在make自动推导时出现无限递归的情况。
五、定义模式规则
- 1、模式规则介绍
- %.o : %.c ;
六、后缀规则
七、隐含规则搜索算法
使用make更新函数库文件
一、函数库文件的成员
- archive(member)
二、函数库成员的隐含规则
三、函数库文件的后缀规则
.c.a:
$(CC) $(CFLAGS) $(CPPFLAGS) -c
<−o$∗.o$(AR)r$@
<
−
o
$
∗
.
o
$
(
A
R
)
r
$
@
<script type="math/tex" id="MathJax-Element-4">< -o \$*.o \$(AR) r \$@ </script>*.o
$(RM) $*.o
其等效于:
(%.o) : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o
∗.o$(AR)r$@
∗
.
o
$
(
A
R
)
r
$
@
*.o
$(RM) $*.o