Makefile详解

来源:互联网 发布:淘宝怎么显示折扣价 编辑:程序博客网 时间:2024/05/22 01:32

Makefile详解


其中的内容大多来自《GNUmake中文手册》3.8版徐海兵译,有小部分是我的理解:


在开始之前,我们要了解一些基本概念:

编译:把高级语言转化成.o文件

链接:把多个.o文件和库文件链接成一个可执行程序,“ld”命令

静态库:Linux下的静态库是.a文件,“ar”命令,程序可以把依赖的.a文件加到自己的程序中,这样,程序的运行就不需要外部的库函数了,但缺点是体积比较大,且依赖的任何一个静态库文件发生变化就要重新编译源程序。

动态库:Linux下的动态库是.so文件,程序无需连接时就把依赖的库文件连接进自己的程序中,这样体积小,但程序的运行需要依赖外部库函数。



make的基本原则:

1.如果所有的源文件没有被编译过,则对各个源文件编译并连接,生成最后的可执行程序;

2.每一个在上次执行make之后修改过的源代码文件在本次执行make时将会被重新编译;

3.头文件在上一次执行make之后被修改,则所有包含此头文件的源文件在本次执行make时将会被重新编译。



Makefile的基本语法:

TARGETPREREQUISITES

COMMAND


TARGET规则的目标。通常是程序中间或者最后需要生成的文件名。可以是.o文件、也可以是最后的可执行程序的文件名。另外,目标也可以是一个make执行的动作的名称,如目标“clean”,称这样的目标是“伪目标”。

PREREQUISITES规则的依赖。生成规则目标所需要的文件名列表。通常一个目标依赖于一个或者多个文件。

COMMAND规则的命令行。任意的shell命令或者可在shell下执行的程序。


注意:每一个COMMAND必须以[Tab]字符开始,[Tab]字符告诉make此行是一个命令行。这是书写Makefile最容易产生的错误。



make原理:

当在shell提示符下输入“make”命令以后。make读取当前目录下的Makefile文件,并将

Makefile文件中的第一个目标作为其“终极目标”,开始处理第一个规则(终极目标所在的规则)。由于该目标依赖于其他目标,所以接下来一层层地去寻找终极目标的依赖文件所在的规则并执行。当终极目标的规则被完全的展开以后,make将从最后一个被展开的规则处开始执行,之后处理倒数第二个规则,......依次回退。最后一步执行的就是终极目标所在的规则。整个过程就类似于C语言中的递归实现一样。同样,如果在shell提示符下输入“makexxx”命令后,make会在Makefile文件中查找xxx目标,然后再递归的查找依赖的目标执行。因此如果xxx目标没有依赖另一个目标,那个目标的命令就不会被执行,所以我们“make”的时候,目标“clean”的动作“rm”就没有被执行。


例:

#samplemakefile#定义变量object= hello1.o hello2.o hello3.ohello:$(object)gcc-o hello $(object)hello1.o:hello1.chello.hgcc-c hello1.chello2.o:hello2.chello.hgcc-c hello2.chello3.o:hello3.chello.hgcc-c hello3.c#伪目标.PHONY:cleanclean:rm$(object) hello

上例中的“.PHONY: clean ”表示将“clean”目标声明为伪目标,一般情况下这句可以省略,但如果当前工作目录下存在文件“clean”时就不能省略了,否则,我们执行“makeclean”时,规则没有依赖文件,所以目标被认为是最新的而不去执行规则作定义的命令,因此命令“rm”将不会被执行!




几点注意:

1. 

在书写时,一个较长行可以使用反斜线(\)分解为多行,注意:反斜线之后不能有空格。

2.

Makefile中“#”字符后的内容被作为是注释(和shell脚本一样)。如果一行的第一个非空字符为“#”,那么此行 为注释行。注释行的结尾如果存在反斜线(\)那么下一行也被作为注释行。

注意:推荐在书写Makefile时将注释作为独立的一 行!

当在Makefile中需要使用字符“#”时,可以使用“\#”来实现,其表示将“#”作为一字符而不是注释的开始标志

3.

如果命令前有“-”则表示如果该命令执行错误(如rm了一个不存在的文件)则忽略,继续往下执行(不推荐这样做!我们可以用rm-f xxx代替-rm xxx)。

4.

makefile中可以使用“includexxx”来包含其他的makefile文件,和c语言是一样的。

5.

默认情况下,make会输出它执行的每一条shell命令,如“echohello”会输出:

echohello

hello

第一行是命令本身,第二行才是命令的执行结果

我们可以在命令前加“@”禁止输出命令,只输出执行结果

6.

Makefile中对“$”表示对变量或者函数的引用,如果我们的规则需要“$”,需要书写两个连续的(“$$”)。如果需要统配符最为字符,可以这样:\*\?

7.

默认的情况下,make会在工作目录(执行make的目录)下按照文件名顺序寻找文件名的顺序:

GNUmakefile-->makefile-->Makefilemakefile文件,推荐使用“makefile”和“Makefile”

