Linux

Ongoingcre 2011-03-27

1,什么是makefile?

Makefile一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,

2,优点

makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。

不同产商的make各不相同,也有不同的语法,但其本质都是在“文件依赖性”上做文章,这里,我仅对GNU的make进行讲述.

3,Makefile的规则

target...:prerequisites...

command

...

...

target也就是一个目标文件,可以是ObjectFile/执行文件/标签(伪目标)

prerequisites就是,要生成那个target所需要的文件或是目标。(也可以没有)

command也就是make需要执行的命令。(任意的Shell命令)

二、一个示例

正如前面所说的,如果一个工程有3个头文件,和8个C文件,我们为了完成前面所述的那三个规则,我们的Makefile应该是下面的这个样子的。

edit:main.okbd.o

cc-oeditmain.okbd.o

main.o:main.cdefs.h

cc-cmain.c

kbd.o:kbd.cdefs.hcommand.h

cc-ckbd.c

clean:

rmeditmain.okbd.o

我们可以把这个内容保存在文件为“Makefile”或“makefile”的文件中,然后在该目录下直接输入命令“make”就可以生成执行文件edit。如果要删除执行文件和所有的中间目标文件,那么,只要简单地执行一下“makeclean”就可以了。

这里要说明一点的是,clean不是一个文件,它只不过是一个动作名字,make不会自动执行其后所定义的命令。要执行其后的命令,就要在make命令后明显得指出这个lable的名字clean。这样的方法非常有用,我们可以在一个makefile中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。

三、make是如何工作的

在默认的方式下,也就是我们只输入make命令。那么,

1、make会在当前目录下找名字叫“Makefile”或“makefile”的文件。

2、如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“edit”这个文件,并把这个文件作为最终的目标文件。

3、如果edit文件不存在,或是edit所依赖的后面的.o文件的文件修改时间要比edit这个文件新,那么,他就会执行后面所定义的命令来生成edit这个文件。

4、如果edit所依赖的.o文件也不存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件.

5、当然,你的C文件和H文件是存在的啦,于是make会生成.o文件,然后再用.o文件生命make的终极任务,也就是执行文件edit了。

四、makefile中使用变量

objects=main.okbd.o

于是,我们就可以很方便地在我们的makefile中以“$(objects)”的方式来使用这个变量了,于是改良版makefile就变成下面这个样子:

objects=main.okbd.o

edit:$(objects)

cc-oedit$(objects)

main.o:main.cdefs.h

cc-cmain.c

kbd.o:kbd.cdefs.hcommand.h

cc-ckbd.c

clean:

rmedit$(objects)

于是如果有新的.o文件加入,我们只需简单地修改一下objects变量就可以了。

五、让make自动推导

GNU的make很强大,它可以自动推导文件以及文件依赖关系后面的命令.

只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中。

并且cc-c***.c也会被推导出来

于是新的改良版makefile就变成下面这个样子:

objects=main.okbd.o

edit:$(objects)

cc-oedit$(objects)

main.o:defs.h

kbd.o:defs.hcommand.h

.PHONY:clean

clean:

rmedit$(objects)

六、清空目标文件的规则

.PHONY:clean

clean:

-rmedit$(objects)

Makefile总述

一、Makefile里有什么?

Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。

二、Makefile的文件名

“GNUmakefile”、“makefile”、“Makefile”

三、引用其它的Makefile

include<filename>

includefoo.make*.mk

等价于:

includefoo.makea.mkb.mkc.mke.mkf.mk

四、环境变量MAKEFILES

如果你的当前环境中定义了环境变量MAKEFILES,那么,make会把这个变量中的值做一个类似于include的动作。这个变量中的值是其它的Makefile,用空格分隔。只是,它和include不同的是,从这个环境变中引入的Makefile的“目标”不会起作用,如果环境变量中定义的文件发现错误,make也会不理。

但是在这里我还是建议不要使用这个环境变量,因为只要这个变量一被定义,那么当你使用make时,所有的Makefile都会受到它的影响,这绝不是你想看到的。在这里提这个事,只是为了告诉大家,也许有时候你的Makefile出现了怪事,那么你可以看看当前环境中有没有定义这个变量。

五、make的工作方式

GNU的make工作时的执行步骤入下:(想来其它的make也是类似)

1、读入所有的Makefile。

2、读入被include的其它Makefile。

3、初始化文件中的变量。

4、推导隐晦规则,并分析所有规则。

5、为所有的目标文件创建依赖关系链。

