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