Makefile
-
工程只有一两个 C 文件,用GCC编译器还好,输入的命令不多,文件几十、上百就需要make工具。
-
描述哪些文件需要编译、哪些需要重新编译的文件就叫做 Makefile,Makefile 就跟脚本文件一样,Makefile 里面还可以执行系统命令,使用时只需make一下。
-
IDE集成开发环境(Integrated Development Environment),对Makefile进行了封装,使用时直接编译C文件就好。
-
Linux下,使用GCC编译器,需要懂Makefile,通过 Makefile 了解整个工程的处理过程。
如何编写Makefile
gcc -c main.c
gcc -c input.c
gcc -c calcu.c
......
gcc main.o input.o calcu.c ... -o main
# 如果只修改了calcu.c 这个文件,只需要下面两条命令
gcc -c calcu.c
gcc main.o input.o calcu.o -o main
但是如果需要修改的文件较多,就需要满足下面的条件的工具:
1、如果工程没有编译过,那么工程中的所有.c 文件都要被编译并且链接成可执行程序。
2、如果工程中只有个别 C 文件被修改了,那么只编译这些被修改的 C 文件即可。
3、如果工程的头文件被修改了,那么我们需要编译所有引用这个头文件的 C 文件,并且链接成可执行文件。
写Makefiel 文档:
行首需要空格的话,使用TAB键,而非空格键。然后直接make执行就行。
main: main.o input.o calcu.o
gcc -o main main.o input.o calcu.o
main.o: main.c
gcc -c main.c
input.o: input.c
gcc -c input.c
calcu.o: calcu.c
gcc -c calcu.c
clean:
rm *.o
rm main
最后一个规则目标是 clean,它没有依赖文件,因此会默认为依赖文件都是最新的,所以其对应的命令不会因为make
而执行。
需要使用命令“make clean”,执行以后才会删除当前目录下所有的.o 文件以及 main。
总结:
1、make 命令会在当前目录下查找以 Makefile(makefile 其实也可以)命名的文件。
2、当找到 Makefile 文件以后就会按照 Makefile 中定义的规则去编译生成最终的目标文件。
3、当发现目标文件不存在,或者目标所依赖的文件比目标文件新(也就是最后修改时间比目标文件晚)的话就会执行后面的命令来更新目标。
Makefile语法
1、规则格式
目标... : 依赖文件集合...
命令 1
命令 2
...
如果要更新目标,就必须先更新它的所有依赖文件,如果依赖文件中的任何一个有更新,那么目标也必须更新.
2、变量
#Makefile 变量的使用 objects
objects = main.o input.o calcu.o
main: $(objects)
gcc -o main $(objects)
注释用 #
, 变量的引用方法:$(变量名)
3、三种赋值符号
=
name = zzk
curname = $(name)
name = zuozhongkai
# output:curname:zuozhongkai,=可以将变量的真实值推到后面去定义
print:
@echo curname: $(curname)
在 Makefile 要输出一串字符的话使用“echo”,在命令前面加上“@”的话就不会输出命令执行过程。
:=
name = zzk
curname := $(name)
name = zuozhongkai
# output:zzk,:=不会使用后面定义的变量,和=有区别
print:
@echo curname: $(curname)
?=
curname ?= zuozhongkai
如果变量 curname 前面没有被赋值,那么此变量就是“zuozhongkai”,如果前面已经赋过值了,那么就使用前面赋的值。
+=
objects = main.o inpiut.o
objects += calcu.o
给前面已经定义好的变量添加一些字符串进去,此时就要使用到符号“+=”.
4、模式规则
模式规则中,至少在规则的目标定定义中要包涵%
,否则就是一般规则,目标中的“%”表示对文件名的匹配,“%”表示长度任意的非空字符串,比如“%.c”就是所有的以.c 结尾的文件,类似与通配符,a.%.c 就表示以 a.开头,以.c 结束的所有文件。
main: $(objects)
gcc -o main $(objects)
%.o : %.c
#命令
clean:
rm *.o
rm main
5、Makefile自动化变量
常用的三种:$@
, $<
,$^
6、伪目标
使用伪目标主要是为了避免 Makefile 中定义的执行命令的目标和工作目录下的实际文件出现名字冲突,有时候我们需要编写一个规则用来执行一些命令,但是这个规则不是用来创建文件的。
即当make clean
时,只执行rm
的两条指令。
clean:
rm *.o
rm main
如果工作目录中有clean文件,执行make clean
时,不会执行后面的命令,清理工程的功能无法完成,为了避免该问题,可以将 clean 声明为伪目标,声明方式如下
.PHONY : clean
objects = main.o input.o calcu.o
main: $(objects)
gcc -o main $(objects)
.PHONY : clean
%.o : %.c
gcc -c $<
clean:
rm *.o
rm main
7、条件判断语句
<条件关键字>
<条件为真时执行的语句>
else
<条件为假时执行的语句>
endif
条件关键字有:ifeq、ifneq、ifdef 和 ifndef
ifeq (<参数 1>, <参数 2>)
ifeq ‘<参数 1 >’,‘ <参数 2>’
ifeq “<参数 1>”, “<参数 2>”
ifeq “<参数 1>”, ‘<参数 2>’
ifeq ‘<参数 1>’, “<参数 2>”
上面都是用来比较“参数 1”和“参数 2”是否相同,如果相同则为真,“参数 1”和“参数 2”可以为函数返回值。ifneq 的用法类似,只不过 ifneq 是用来了比较“参数 1”和“参数 2”是否不相等,如果不相等的话就为真。
8、函数
函数用法:
$(函数名 参数集合)
# 或者
${函数名 参数集合}
- subst
# 将字符串<text>中的<from>内容替换为<to>,函数返回被替换以后的字符串
$(subst <from>,<to>,<text>)
- patsubst
# 查找字符串<text>中的单词是否符合模式<pattern>,如果匹配就用<replacement>来替换掉,<pattern>可以使用通配符“%”,表示任意长度的字符串,函数返回值就是替换后的字符串
$(patsubst <pattern>,<replacement>,<text>)
# 将字符串“a.c b.c c.c”中的所有符合“%.c”的字符串,替换为“%.o”,替换完成以后的字符串为“a.o b.o c.o”。
$(patsubst %.c,%.o,a.c b.c c.c
- dir
# 从文件名序列<names>中提取出目录部分,返回值是文件名序列<names>的目录部分
$(dir <names…>)
# 提取文件“/src/a.c”的目录部分,也就是“/src”。
$(dir </src/a.c>)
- notdir
# 从文件名序列<names>中提取出文件名非目录部分
$(notdir <names…>)
# 提取文件“/src/a.c”中的非目录部分,也就是文件名“a.c”。
$(notdir </src/a.c>)
- foreach
# 把参数<list>中的单词逐一取出来放到参数<var>中,然后再执行<text>所包含的表达式。每次<text>都会返回一个字符串,循环的过程中,<text>中所包含的每个字符串会以空格隔开,最后当整个循环结束时,<text>所返回的每个字符串所组成的整个字符串将会是函数 foreach 函数的返回值。
$(foreach <var>, <list>,<text>)
- wildcard
# 通配符“%”只能用在规则中,只有在规则中它才会展开,如果在变量定义和函数使用时,通配符不会自动展开,这个时候就要用到函数 wildcard
$(wildcard PATTERN…)
# 获取当前目录下所有的.c 文件,类似“%”
$(wildcard *.c)