学习MakeFile
一、Why?
最近在学习oMnet++,经常需要用到一些工程,里面有很多Makefile文件,经常会出现报错,尤其是在安装软件的时候,经常遇到,平时遇到的可能都是利用工具自动生成的,问题比较少。
二、 程序的编译和链接
一般来说,无论是C、C++、还是pas,首先要把源文件编译成中间代码文件,在Windows下也就是 .obj 文件,UNIX下是 .o 文件,即 Object File,这个动作叫做编译(compile)。然后再把大量的Object File合成执行文件,这个动作叫作链接(link)。
编译时,编译器需要的是语法的正确,函数与变量的声明的正确。对于后者,通常是你需要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在C/C++文件中),只要所有的语法正确,编译器就可以编译出中间目标文件。一般来说,每个源文件都应该对应于一个中间目标文件(O文件或是OBJ文件)。
链接时,主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O文件或是OBJ文件)来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在Windows下这种包叫“*库文件”(Library File)*,也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。
总结一下,源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error)。
Make
Make命令直接用了这个意思,就是要做出某个文件。make是根据makefile中的规则来构建文件的。
Makefile文件的格式
-
文件格式:
<target> : <prerequisites>
[tab] <commands>上面第一行冒号前面的部分,叫做"目标"(target),冒号后面的部分叫做"前置条件"(prerequisites);第二行必须由一个tab键起首,后面跟着"命令"(commands)。
"目标"是必需的,不可省略;"前置条件"和"命令"都是可选的,但是两者之中必须至少存在一个。
每条规则就明确两件事:构建目标的前置条件是什么,以及如何构建。下面就详细讲解,每条规则的这三个组成部分。
-
目标(target)
一个目标(target)就构成一条规则。目标通常是文件名,指明Make命令所要构建的对象。目标可以是一个文件名,也可以是多个文件名,之间用空格分隔。
-
伪目标
除了文件名,目标还可以是某个操作的名字,这称为"伪目标"(phony target)。
clean:
rm *.o上面代码的目标是clean,它不是文件名,而是一个操作的名字,属于"伪目标 ",作用是删除对象文件。
但是如果目录里存在了clean文件,那么make会认为文件已经存在,没有必要重新构建了,就不会执行指定的rm命令。
解决办法,指明为目标:
clean:
rm *.o temp
如果Make命令运行时没有指定目标,默认会执行Makefile文件的第一个目标
-
-
前置条件(prerequisites)
前置条件通常是一组文件名,之间用空格分隔。它指定了"目标"是否重新构建的判断标准:只要有一个前置文件不存在,或者有过更新(前置文件的last-modification时间戳比目标的时间戳新),"目标"就需要重新构建。
-
命令(commands)
命令(commands)表示如何更新目标文件,由一行或多行的Shell命令组成。它是构建"目标"的具体指令,它的运行结果通常就是生成目标文件。
每行命令之前必须有一个tab键。如果想用其他键,可以用内置变量.RECIPEPREFIX声明
.RECIPEPREFIX = >
all:
> echo Hello, world上面代码用.RECIPEPREFIX指定,大于号(>)替代tab键。所以,每一行命令的起首变成了大于号,而不是tab键。
每行命令在一个单独的shell中执行。这些Shell之间没有继承关系。
如果需要在一起使用:
一个解决办法是将两行命令写在一行,中间用分号分隔。
另一个解决办法是在换行符前加反斜杠转义。
最后一个方法是文件头部加上
.ONESHELL:
命令。
三、 Makefile的语法
3.1注释
井号(#)在Makefile中表示注释。可以单独一行,或者在行尾.
3.2 回声(echoing)
正常情况下,make会打印每条命令,然后再执行,这就叫做回声(echoing)。
在命令的前面加上@,就可以关闭回声.
由于在构建过程中,需要了解当前在执行哪条命令,所以通常只在注释和纯显示的echo命令前面加上@。
3.3 通配符
通配符(wildcard)用来指定一组符合条件的文件名。Makefile 的通配符与 Bash 一致,主要有星号()、问号(?)和 […] 。比如, .o 表示所有后缀名为o的文件。
3.4 模式匹配
Make命令允许对文件名,进行类似正则运算的匹配,主要用到的匹配符是%。比如,假定当前目录下有 f1.c 和 f2.c 两个源码文件,需要将它们编译为对应的对象文件。
%.o: %.c |
等同于下面的写法。
f1.o: f1.c |
使用匹配符%,可以将大量同类型的文件,只用一条规则就完成构建。
3.5 变量和赋值符
Makefile 允许使用等号自定义变量。
调用时,变量需要放在 $( ) 之中
调用Shell变量,需要在美元符号前,再加一个美元符号,这是因为Make命令会对美元符号转义。
有时,变量的值可能指向另一个变量。
v1 = $(v2) |
上面代码中,变量 v1 的值是另一个变量 v2。这时会产生一个问题,v1 的值到底在定义时扩展(静态扩展),还是在运行时扩展(动态扩展)?如果 v2 的值是动态的,这两种扩展方式的结果可能会差异很大。
为了解决类似问题,Makefile一共提供了四个赋值运算符 (=、:=、?=、+=)
VARIABLE = value |
3.6 内置变量(Implicit Variables)
Make命令提供一系列内置变量,比如,$(CC) 指向当前使用的编译器,
$(MAKE) 指向当前使用的Make工具。
3.7 自动变量(Automatic Variables)
Make命令还提供一些自动变量,它们的值与当前规则有关
-
$@
$@指代当前目标,就是Make命令当前构建的那个目标。比如,
make foo
的$@ 就指代foo。
-
$<
指代指代第一个前置条件。比如,规则为 t: p1 p2,那么$< 就指代p1。
-
$?
指代比目标更新的所有前置条件,之间以空格分隔。比如,规则为 t: p1 p2,其中 p2 的时间戳比 t 新,$?就指代p2。
-
$^
$^ 指代所有前置条件,之间以空格分隔。比如,规则为 t: p1 p2,那么
$^ 就指代 p1 p2 。
-
$*
$ *指代匹配符 % 匹配的部分, 比如% 匹配 f1.txt 中的f1 ,
$* 就表示 f1。
-
$(@D) 和
** $(@F)**
$(@D) 和
$(@F) 分别指向
$@ 的目录名和文件名。
比如,$@是 src/input.c,
那么$(@D) 的值为 src ,
$(@F) 的值为 input.c。
3.8 判断和循环
Makefile使用 Bash 语法,完成判断和循环。
ifeq ($(CC),gcc) |
上面代码判断当前编译器是否 gcc ,然后指定不同的库文件。
LIST = one two three |
上面代码的运行结果。
one |
3.9 函数
Makefile 还可以使用函数,格式如下。
$(function arguments) |
Makefile提供了许多内置函数,可供调用。下面是几个常用的内置函数。
(1)shell 函数
shell 函数用来执行 shell 命令
srcfiles := $(shell echo src/{00..99}.txt) |
(2)wildcard 函数
wildcard 函数用来在 Makefile 中,替换 Bash 的通配符。
srcfiles := $(wildcard src/*.txt) |
(3)subst 函数
subst 函数用来文本替换,格式如下。
$(subst from,to,text) |
下面的例子将字符串"feet on the street"替换成"fEEt on the strEEt"。
$(subst ee,EE,feet on the street) |
下面是一个稍微复杂的例子。
comma:= , |
(4)patsubst函数
patsubst 函数用于模式匹配的替换,格式如下。
$(patsubst pattern,replacement,text) |
下面的例子将文件名"x.c.c bar.c",替换成"x.c.o bar.o"。
$(patsubst %.c,%.o,x.c.c bar.c) |
(5)替换后缀名
替换后缀名函数的写法是:变量名 + 冒号 + 后缀名替换规则。它实际上patsubst函数的一种简写形式。
min: $(OUTPUT:.js=.min.js) |
上面代码的意思是,将变量OUTPUT中的后缀名 .js 全部替换成 .min.js 。