Makefile 由一系列的规则组成的,这些规则格式如下:
目标…… : 依赖文件集合……
命令 1
命令 2
命令列表中的每条命令必须以 TAB 键开始,不能使用空格!
命令列表中的每条命令必须以 TAB 键开始,不能使用空格!
make 命令会为 Makefile 中的每个以 TAB 开始的命令创建一个 Shell 进程去执行。
make 工具就是在 Makefile 中一层一层的查找依赖关系,并执行相应的命令。编译出最终的可执行文件。
Makefile中规则用来描述在什么情况下使用什么命令来构建一个特定的文件,这个文件就是规则的“目标”。
为了生成这个“目标”而作为材料的其它文件称为“目标”的依赖,规则的命令是用来创建或者更新目标的。
除了 Makefile 的“终极目标”所在的规则以外,其它规则的顺序在 Makefile 中是没有意义的,
“终极目标”就是指在使用 make 命令后边指定的目标,
如果没有指定具体的目标时, make 默认的那个目标,它是 Makefile 文件中第一个规则的目标,
又如果 Makefile 中的第一个规则有多个目标,那么这些目标中的第一个目标就是 make 的“终极目标”。
Makefile伪目标.PHONY, 用于只执行命令行操作。
Makefile 有一种特殊的目标——伪目标,一般的目标名都是要生成的文件,
而伪目标不代表真正的目标名,在执行
make 命令的时候通过指定这个伪目标来执行其所在规则的定义的命令。
使用伪目标的主要是为了避免 Makefile 中定义的只执行命令的目标和工作目录下的实际文
件出现名字冲突,有时候我们需要编写一个规则用来执行一些命令,但是这个规则不是用来创
建文件的。
#声明伪目标clean,用于工程清理,删除生成的.o文件
.PHONY: clean
clean:
rm -f *.o
cxx@ubuntu16:~/driver/demoMakefile$ ls
calcu.c calcu.h calcu.o input.c input.h input.o main.c main.o Makefile run
cxx@ubuntu16:~/driver/demoMakefile$
cxx@ubuntu16:~/driver/demoMakefile$ make clean
rm -f ./calcu.o ./main.o ./input.o
cxx@ubuntu16:~/driver/demoMakefile$
Makefile 打印输出echo
在 Makefile 要输出一串字符的话使用“echo”,就和 C 语言中的“printf”一样,“echo”前面加了个“@”符号,
Make 在执行的过程中会自动输出命令执行过程,在命令前面加上“@”的话就不会输出命令执行过程。
#定义了一个变量 name,并且给这个变量进行了赋值,其值为字符串“cxx”
#变量赋值 =
name = cxx
#变量追加 += “cxx666”
name += 666
# :=符表示不会使用后面定义的变量,只能使用前面已经定义好的,
curname := $(name)
#如果变量 curname 前面没有被赋值 ?= 就进行赋值
curname ?= cjx
#目标:
print:
#命令列表中的每条命令必须以 TAB 键开始,不能使用空格!
@echo curname: $(curname)
#echo输出一串字符加上“@”的话就不会输出命令执行过程
cxx@ubuntu16:~/driver/demoMakefile$ make print
curname:cxx 666
cxx@ubuntu16:~/driver/demoMakefile$
Makefile 模式规则 %
Makefile 中将对应的.c 源文件编译为.o 文件,每一个 C 文件都要写一个对应的规则,
如果工程中 C 文件很多的话显然不能这么做。
为此,我们可以使用 Makefile 中的模式规则,通过模式规则我们就可以使用一条规则来将所有的.c 文件编译为对应的.o 文件。
模式规则中,至少在规则的目标定义中要包涵 “%”,否则默认是一般规则。
目标中的“%”表示对文件名的匹配,“%”表示长度任意的非空字符串。
比如“%.c”就是所有的以.c 结尾的文件,类似与通配符, a.%.c 就表示以 a.开头,以.c 结束的所有文件。
Makefile 几个常用函数:
函数 subst 用来完成一般字符串替换:
#把字符串“my name is cxx”中的“cxx”替换为“cxx”,替换完成以后的字符串为“my nameis CXX”
$(subst cxx,CXX,my name is cxx)
函数 patsubst 用来完成 模式字符串替换
$(patsubst ,,
#将字符串“a.c b.c c.c”中的所有符合“%.c”的字符串,替换为“%.o”,替换完成以后的字符串为“a.o b.o c.o”
$(patsubst %.c,%.o,a.c b.c c.c)
函数 wildcard获取工作目录下的文件列表
通配符“%”只能用在规则中,只有在规则中它才会展开,如果在变量定义和函数使用时,
通配符不会自动展开,这个时候就要用到函数 wildcard:
#定义变量SRCS,利用wildcard函数获取当前目录下所有的.c 文件,类似“%”
SRCS = $(wildcard ./*.c)
#定义变量OBJS,利用patsubst函数将SRCS变量的所有.c源文件替换成.o文件。
OBJS = $(patsubst %.c,%.o,$(SRCS))
函数 dir 用来获取目录,使用方法如下:
#提取文件“/src/a.c”的目录部分,也就是“/src”。
$(dir </src/a.c>)
函数 notdir 此函数用于从文件名序列中提取出文件名非目录部分,也就是提取文件名,用法如下:
#提取文件“/src/a.c”中的非目录部分,也就是文件名“a.c”。
$(notdir </src/a.c>)
foreach 函数用来完成循环:
$(foreach <var>, <list>,<text>)
此函数的意思就是把参数中的单词逐一取出来放到参数中,然后再执行
包含的表达式。每次
会以空格隔开,最后当整个循环结束时,
函数 foreach 函数的返回值。
Makefile 条件判断:
与C语言一般,常用格式:
<条件关键字>
<条件为真时执行的语句>
endif
<条件关键字>
<条件为真时执行的语句>
else
<条件为假时执行的语句>
endif
条件关键字有 4 个: ifeq、 ifneq、 ifdef 和 ifndef,
四个关键字分为两对、 ifeq 与ifneq、 ifdef 与 ifndef。
ifeq 用来判断是否相等,ifneq 就是判断是否不相等, ifeq用法如下:
ifeq (<参数 1>, <参数 2>)
ifeq ‘<参数 1 >’ ,‘ <参数 2>’
ifeq “<参数 1>” , “<参数 2>”
ifeq “<参数 1>” , ‘<参数 2>’
ifeq ‘<参数 1>’ , “<参数 2>”
ifneq 的用法类似,只不过 ifneq 是用来了比较“参数 1”和“参数 2”是否不相等,如果不相等的话就为真。
“参数 1”和“参数 2”可以为函数的返回值。
ifdef 和 ifndef 的用法如下:
如果“变量名”的值非空,那么表示表达式为真,否则表达式为假。
ifdef <变量名>
ifndef 用法类似,但是含义 与ifdef 相反:
如果“变量名”的值空,那么表示表达式为真,否则表达式为假。
ifndef <变量名>
“变量名”同样也可以是一个函数的返回值。
Makefile 自动化变量:
模式规则中,目标和依赖都是一系列的文件,每一次对模式规则进行解析的时候都会是不同的目标和依赖文件,
而命令只有一行,如何通过一行命令来从不同的依赖文件中生成对应的目标?。自动化变量就是用来完成这个操作的。
所谓自动化变量就是这种变量会把模式中所定义的一系列的文件自动的挨个取出,直至所有的符合模式的文件都取完为止。
$@ :规则中的目标集合,在模式规则中,如果有多个目标的话,“$@”表示匹配模式中定义的目标集合。
$< :依赖文件集合中的第一个文件,如果依赖文件是以模式(即“%” )定义的,那么“$<”就是符合模式的一系列的文件集合。
$^ :所有依赖文件的集合,使用空格分开,如果在依赖文件中有多个重复的文件,“$^”会去除重复的依赖文件,值保留一份。
$% :当目标是函数库的时候表示规则中的目标成员名,如果目标不是函数库文件,那么其值为空。
$? :所有比目标新的依赖目标集合,以空格分开。
$+ :和“$^”类似,但是当依赖文件存在重复的话不会去除重复的依赖文件。
$* :这个变量表示目标模式中"%"及其之前的部分,如果目标是 test/a.test.c,目标模式为 a.%.c,那么“$*”就是 test/a.test
常用:$^ 所有的依赖文件 $@ 生成的目标文件 $< 第一个依赖文件
自动化变量只应该出现在规则的命令中。
cxx@ubuntu16:~/driver/demoMakefile$
cxx@ubuntu16:~/driver/demoMakefile$ ls
calcu.c calcu.h input.c input.h main.c Makefile
cxx@ubuntu16:~/driver/demoMakefile$
cxx@ubuntu16:~/driver/demoMakefile$ make
gcc -D_MACRO -Wall -c calcu.c -o calcu.o -w
gcc -D_MACRO -Wall -c main.c -o main.o -w
gcc -D_MACRO -Wall -c input.c -o input.o -w
gcc -std=c99 ./calcu.o ./main.o ./input.o -o run
cxx@ubuntu16:~/driver/demoMakefile$ ls
calcu.c calcu.h calcu.o input.c input.h input.o main.c main.o Makefile run
cxx@ubuntu16:~/driver/demoMakefile$ ./run
input two num:
6
6
6 + 6 = 12
cxx@ubuntu16:~/driver/demoMakefile$ make clean
rm -f ./main.o ./calcu.o ./input.o
cxx@ubuntu16:~/driver/demoMakefile$ ls
calcu.c calcu.h input.c input.h main.c Makefile run
cxx@ubuntu16:~/driver/demoMakefile$