概述
makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译
程序的编译和链接
无论是C、C++、还是pas,首先要把源文件编译成中间代码文件,这个动作叫做编译(compile)。然后再把大量的 Object File合成执行文件,这个动作叫作链接(link)。
编译时,编译器需要的是语法的正确,函数与变量的声明的正确。
链接时,主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O文件或是OBJ文件)来链接我们的应用程序。编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在UNIX下,是Archive File,也就是 .a 文件。
总结一下,源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error)
如果prerequisites文件的日期要比targets文件的日期要新,或者target不存在的话,那么,make就会执行后续定义的命令
clean不是一个文件,它只不过是一个动作名字,有点像c语言中的lable一样,其冒号后什么也没有,那么,make就不会自动去找它的依赖性,也就不会自动执行其后所定义的命令。要执行其后的命令(不仅用于clean,其他lable同样适用),就要在make命令后明显得指出这个lable的名字。这样的方法非常有用,我们可以在一个makefile中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。
VPATH特殊变量
当make需要去找寻文件的依赖关系(此处注意在指令中VPATH的设置无效)时,可以在文件前加上路径,但最好的方法是把一个路径告诉make,让make自动找。
Makefile文件中的特殊变量“VPATH”就是完成这个功能的,如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make就会在当前目录找不到的情况下,到所指定的目录中去找寻文件了。
all目标
放在第一个目标处成未文件的终极目标,后面的依赖为要生成的可执行文件;
目标若无依赖,则认为目标是最新的,不执行指令;
伪目标总是会被执行;
静态模式
如果我们的<target-pattern>定义成“%. o”,意思是我们的<target>集合中都是以“.o”结尾的,而如果我们的<prereq-patterns>定义成 “%.c”,意思是对<target-pattern>所形成的目标集进行二次定义,其计算方法是,取<target- pattern>模式中的“%”(也就是去掉了[.o]这个结尾),并为其加上[.c]这个结尾,形成的新集合。
filter函数
$(filter %.o,$(files))表示调用Makefile的filter函数,过滤“$files”集,只要其中模式为“%.o”的内容
ifeq ($(strip $(PLATFORM)), $(filter $(PLATFORM), xinchu android5))
跟我一起写Makefile:
http://wiki.ubuntu.org.cn/跟我一起写Makefile:MakeFile介绍#makefile.E7.9A.84.E8.A7.84.E5.88.99
make -n : 只显示会执行的指令;
执行时遇到错误
格式
目标:依赖
.PHONY: clean
命令
注释
注释: #;
若要输入#,则使用\#
命令要以Tab键开头;
基本语法
#为注释符。
@echo表示禁止echo回显。
= 、:= 与?=的区别
1) :=为立刻赋值,如B :=$(A)时,它只会到这句语句之前去找A的值,如A没有定义所以什么都没有输出。
2) =不是立刻赋值,而是从makefile文件最后往前找。
3) ?=含义为:如没定义,则赋值。如:TEMP ?= var 等价于
ifeq($(TEMP),undefined)
TEMP = var
endif
ifeq用法
ifeq ()
....
else
ifeq ()
...
endif
endif
设置tab为4个空格后,输入TAB键
ctrl+v+TAB。
ifdef..else..endif
预定义宏,示例:
HW=1
ifdef HW
contxt="hello"
else
contxt="local"
endif
all:
echo $(contxt)
如上例,若HW为真,则输出为hello,若HW为假,则输出为local。
ifeq...else...endif
条件编译,示例:
ifeq($(gdb),y)
echo "debug"
else
echo "release"
endif
make时,可以加编译选项make gdb=y使编译出debug版本;也可以在makefile文件中定义变量来实现。
$(shell pwd)中shell的意义
makefile中,要调用shell脚本。需要特殊的语法。 比如要调用xxx命令。 相应的语法是 $(shell xxx) 你直接使用xxx,比如pwd命令。人家makefile哪知道是shell命令啊。它只会把pwd当成makefile变量或者规则。如:
p=$(shell pwd)
all:
echo $p
ar命令
在makefile中用ar 创建静态库.a,一般加参数rcs,即 ar rcs libxxx.a a.o b.o;
STRIP命令
当最终的可执行程序不需要包含调试信息时,可使用“strip”去掉可执行程序中的调试符号以减小最终的程序大小。
ifeq() else endif
条件判断 oo: $(objects)
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif
awk
echo $i | awk -F ':' '{print "#define " "SERVER_"$1" " "\""$2"\""}' >> $sfile; 按照分割符:进行操作,将分割符前后的内容显示;
1、wildcard : 扩展通配符
2、notdir : 去除路径
3、patsubst :替换通配符
在test下,建立a.c和b.c2个文件,在sub目录下,建立sa.c和sb.c2 个文件
建立一个简单的Makefilesrc=$(wildcard *.c ./sub/*.c)dir=$(notdir $(src))obj=$(patsubst %.c,%.o,$(dir) )
all: @echo $(src) @echo $(dir) @echo $(obj) @echo "end" 执行结果分析:第一行输出:a.c b.c ./sub/sa.c ./sub/sb.c
wildcard把 指定目录 ./ 和 ./sub/ 下的所有后缀是c的文件全部展开。
第二行输出:a.c b.c sa.c sb.cnotdir把展开的文件去除掉路径信息
第三行输出:a.o b.o sa.o sb.o
在$(patsubst %.c,%.o,$(dir) )中,patsubst把$(dir)中的变量符合后缀是.c的全部替换成.o,任何输出。或者可以使用obj=$(dir:%.c=%.o)效果也是一样的。
这里用到makefile里的替换引用规则,即用您指定的变量替换另一个变量。它的标准格式是$(var:a=b) 或 ${var:a=b}它的含义是把变量var中的每一个值结尾用b替换掉a
今天在研究makefile时在网上看到一篇文章,介绍了使用函数wildcard得到指定目录下所有的C语言源程序文件名的方法,这下好了,不用手工一个一个指定需要编译的.c文件了,方法如下:
SRC = $(wildcard *.c)
等于指定编译当前目录下所有.c文件,如果还有子目录,比如子目录为inc,则再增加一个wildcard函数,象这样:
SRC = $(wildcard *.c) $(wildcard inc/*.c)
自动化编译;
1) make -C dir :切换到指定目录再执行 make 过程,makefile 在这个指定目录里面;通常出现在makefile文件中;
2) 伪目标是一个标签不是文件,所以需要指定才可以执行,如make clean;一般用.PHONY指定(指定后伪目标就可以和真正的目标重名了);一般没有依赖,但是我们可以为伪目标指定依赖并将它放在第一个使其成为默认目标,如all;
3) STRIP的含义是当最终的可执行程序不需要包含调试信息时,可使用“strip”去掉可执行程序中的调试符号以减小最终的程序大小。
4) := 是立即变量赋值,在定义时值已经被确定;
= 是延时变量赋值,只有在这个变量被使用时才展开,$(VAR)就是一种被使用 4)
5)$@ 代表目标 上例为$(BUILT_IN_OBJ)
$^ 代表所有的依赖对象 上例第一个$^为$(OBJS),即 ifconfig.o tftp_util.oping.o,上例第二个$^为$(SRCS),即$(OBJS:.o=.c) (Makefile解释后为: ifconfig.ctftp_util.c ping.c)
$< 代表第一个依赖对象 前面例子修改如下:
$(BUILT_IN_OBJ) : $(OBJS)
则$<表示为ifconfig.o
它是在GUNmake的语法层次上的,例如 vpath %.h ../headers ,该语句表示,要求make在“../headers”目录下搜索所有以“.h”结尾的文件.
*是Shell所支持的通配符,是在shell的语法层次上,*.c,一般用在shell命令里面,如:
clean:
rm -f *.o
Makefile文件
自动化编译;
1) make -C dir :切换到指定目录再执行 make 过程,makefile 在这个指定目录里面;通常出现在makefile文件中;
2) 伪目标是一个标签不是文件,所以需要指定才可以执行,如make clean;一般用.PHONY指定(指定后伪目标就可以和真正的目标重名了);一般没有依赖,但是我们可以为伪目标指定依赖并将它放在第一个使其成为默认目标,如all;
3) STRIP的含义是当最终的可执行程序不需要包含调试信息时,可使用“strip”去掉可执行程序中的调试符号以减小最终的程序大小。
4) := 是立即变量赋值,在定义时值已经被确定;
= 是延时变量赋值,只有在这个变量被使用时才展开,$(VAR)就是一种被使用 4)
5)$@ 代表目标 上例为$(BUILT_IN_OBJ)
$^ 代表所有的依赖对象 上例第一个$^为$(OBJS),即 ifconfig.o tftp_util.oping.o,上例第二个$^为$(SRCS),即$(OBJS:.o=.c) (Makefile解释后为: ifconfig.ctftp_util.c ping.c)
$< 代表第一个依赖对象 前面例子修改如下:
$(BUILT_IN_OBJ) : $(OBJS)
则$<表示为ifconfig.o