《跟我一起学Makefile》文章里,个人认为比较重要的知识点总结
-
**反斜杠(\)**是换行符的意思。这样比较便于 Makefile 的易读
-
make 会比较目标文件和 依赖文件的修改日期,如果 依赖文件的日期要比目标文件的日期要新,或者 依赖文件不存在的话,那么,make 就会执行后续定义的命令。
-
如果冒号后什么也没有,那么,make 就不会自动去找文件的依赖性,例如clean:,也就不会自动执行其后所定义的命令。但是如果一个文件在最开头,是例外,不管有没有依赖,都会被当成默认目标执行。(第一个目标文件就是默认文件)
-
为了 makefile 的易维护,在 makefile 中我们可以使用变量。makefile 的变量也就是一个字符串,可以理解成 C 语言中的宏。变量在声明时需要给予初值,而在使用时,需要给在变量名前加上“$”符号。如果你要使用真实的“$”字符,那么你需要用“$$”来表示。
-
定义伪目标更为稳健的做法是:
.PHONY : clean clean : -rm edit $(objects)
.PHONY 意思表示 clean 是一个“伪目标”。不成文的规矩是——“clean 从来都是放在文件的最后”
一个示例就是,如果你的 Makefile 需要一口气生成若干个可执行文件,但你只想简单地敲一个 make 完事,并且,所有的目标文件都写在一个 Makefile 中,那么你可以使用“伪目标”这个特性:(伪目标放在最前面,是默认目标,相当于指定了,所以也一定会执行)all : prog1 prog2 prog3 .PHONY : all prog1 : prog1.o utils.o cc -o prog1 prog1.o utils.o prog2 : prog2.o cc -o prog2 prog2.o prog3 : prog3.o sort.o utils.o cc -o prog3 prog3.o sort.o utils.o
-
注释是用“#”字符
-
在 Makefile 使用 include 关键字可以把别的Makefile 包含进来,include 的语法是:
include <filename>
,include
绝不能是[Tab]键开始。include
和<filename>
可以用一个或多个空格隔开include foo.make *.mk $(bar) 等价于: include foo.make a.mk b.mk c.mk e.mk f.mk
-
规则包含两个部分,一个是依赖关系,一个是生成目标的方法。Makefile 中只应该有一个最终目标,第一条规则中的目标将被确立为最终的目标
-
如果我们的文件名中有通配符,如:“*”,那么可以用转义字符“\”,如“
\*
"来表示真实的“*”字符,而不是任意长度的字符串 -
objects = *.o
上面这个例子,表示了,通符同样可以用在变量中。 但[*.o]不会展开,objects的值就是“*.o”。Makefile 中的变量其实就是 C/C++中的宏。如果你要让通配符在变量中展开,也就是让 objects 的值是所有[.o]的文件名的集合,那么,你可以这样:objects := $(wildcard *.o) -
当 make 需要去找寻文件的依赖关系时,你可以在文件前加上路径
-
静态模式
<targets ...>: <target-pattern>: <prereq-patterns ...> <commands>
$(objects): %.o: %.c 表示objects中的.o文件的依赖是.c 其中 .o文件是属于objects里面定义的
-
make 会把其要执行的命令行在命令执行前输出到屏幕上。当我们用“@”字符在命令行前,那么,这个命令将不被 make 显示出来。如:
@echo 正在编译 XXX 模块......
当 make 执行时,会输出“正在编译 XXX 模块......
”字串,但不会输出命令,如果没有“@”,那么,make 将输出:echo 正在编译 XXX 模块......正在编译 XXX 模块......
-
make 参数“-n”,那么其只是显示命令,但不会执行命令。可以查看makefile有没有写错
-
make 参数“-s”则是静默输出 。
make -s
禁止显示命令,但是命令执行了,这样方便修改err和warning -
-d 相当于debug -f 指定需要的makefile -i忽略所有的错误 VPATH指定依赖文件路径 -I指定头文件所在的目录 -v输出make的版本
-
定义命令包(多行变量)。定义这种命令序列的语法以“define”开始,以“endef”结束,如:
define run-yacc yacc $(firstword $^) mv y.tab.c $@ endef
可以这么使用
foo.c : foo.y $(run-yacc)
-
注意= := ?= 这三种等号的区别
-
如果我们要定义一个变量,其值是一个空格,那么我们可以这样来:
nullstring := space := $(nullstring) # end of the line
nullstring 是一个 Empty 变量,其中什么也没有,而我们的 space 的值是一个空格。先用一个 Empty 变量来标明变量的值开始了,而后面采用“#”注释符来表示变量定义的终止。如果直接用
space := #
将不会有空格。必须前面要先从一个变量算起,再用#,才会有空格因此这里需要定义一个空变量。另外,dir := /foo/bar #
只要#符号离末尾距离大于1(2345678等等),都只会空一个空格。 -
变量的高级用法:第一种是变量值的替换
foo := a.o b.o c.o bar := $(foo:.o=.c)
另外一种变量替换的技术是以“静态模式”(参见前面章节)定义的,如:
foo := a.o b.o c.o bar := $(foo:%.o=%.c)
第二种高级用法是——“把变量的值再当成变量”
x = variable1 variable2 := Hello y = $(subst 1,2,$(x)) z = y a := $($($(z)))
first_second = Hello a = first d all = $($a_$b)
-
追加变量值
我们可以使用“+=”操作符给变量追加值,如:objects = main.o foo.o bar.o utils.o objects += another.o
-
使用条件判断:ifeq ifneq ifdef ifndef
ifeq格式:<conditional-directive> <text-if-true> else <text-if-false> endif
在
<conditional-directive>
这一行上,多余的空格是被允许的,但是不能以[Tab]键做
为开始(不然就被认为是命令)。包括定义变量时,不能有tab,但是可以有空格,因为tab后代表命令示例:
ifeq ($(CC),gcc) libs=$(libs_for_gcc) else libs=$(normal_libs) endif
ifneq (<arg1>, <arg2>)
ifdef格式:
ifdef <variable-name>
如果变量<variable-name>
的值非空,那到表达式为真。否则,表达式为假。示例:
foo = ifdef foo frobozz = yes else frobozz = no endif
特别注意的是,make 是在读取 Makefile 时就计算条件表达式的值,并根据条件表达式
的值来选择语句,所以,你最好不要把自动化变量(如“$@”等)放入条件表达式中 -
函数的调用语法
函数调用,很像变量的使用,也是以“$”来标识的,其语法如下:$(<function> <arguments>)
<function>
就是函数名,<arguments>
是函数的参数,参数间以逗号“,”分隔,而函数名和参数之间以“空格”分隔 -
字符串处理函数 subst
$(subst <from>,<to>,<text>)
名称:字符串替换函数——subst。
功能:把字串<text>中的<from>字符串替换成<to>。
返回:函数返回被替换过后的字符串。
$(subst ee,EE,feet on the street),
把“feet on the street”中的“ee”替换成“EE”,返回结果是“fEEt on the strEEt”。 -
模式字符串替换函数——patsubst
$(patsubst <pattern>,<replacement>,<text>)
示例:
$(patsubst %.c,%.o,x.c.c bar.c)
把字串“x.c.c bar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.c.o bar.o”
“$(objects:.o=.c)”和“$(patsubst %.o,%.c,$(objects))”是一样的。 -
取目录函数——dir。
$(dir <names…>)
功能:从文件名序列<names>中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前
的部分。如果没有反斜杠,那么返回“./”。
返回:返回文件名序列<names>的目录部分。
示例: $(dir src/foo.c hacks)返回值是“src/ ./”。 -
取文件函数——notdir。
$(notdir <names…>) .
功能:从文件名序列<names>中取出非目录部分。非目录部分是指最后一个反斜杠(“ /”)
之后的部分。
返回:返回文件名序列<names>的非目录部分。
示例: $(notdir src/foo.c hacks)返回值是“foo.c hacks”。 -
foreach 函数 用来做循环用的
$(foreach <var>,<list>,<text>)
把参数<list>中的单词逐一取出放到参数<var>所指定的变量中,然后再执行<text>所包含的表达式
<var>最好是一个变量名,<list>可以是一个表达式,而<text>中一般会使用<var>
这个参数来依次枚举<list>中的单词。举个例子:names := a b c d files := $(foreach n,$(names),$(n).o) 一般我们可以使用“$(wildcard *.c)”来获取工作目录下的所有的.c文件列表
foreach 中的<var>参数是一个临时的局部变量,foreach 函数执行完后,参数
<var>的变量将不在作用,其作用域只在 foreach 函数当中 -
函数 wildcard
通配符“%”只能用在规则中,只有在规则中它才会展开,如果在变量定义和函数使用时,通配符不会自动展开,这个时候就要用到函数 wildcard -
我们可以给 make 命令指定一个特殊名字的 Makefile。要使用 make 的“-f”参数。
make –f hchen.mk
-
有一点需要注意的是,"%“的展开发生在变量和函数的展开之后,变量和函数的展开发
生在 make 载入 Makefile 时,而模式规则中的”%"则发生在运行时。 -
$@ 规则中的目标集合
-
$< 依赖文件集合的第一个文件,是符合规则的一系列依赖文件的集合
-
$^ 规则中所有的依赖文件集合
-
gcc 命令的时候加上-o 来指定生成的可执行文件名字
-
-c: 只编译不链接为可执行文件,编译器将输入的.c 文件编译为.o 的目标文件。
-o: <输出文件名>用来指定编译结束以后的输出文件名,如果使用这个选项的话 GCC 默认编译出来的可执行文件名字为 a.out。
-O: 对程序进行优化编译