其中更推荐“Makefile ”,因为首字母大写而比较显著,一般在一个目录中和当前目录的一些重要文件(READMEChagelist等)靠近,在寻找时会比较容易的发现它。

我们也可以自己指定makefile文件名,需要通过make的“-f xxx”或者“--file xxx”选项。也可以通过多个“-f”或者“--file”选项来指定多个需要读取的makefile文件,多个makefile文件将会被按照指定的顺序进行连接并被make解析执行。当通过 “-f”“--file”指定make读取makefile的文件时,make就不再自动查找这三个标准命名的makefile文件。

8.

定义变量时如果:object= *.o,那么$(object)只表示“*.o”而不是所有以.o结尾的文件!如果需要变量“objects”代表当前目录下的所有的.o文件,则需要是用 函数“wildcard”来实现:objects= $(wildcard *.o)





make常用命令行参数:

-w

在进入一个目录时输出:

make:Entering directory xxx.

在离开一个目录时输出:

make:Leaving directory xxx.






1.定义变量:

Makefile中的变量大小写敏感,Makefile传统做法是变量名是全采用大写的方式。

推荐的做法是在对于内部定义定义的一般变量(例如:目标文件列表objects)使用小写方式, 而对于一些参数列表 (例如:编译选项CFLAGS)采用大写方式。


变量定义完后可以“$()”或“${}”方式引用

特殊变量:“$<”“$@”“$?”“$*”



=”,“:=”,“”在定义变量时的区别:

=”表示如果一个变量引用了另一个变量,那么它在定义时不会展开,在引用时才展开。它的优点是可以引用在当前行之后定义的变量。

:=”表示如果一个变量引用了另一个变量,那么它在定义时就直接展开,得到一个不包含任何其他引用的字符串。缺点是不能引用之后定义的变量。如果引用一个未定义的变量,则为空

“?=”表示如果此变量在之前没有定义,就赋值,否则保持原值

例:

a = $(b)b = goodtarget:@echo$(a)

和:

a := $(b)b = goodtarget:@echo$(a)

第一个输出”good“,第二个输出”“


追加变量:

通常对于一个通用变量在定义之后的其他一个地方,需要给它的值进行追加。在Makefile中使用“+=”(追加方式)来实现对一个变量值的追加操作。像下边那样:

objects+= another.o

有些需要注意的地方:

1.

如果被追加值的变量之前没有定义,那么,“+=”会自动变成“=”,此变量就被定义为一 个递归展开式的变量。

2.

如果之前存在这个变量定义,那么“+=”就继承之前定义时的变量风格(可参考5.2两种变量定义 一节)

variable := valuevariable += more

等价与:

variable := valuevariable := $(variable) more


variable = valuevariable += more

等价与:

temp = valuevariable = $(temp) more


我们可以用define来定义一个多行变量:

define”定义变量的语法格式:以指示符“define”开始,“endif”结束,之间的所有内容就是所定义变量的值。所要定义的变量名字指示符“define”的同一行之后;指示符所在行的下一行开始一直到“end”所在行的上一行之间的若干行,是变量的值定义。

definetwo-linesechofooecho$(bar)endef

注意,define定义等价与“=”定义,只不过定义了多行。

上面的例子等价与:

two-lines= echo foo; echo $(bar)




Makefile中可以使用所有的环境变量,它们作为默认已定义的变量,$(xxx)便可引用,如echo$(PATH)



如果在上层makefile中定义了一个变量,那么它只在这个makefile中有效,如果要让它在子makefile中仍然有效,需要:exportxxx




Makefile条件语句:

条件语句的一般格式为:

ifeq …  xxxendif

ifeq …  xxxelse …  yyyendif



合法的ifeq格式:

ifeq (ARG1, ARG2)

ifeq ‘ARG1’ ‘ARG2‘

ifeq ”ARG1” “ARG2“


合法的ifneq格式:

ifneq (ARG1, ARG2)

ifneq ‘ARG1’ ‘ARG2‘

ifneq ”ARG1” “ARG2“


ifdef用来判断一个变量是否定义:ifdef xxx

注意:只有在xxx没有定义或者这样定义:xxx=ifdef才是false,其他均为true,如:

bar =foo =$(bar)ifdef foo  @echoyeselse  @echonoendif

输出:yes,虽然展开后foo=


ifndef用来判断一个变量是否没有定义:ifndef xxx,和ifdef正好相反




Makefile函数:

Makefile中引用函数:$(函数名参数)

注意:上面的函数名应该是make内嵌的函数名。对于用户自己的函数需要通过make的“call”函数来间接调用;参数之间用逗号分割

常用函数简介:

$(substFROM,TO,TEXT)

函数名称:字符串替换函数—subst

函数功能:把字串“TEXT”中的“FROM”字符替换为“TO”

返回值:替换后的新字符串。

示例:

$(substee,EE,feet on the street)

替换“feeton the street”中的“ee”为“EE”

,结果是新的字符串“fEEton the strEEt”


原创粉丝点击