6、根据依赖关系,决定哪些目标要重新生成。

7、执行生成命令。

规则的语法

targets:prerequisites

command

...

或是这样:

targets:prerequisites;command

command

...

targets是文件名,以空格分开,可以使用通配符。一般来说,我们的目标基本上是一个文件,但也有可能是多个文件。

command是命令行,如果其不与“targetrerequisites”在一行,那么,必须以[Tab键]开头,如果和prerequisites在一行,那么可以用分号做为分隔。(见上)

在规则中使用通配符

如果我们想定义一系列比较类似的文件,我们很自然地就想起使用通配符。make支持三各通配符:“*”,“?”和“[...]”。

伪目标

.PHONY:clean

clean:

rm*.otemp

伪目标同样也可成为依赖。看下面的例子:

.PHONY:cleanallcleanobjcleandiff

cleanall:cleanobjcleandiff

rmprogram

cleanobj:

rm*.o

cleandiff:

rm*.diff

静态模式

静态模式可以更加容易地定义多目标的规则,可以让我们的规则变得更加的有弹性和灵活。我们还是先来看一下语法:

<targets...>:<target-pattern>:<prereq-patterns...>

<commands>

...

targets定义了一系列的目标文件,可以有通配符。是目标的一个集合。

target-parrtern是指明了targets的模式,也就是的目标集模式。

prereq-parrterns是目标的依赖模式,它对target-parrtern形成的模式再进行一次依赖目标的定义。

这样描述这三个东西,可能还是没有说清楚,还是举个例子来说明一下吧。如果我们的<target-parrtern>定义成“%.o”,意思是我们的<target>集合中都是以“.o”结尾的,而如果我们的<prereq-parrterns>定义成“%.c”,意思是对<target-parrtern>所形成的目标集进行二次定义,其计算方法是,取<target-parrtern>模式中的“%”(也就是去掉了[.o]这个结尾),并为其加上[.c]这个结尾,形成的新集合。

所以,我们的“目标模式”或是“依赖模式”中都应该有“%”这个字符,如果你的文件名中有“%”那么你可以使用反斜杠“\”进行转义,来标明真实的“%”字符。

看一个例子:

objects=foo.obar.o

all:$(objects)

$(objects):%.o:%.c

$(CC)-c$(CFLAGS)$<-o$@

上面的例子中,指明了我们的目标从$object中获取,“%.o”表明要所有以“.o”结尾的目标,也就是“foo.obar.o”,也就是变量$object集合的模式,而依赖模式“%.c”则取模式“%.o”的“%”,也就是“foobar”,并为其加下“.c”的后缀,于是,我们的依赖目标就是“foo.cbar.c”。而命令中的“$<”和“$@”则是自动化变量,“$<”表示所有的依赖目标集(也就是“foo.cbar.c”),“$@”表示目标集(也就是“foo.obar.o”)。于是,上面的规则展开后等价于下面的规则:

foo.o:foo.c

$(CC)-c$(CFLAGS)foo.c-ofoo.o

bar.o:bar.c

$(CC)-c$(CFLAGS)bar.c-obar.o

试想,如果我们的“%.o”有几百个,那种我们只要用这种很简单的“静态模式规则”就可以写完一堆规则,实在是太有效率了。“静态模式规则”的用法很灵活,如果用得好,那会一个很强大的功能。再看一个例子:

files=foo.elcbar.olose.o

$(filter%.o,$(files)):%.o:%.c

$(CC)-c$(CFLAGS)$<-o$@

$(filter%.o,$(files))表示调用Makefile的filter函数,过滤“$filter”集,只要其中模式为“%.o”的内容。

自动生成依赖性

cc-Mmain.c

其输出是:

main.o:main.cdefs.h

如果你使用GNU的C/C++编译器,你得用“-MM”参数,不然,“-M”参数会把一些标准库的头文件也包含进来。

一、显示命令

@echo正在编译XXX模块......

当make执行时,会输出“正在编译XXX模块......”字串,但不会输出命令,如果没有“@”,那么,make将输出:

echo正在编译XXX模块......

正在编译XXX模块......

如果make执行时,带入make参数“-n”或“--just-print”,那么其只是显示命令,但不会执行命令,这个功能很有利于我们调试我们的Makefile.

而make参数“-s”或“--slient”则是全面禁止命令的显示。

Makefile的条件执行

ifeqifneqifdef

ifeq($(CC),gcc)

....

else

....

endif

相关推荐