Make命令完全详解教程_第1页
Make命令完全详解教程_第2页
Make命令完全详解教程_第3页
Make命令完全详解教程_第4页
Make命令完全详解教程_第5页
已阅读5页,还剩56页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Make命令完全详解教程无论是在Linux还是在Unix环境中,make都是一个非常重要的编译命令。不管是自己进行项目开发还是安装应用软件,我们都经常要用到make或makeinstall。利用make工具,我们可以将大型的开发项目分解成为多个更易于管理的模块,对于一个包括几百个源文件的应用程序,使用make和makefile工具就可以简洁明快地理顺各个源文件之间纷繁复杂的相互关系。而且如此多的源文件,如果每次都要键入gcc命令进行编译的话,那对程序员来说简直就是一场灾难。而make工具则可自动完成编译工作,并且可以只对程序员在上次编译后修改过的部分进行编译。因此,有效的利用make和make

2、file工具可以大大提高项目开发的效率。同时掌握make和makefile之后,您也不会再面对着Linux下的应用软件手足无措了。一、Make程序的命令行选项和参数Make命令参数的典型序列如下所示:make-fmakefile文件名选项宏定义目标这里用括起来的表示是可选的。命令行选项由破折号“-”指明,后面跟选项,如make-e如果需要多个选项,可以只使用一个破折号,如make-kr也可以每个选项使用一个破折号,如make-k-r甚至混合使用也行,如make-e-krMake命令本身的命令行选项较多,这里只介绍在开发程序时最为常用的三个,它们是:-k:如果使用该选项,即使make程序遇到错误

3、也会继续向下运行;如果没有该选项,在遇到第一个错误时make程序马上就会停止,那么后面的错误情况就不得而知了。我们可以利用这个选项来查出所有有编译问题的源文件。-n:该选项使make程序进入非执行模式,也就是说将原来应该执行的命令输出,而不是执行。-f:指定作为makefile的文件的名称。如果不用该选项,那么make程序首先在当前目录查找名为makefile的文件,如果没有找到,它就会转而查找名为Makefile的文件。如果您在Linux下使用GNUMake的话,它会首先查找GNUmakefile,之后再搜索makefile和Makefile。按照惯例,许多Linux程序员使用Makefil

4、e,因为这样能使Makefile出现在目录中所有以小写字母命名的文件的前面。所以,最好不要使用GNUmakefile这一名称,因为它只适用于make程序的GNU版本。当我们想构建指定目标的时候,比如要生成某个可执行文件,那么就可以在make命令行中给出该目标的名称;如果命令行中没有给出目标的话,make命令会设法构建makefile中的第一目标。我们可以利用这一特点,将all作为makefile中的第一个目标,然后将让目标作为all所依赖的目标,这样,当命令行中没有给出目标时,也能确保它会被构建。二、Makefile概述上面提到make命令对于构建具有多个源文件的程序有很大的帮助。事实上,只有

5、make命令还是不够的,前面说过还必用须makefile告诉它要做什么以及怎么做才行,对于程序开发而言,就是告诉make命令应用程序的组织情况。我们现在对makefile的位置和数量简单说一下。一般情况下,makefile会跟项目的源文件放在同一个目录中。另外,系统中可以有多个makefile,一般说来一个项目吏用一个makefile就可以了;如果项目很大的话,我们就可以考虑将它分成较小的部分,然后用不同的makefile来管理项目的不同部分。make命令和Makefile配合使用,能给我们的项目管理带来极大的便利,除了用于管理源代码的编译之外,还用于建立手册页,同时还能将应用程序安装到指定的

6、目录。因为Makefile用于描述系统中模块之间的相互依赖关系,以及产生目标文件所要执行的命令,所以,一个makefile由依赖关系和规则两部分内容组成。下面分别加以解释。依赖关系由一个目标和一组该目标所依赖的源文件组成。这里所说的目标就是将要创建或更新的文件,最常见的是可执行文件。规则用来说明怎样使用所依赖得文件来建立目标文件。当make命令运行时,会读取makefile来确定要建立的目标文件或其他文件,然后对源文件的日期和时间进行比较,从而决定使用那些规则来创建目标文件。一般情况下,在建立起最终的目标文件之前,肯定免不了要建立一些中间性质的目标文件。这时,Make命令也是使用makefil

7、e来确定这些目标文件的创建顺序,以及用于它们的规则序列。Makefile中的依赖关系make程序自动生成和维护通常是可执行模块或应用程序的目标,目标的状态取决于它所依赖的那些模块的状态。Make的思想是为每一块模块都设置一个时间标记,然后根据时间标记和依赖关系来决定哪一些文件需要更新。一旦依赖模块的状态改变了,make就会根据时间标记的新旧执行预先定义的一组命令来生成新的目标。依赖关系规定了最终得到的应用程序跟生成它的各个源文件之间的关系。如下面的图1描述了可执行文件main对所有的源程序文件及其编译产生的目标文件之间的依赖关系,见下图:图1模块间的依赖关系就图1而言,我们可以说可执行程序ma

8、in依赖于main.o、f1.o和ff1.o。与此同时,main.0依赖于main.c和defl.h;f1.o依赖于fl.c、defl.h和def2.h;而ffl.o则依赖于ffl.c、def2.h和def3.h。在makefile中,我们可以用目标名称,加冒号,后跟空格键或tab键,再加上由空格键或tab键分隔的一组用于生产目标模块的文件来描述模块之间的依赖关系对于上例来说,可以作以下描述:main:main.of1.of2.omain.o:main.cdef1.hf1.o:f1.cdef1.hdef2.hf2.o:f2.cdef2.hdef3.h不难发现,上面的各个源文件跟各模块之间的关系

9、具有一个明显的层次结构,如果def2.h发生了变化,那么就需要更新f1.。和f2.o,而f1.。和f2.。发生了变化的话,那么main也需要随之重新构建。默认时,make程序只更新makefile中的第一个目标,如果希望更新多个目标文件的话,可以使用一个特殊的目标all,假如我们想在一个makefile中更新main和hello这两个程序文件的话,可以加入下列语句达到这个目的:all:mainhello实际上,makefile是以相关行为基本单位的,相关行用来描述目标、模块及规则(即命令行)三者之间的关系。一个相关行格式通常为:冒号左边是目标(模块)名;冒号右边是目标所依赖的模块名;紧跟着的规

10、则(即命令行)是由依赖模块产生目标所使用的命令。相关行的格式为:目标:依赖模块;命令习惯上写成多行形式,如下所示:目标:依赖模块命令命令目标(TARGET)程序产生的文件,如可执行文件和目标文件;目标也可以是要执行的动作,如“clean”。依赖(DEPENDENCIES)是用来产生目标的输入文件,个目标通常依赖于多个文件。命令(COMMAND)是make执行的动作,一个可以有多个命令,每个占一行。注意:每个命令行的起始字符必须为TAB字符!有依赖关系规则中的命令通常在依赖文件变化时负责产生target文件,make执行这些命令更新或产生target。规则可以没有依赖关系,如包含target“c

11、lean”的规则。规则解释如何和何时重做该规则中的文件,make根据依赖关系执行产生或更新目标;规则也说明如何和何时执行动作。有的规则看起来很复杂,但都符合上述模式。需要注意的是,如果相关行写成一行,“命令”之前用分号“;”隔开,如果分成多行书写的话,后续的行务必以tab字符为先导。对于makefile而言,空格字符和tab字符是不同的。所有规则所在的行必须以tab键开头,而不是空格键。初学者一定对此保持警惕,因为这是新手最容易疏忽的地方,因为几个空格键跟一个tab键在肉眼是看不出区别的,但make命令却能明察秋毫。此外,如果在makefile文件中的行尾加上空格键的话,也会导致make命令运

12、行失败。所以,大家一定要小心了,免得耽误许多时间。例,一个名为prog的程序由三个C源文件filea.c、fileb.c和filec.c以及库文件LS编译生成,这三个文件还分别包含自己的头文件a.h、b.h和c.h。通常情况下,C编译器将会输出三个目标文件filea.o、fileb.o和filec.o。假设filea.c和fileb.c都要声明用到一个名为defs的文件,旦filec.c不用。即在filea.c和fileb.c里都有这样的声明:#includedefs那么下面的文档就描述了这些文件之间的相互联系:line#Itisaexamplefordescribingmakefilepro

13、g:filea.ofileb.ofilec.occfilea.ofileb.ofilec.o-LS-oprogfilea.o:filea.ca.hdefscc-cfilea.cfileb.o:fileb.cb.hdefscc-cfileb.cfilec.o:filec.cc.hcc-cfilec.c这个描述文档就是一个简单的makefile文件。从上面的例子注意到,第一个字符为#的行为注释行。第一个非注释行指定prog由三个目标文件filea.o、fileb.o和filec.o链接生成。第三行描述了如何从prog所依赖的文件建立可执行文件。接下来的4、6、8行分别指定三个目标文件,以及它们所依

14、赖的.C和.h文件以及defs文件。而5、7、9行则指定了如何从目标所依赖的文件建立目标。当filea.c或a.h文件在编译之后又被修改,则make工具可自动重新编译filea.o,如果在前后两次编译之间,filea.C和a.h均没有被修改,而且test.o还存在的话,就没有必要重新编译。这种依赖关系在多源文件的程序编译中尤其重要。通过这种依赖关系的定义,make工具可避免许多不必要的编译工作。当然,利用Shell脚本也可以达到自动编译的效果,但是,Shell脚本将全部编译任何源文件,包括哪些不必要重新编译的源文件,而make工具则可根据目标上一次编译的时间和目标所依赖的源文件的更新时间而自动

15、判断应当编译哪个源文件。Makefile文件举例根据图1的依赖关系,这里给出了一个完整的makefile文件,这个例子很简单,由四个相关行组成,我们将其命名为mymakefile1。文件内容如下所示:main:main.of1.of2.ogcc-omainmain.of1.of2.omain.o:main.cdef1.hgcc-cmain.cf1.o:f1.cdef1.hdef2.hgcc-cf1.cf2.o:f2.cdef2.hdef3.hgcc-cf2.c注意,由于我们这里没有使用缺省名makefile或者Makefile,所以一定要在make命令行中加上-f选项。如果在没有任何源码的目录

16、下执行命令“make-fMymakefile1”的话,将收到下面的消息:make:*Noruletomaketargetmain.c,neededbymain.。.Stop.Make命令将makefile中的第一个目标即main作为要构建的文件,所以它会寻找构建该文件所需要的其他模块,并判断出必须使用一个称为main.c的文件。因为迄今尚未建立该文件,而makefile又不知道如何建立它,所以只好报告错误。好了,现在建立这个源文件,为简单起见,我们让头文件为空,创建头文件的具体命令如下:$touchdefl.h$touchdef2.h$touchdef3.h我们将main函数放在main.c文

17、件中,让它调用function2和function3,但将这两个函数的定义放在另外两个源文件中。由于这些源文件含有#include命令,所以它们肯定依赖于所包含的头文件。如下所示:/*main.c*/#includevSTDLIDEF2.H#include“defl.h”externvoidfunction2();externvoidfunction3();intmain()function2();function3();exit(EXIT_SUCCESS);/*f1.c*/#include“defl.h”#include“def2.h”voidfunction2()/*f2.c*/#incl

18、ude“def2.h”#include“def3.h”voidfunction3()建好源代码后,再次运行make程序,看看情况如何:$make-fMymakefilelgcc-cmain.cgcc-cfl.cgcc-cf2.cgcc-omainmain.ofl.of2.o$好了,这次顺利通过了。这说明Make命令已经正确处理了makefile描述的依赖关系,并确定出了需要建立哪些文件以及它们的建立顺序。虽然我们在makefile中首先列出的是如何建立main,但是make还是能够正确的判断出这些文件的处理顺序,并按相应的顺序调用规则部分规定的相应命令来创建这些文件。当这些命令执行时,make

19、程序会按照执行情况来显示这些命令。如今,我们对def2.h加以变动,来看看makefile能否对此作出相应的回应:$touchdef2.h$make-fMymakefile1gcc-cf1.cgcc-cf2.cgcc-omainmain.of1.of2.o$这说明,当Make命令读取makefile后,只对受def2.h的变化的影响的模块进行了必要的更新,注意它的更新顺序,它先编译了C程序,最后连接生产了可执行文件。现在,让我们来看看删除目标文件后会发生什么情况,先执行删除,命令如下:$rmfl.o然后运行make命令,如下所示:$make-fMymakefilelgcc-cf1.cgcc-o

20、mainmain.of1.of2.o$很好,make的行为让我们非常满意。Makefile中的宏在makefile中可以使用诸如XLIB、UIL等类似于Shell变量的标识符,这些标识符在makefile中称为“宏,它可以代表一些文件名或选项。宏的作用类似于C语言中的define,利用它们来代表某些多处使用而又可能发生变化的内容,可以节省重复修改的工作,还可以避免遗漏。Make的宏分为两类,类是用户自己定义的宏,类是系统内部定义的宏。用户定义的宏必须在makefile或命令行中明确定义,系统定义的宏不由用户定义。我们首先介绍第一种宏。这里是一个包含宏的makefile文件,我们将其命名为mym

21、akefile2,如下所示:all:main#使用的编译器CC=gcc#包含文件所在目录INCLUDE=.#在开发过程中使用的选项CFLAGS=-g-Wall-ansi#在发行时使用的选项#CFLAGS=-O-Wall-ansimain:main.ofl.of2.o$(CC)-omainmain.ofl.of2.omain.o:main.cdefl.h$(CC)-I$(INCLUDE)$(CFLAGS)-cmain.cfl.o:fl.cdefl.hdef2.h$(CC)-I$(INCLUDE)$(CFLAGS)-cfl.cf2.o:f2.cdef2.hdef3.h$(CC)-I$(INCLUD

22、E)$(CFLAGS)-cf2.c我们看到,在这里有一些注释。在makefile中,注释以#为开头,至行尾结束。注释不仅可以帮助别人理解我们的makefile,如果时间久了,有些东西我们自己也会忘掉,它们对makefile的编写者来说也是很有必要的。现在言归正传,先看一下宏的定义。我们既可以在make命令行中定义宏,也可以在makefile中定义宏。2.4在makefile中定义宏的基本语法是:宏标识符二值列表其中,宏标识符即宏的名称通常全部大写,但它实际上可以由大、小写字母、阿拉伯数字和下划线构成。等号左右的空白符没有严格要求,因为它们最终将被make删除。至于值列表,既可以是零项,也可以是

23、一项或者多项。如:LIST_VALUE=onetwothree当一个宏定义之后,我们就可以通过$(宏标识符)或者$宏标识符来访问这个标识符所代表的值了。在makefile中,宏经常用作编译器的选项。很多时候,处于开发阶段的应用程序在编译时是不用优化的,但是却需要调试信息;而正式版本的应用程序却正好相反,没有调试信息的代码不仅所占内存较小,进过优化的代码运行起来也更快。对于Mymakefile1来说,它假定所用的编译器是gcc,不过在其他的UNIX系统上,更常用的编译器是cc或者c89,而非gcc。如果你想让自己的makefile适用于不同的UNIX操作系统,或者在一个系统上使用其他种类的编译器

24、,这时就不得不对这个makefile中的多处进行修改。但对于mymakefile2来说则不存在这个问题,我们只需修改一处,即宏定义的值就行了。除了在makefile中定义宏的值之外,我们还可以在make命令行中加以定义,如:$makeCC=c89当命令行中的宏定义跟makefile中的定义有冲突时,以命令行中的定义为准。当在makefile文件之外使用时,宏定义必须作为单个参数进行传递,所以要避免使用空格,但是更妥当的方法是使用引号,如:$make“CC=c89”这样就不必担心空格所引起的问题了。现在让我们将前面的编译结果删掉,来测试一下mymakefile2的工作情况。命令如下所示:$rm*

25、.omain$make-fMymakefile2gcc-I.-g-Wall-ansi-cmain.cgcc-I.-g-Wall-ansi-cf1.cgcc-I.-g-Wall-ansi-cf2.cgcc-omainmain.of1.of2.o$就像我们看到的那样,Make程序会用相应的定义来替换宏引用$(CC)、$(CFLAGS)和$(INCLUDE),这跟C语言中的宏的用法比较相似。上面介绍了用户定义的宏,现在介绍make的内部宏。常用的内部宏有:$?:比目标的修改时间更晚的那些依赖模块表。$:当前目标的全路径名。可用于用户定义的目标名的相关行中。$:比给定的目标文件时间标记更新的依赖文件名

26、。$*:去掉后缀的当前目标名。例如,若当前目标是pro.o,则$*表示pro。Makefile文件作为一种描述文档一般需要包含以下内容:宏定义源文件之间的相互依赖关系可执行的命令Makefile中允许使用简单的宏指代源文件及其相关编译信息,在Linux中也称宏为变量。在引用宏时只需在变量前加$符号,但值得注意的是,如果变量名的长度超过一个字符,在引用时就必须加圆括号()。下面都是有效的宏引用:$(CFLAGS)$2$Z$(Z)其中最后两个引用是完全一致的。需要注意的是一些宏的预定义变量,在Unix系统中,$*、$、$?和$四个特殊宏的值在执行命令的过程中会发生相应的变化,而在GNUmake中则

27、定义了更多的预定义变量。关于预定义变量的详细内容,宏定义的使用可以使我们脱离那些冗长乏味的编译选项,为编写makefile文件带来很大的方便。#DefineamacrofortheobjectfilesOBJECTS=filea.ofileb.ofilec.oDefineamacroforthelibraryfileLIBES=-LSusemacrosrewritemakefileprog:$(OBJECTS)cc$(OBJECTS)$(LIBES)-oprog此时如果执行不带参数的make命令,将连接三个目标文件和库文件LS;但是如果在make命令后带有新的宏定义:makeLIBES=-LL

28、-LS则命令行后面的宏定义将覆盖makefile文件中的宏定义。若LL也是库文件,此时make命令将连接三个目标文件以及两个库文件LS和LL。在Unix系统中没有对常量NULL作出明确的定义,因此我们要定义NULL字符串时要使用下述宏定义:STRINGNAME=Make命令在make命令后不仅可以出现宏定义,还可以跟其他命令行参数,这些参数指定了需要编译的目标文件。其标准形式为:target1target2dependent.;commands#.(tab)commands#.方括号中间的部分表示可选项。Targets和dependents当中可以包含字符、数字、句点和7符号。除了引用,com

29、mands中不能含有#,也不允许换行。在通常的情况下命令行参数中只含有一个:,此时commanc序列通常和makefile文件中某些定义文件间依赖关系的描述行有关。如果与目标相关连的那些描述行指定了相关的command序列,那么就执行这些相关的command命令,即使在分号和(tab)后面的aommand字段甚至有可能是NULL。如果那些与目标相关连的行没有指定command,那么将调用系统默认的目标文件生成规则。如果命令行参数中含有两个冒号:,则此时的command序列也许会和makefile中所有描述文件依赖关系的行有关。此时将执行那些与目标相关连的描述行所指向的相关命令。同时还将执行bu

30、ild-in规则。如果在执行command命令时返回了一个非0的出错信号,列如makefile文件中出现了错误的目标文件名或者出现了以连字符打头的命令字符串,make操作一般会就此终止,但如果make后带有-i参数,则make将忽略此类出错信号。Make命本身可带有四种参数:标志、宏定义、描述文件名和目标文件名。其标准形式为:MakeflagsmacrodefinitionstargetsUnix系统下标志位flags选项及其含义为:-ffile指定file文件为描述文件,如果file参数为-符,那么描述文件指向标准输入。如果没有-f参数,则系统将默认当前目录下名为makefile或者名为Ma

31、kefile的文件为描述文件。在Linux中,GNUmake工具在当前工作目录中按照GNUmakefile、makefile、Makefile的顺序搜索makefile文件。-i忽略命令执行返回的出错信息。-s沉默模式,在执行之前不输出相应的命令行信息。-r禁止使用build-in规则。-n非执行模式,输出所有执行命令,但并不执行。-t更新目标文件。-qmake操作将根据目标文件是否已经更新返回0或非0的状态信息。-p输出所有宏定义和目标文件描述。-dDebug模式,输出有关文件和检测时间的详细信息。Linux下make标志位的常用选项与Unix系统中稍有不同,下面我们只列出了不同部分:-cd

32、ir在读取makefile之前改变到指定的目录dir。-Idir当包含其他makefile文件时,利用该选项指定搜索目录。-hhelp文挡,显示所有的make选项。-w在处理makefile之前和之后,都显示工作目录。Linux下make标志位的常用选项与Unix系统中稍有不同,下面我们只列出了不同部分:-cdir在读取makefile之前改变到指定的目录dir。-Idir当包含其他makefile文件时,利用该选项指定搜索目录。-hhelp文挡,显示所有的make选项。-w在处理makefile之前和之后,都显示工作目录。通过命令行参数中的target,可指定make要编译的目标,并且允许同

33、时定义编译多个目标操作时按照从左向右的顺序依次编译target选项中指定的目标文件。如果命令行中没有指定目标,则系统默认target指向描述文件中第一个目标文件。通常,makefile中还定义有clean目标,可用来清除编译过程中的中间文件,例如:clean:rm-f*.o运行makeclean时,将执行rm-f*.o命令,最终删除所有编译过程中产生的所有中间文件。2.5.清理编写规则不至于编译程序。Makefile通常描述如何做其它事情:比如删除目录中的目标文件和可执行文件来清理目录。例子中是这样写的:clean:rmedit$(objects)实际情况是,我们需要处理一些意外事件:存在一个

34、叫做clean的文件;如果rm出错,并不希望make过程停止下来,修改过的版本如下:.PHONY:cleanclean:-rmedit$(objects)这样的规则当然不能放在makefile的开始,因为这并不是我们缺省要做的工作。由于clean并不是edit的依赖,在运行make时没有参数时,这条规则不会执行;要执行这个规则,必须运行makeclean。三、规则makefile中的规则描述如何生成特定的文件,即规则的目标。规则列出了目标的依赖文件,指定生成或更新目标的命令。规则的次序是不重要的,除非是确定缺省目标:缺省目标是第一makefile中的第一个规则;如果第一个规则有多个目标,第一个

35、目标是缺省的。有两个例外:以.开头的目标不是缺省目标;模式规则对缺省目标没有影响。通常我们所写的地一个规则是编译整个或makefile中指定的所有程序。例子foo.o:foo.cdefs.h#modulefortwiddlingthefrobscc-c-gfoo.c它的目标是foo.o,依赖于foo.c和defs.h,有一命令cc-c-gfoo.c。命令行以TAB字符开始标识它是一个命令。这条规则说明两件事:8如何决定foo.o是旧的:如果它不存在,或者foo.c或者defs.h比它新。9如何更新foo.o文件:通过运行cc程序。命令未提及defs.h,担可以猜想foo.c包含了它,这是def

36、s.h被置于依赖关系中的理由。规则的语法语法如下:TARGETS:DEPENDENCIESCOMMAND或者TARGETS:DEPENDENCIES;COMMANDCOMMANDTARGETS是以空格隔开的文件名,统配符可以使用。通常一个规则只有一个目标,偶尔也有多个。命令行以TAB键开始。第一条命令可在依赖关系的下一行;或者在同一行,在分号后面;两种方式效果相同。因为$符号被用做变量引用,如果要在规则中使用$符号,必须写两个:$。可以用符号来分割一个长行,这不是必须的,因为ake对行的长度没有限制。通配符规则中的文件名可以包含统配符,如*,?。文件名前的字符有特殊的含义。单独使用,或跟随一个

37、/,代表用户的home目录,比如/bin扩展为/home/you/bin;如果跟随一单词,表示单词指示的那个用户的home目录,如john/bin扩展为/home/john/bin。通配符在目标,依赖关系,命令中自动扩展,其它情况下,统配符的扩展除非显式使用wildcard函数。通配符的特殊意义可以使用符号关闭。例子:clean:rm-f*.o和print:*.clpr-p$?touchprint通配符在定义变量时并不扩展,例如:objects=*.o贝objects的值是字符串*.o;但是如果你将objects用于目标,依赖或命令中,扩展会进行。要将objects设置成扩展过的内容,使用:o

38、bjects:=$(wildcard*.o)通配符的缺陷这是一个使用通配符的例子,但结果不是你所期望的。假设可执行文件foo是从当前目录中的所有.o文件生成的:objects=*.ofoo:$(objects)cc-ofoo$(CFLAGS)$(objects)objects变量的值是字符串*.o。通配符扩展在规则foo中进行,于是所有存在的.o文件成为foo的依赖而且在需要时重新编译。但如果删除了所有的.o文件呢?当通配符不匹配任何文件时,一切都保持原样:则foo依赖于一个叫做*.o的文件;由于这个文件不大可能存在,make程序会报告一个无法生成*.o文件的错误,这不是期待的结果。实际上可以

39、用通配符获得期望结果,但是需要复杂的技术,包括wildcard函数和字符串替换函数。332wildcard函数通配符自动在规则中进行。但是在变量赋值的和函数的参数中通配符不会扩展,如果在这些情况下需要通配符扩展,必须使用wildcard函数。语法如下:$(wildcardPATTERN.)这个在makefile任何地方出现的字符串,会被匹配任何一个文件名格式的以空格隔开的现有文件列表替换。如果没有任何文件匹配一个模式,这个模式从wildcard的输出中忽略,注意,这和上述的通配符的处理是不一样的。wildcard函数的一个功能是找出目录中所有的.c文件:$(wildcard*.c)可以通过替换

40、后缀.c为.o从C文件列表得到目标文件的列表:$(patsubst%.c,%.o,$(wildcard*.c)这样,上节中的makefile改写为:objects:=$(patsubst%.c,%.o,$(wildcard*.c)foo:$(objects)cc-ofoo$(objects)这个makefile利用了编译C程序的隐含规则,所以不需要对编译写出显式的规则。(:=是=的一个变体)注意:PATTERN是大小写敏感的。目录搜索对于大的系统,通常将源文件和目标文件放在不同的目录中。目录搜索功能可以让make自动在多个目录中搜寻依赖文件,当你将文件重新分布是,不需要改变规则,更改搜索路径即

41、可。VPATHmake变量VPATH列出make应当搜索的目录列表。很多情况下,当前目录不包含依赖文件,VPATH描述一个对所有文件的搜索列表,包含那些是规则的目标的文件。如果一个目标或者依赖文件在当前目录没找到的话,make在VPATH中列出的目录中查找同名的文件。如果找到的话,那个文件成为依赖文件;规则可以象这些文件在当前目录中一样来使用他们。在VPATH变量中,目录名以冒号或空格隔开;目录列出的顺序决定make查找的顺序。(注:在pSOSystem2.5移植到Win32的GNUmake目录名必须使用分号隔开,以下均简称Win32GNUmake)。举例说明:VPATH=src:./head

42、ers则规则foo.o:foo.c被解释为foo.o:src/foo.c假设foo.c在当前目录不存在,在src目录中可以找到。选择性搜索与VPATH变量相似但更具选择性的是vpath指令(注意是小写),可以指定对于符合特定模式文件的查找路径。这样可以为不同类型的文件指定不同的搜索路径。vpath指令共有三中形式:vpathPATTERNDIRECTORIES为匹配PATTERN的文件名指定搜索路径DIRECTORIES,目录的分隔和VPATH的相同vpathPATTERN清除为匹配PATTERN的文件名指定的搜索路径vpath清除所有以前用vpath指定的搜索路径vpath的模式是包含%的字

43、符串:这个字符串必须匹配需要搜索的依赖文件名,%字符匹配0个或多个任意字符。例如:.h匹配任何以.h结尾的文件如果没有贝SPATTERN必须和依赖文件完全一致这种用法不太多)。当当前目录中不存在依赖文件时,如果vpath中的PATTERN匹配依赖文件名,则指令中DIRECTORIES列出的目录和VPATH中同样处理。举例:vpath%.h./headers告诉make在当前目录中未找到的.h文件在./headers目录中查找。如果多个vapth的模式匹配依赖文件名,make将逐一处理,在所有指定的目录中搜索。Make按照vapth在makefile中的次序;来处理它们,多个相同模式的vapth

44、是相互独立的。vpath%.cfoovpath%blishvpath%.cbar将按照foo,blish,bar的次序查找.c文件。而vpath%.cfoo:barvpath%blish按照foo,bar,blish的顺序搜索。使用自动变量目录搜索的结果并不改变规则中的命令:命令按原样被执行。因此,必须写出与目录搜索功相适应的命令。这可以通过使用$人这样的自动变量来完成。$A表示规则中的所有依赖文件,包含它们所在的目录名(参见目录搜索);$表示目标。例如:foo.o:foo.ccc-c$(CFLAGS)$A-o$通常情况下,依赖文件也包含头文件,但命令中并不提及这些文件:变量$表示第一个依赖文

45、件:VPATH=src:./headersfoo.o:foo.cdefs.hhack.hcc-c$(CFLAGS)$bigoutput:text.ggeneratetext.g-bigbigoutputlittleoutput:text.ggeneratetext.g-littlelittleoutput等同。这里假设程序generate产生两种输出:一种使用-big选项,一种使用-little选项。如果想象使用$变化命令那样来变化依赖关系,不能通过多目标的普通规则实现,但是可以通过模式规则来实现。一个目标多条规则一个文件可以是多条规则的目标,所有规则的依赖关系被合并。如果目标比任一个依赖文件

46、旧,命令被执行。一个文件只能有一组命令执行。如果多个规则对于同一个文件都给出了命令,make使用最后一组并打印错误信息(特殊情况:如果文件名以开始,并不打印错误信息,这一点是为了和其它make兼容)。没有任何理由需要将makefile写成这样,这是make给出错误信息的理由。一条只有依赖关系的附加规则可以一次给出许多文件的附加依赖文件。例如objects变量表示系统中编译器的所有输出.,说明当config.h更改时所有文件必须重做的简单方法如下:objects=foo.obar.ofoo.o:defs.hbar.o:defs.htest.h$(objects):config.h不用改变实际目标

47、文件生成的规则,这条规则可以在需要增删附加的依赖关系时插入或提出。另一个诀窍是附加的依赖关系可以用变量表示,在make执行时,可以给变量赋值:extradeps=$(objects):$(extradeps)当命令makeextradeps二foo.h执行时会认为foo.h是每个目标文件的依赖文件,但简单的make命令不是这样。静态模式规则静态模式规则(staticpatternrules)可以指定多个目标,并且使用目标名字来建议依赖文件的名字;比普通多目标规则更通用因为不需要依赖关系是相同的:依赖关系必须类似但不需要相同。语法TARGETS.:TARGET-PATTERN:DEP-PATTE

48、RNS.COMMANDSTARGETS列表指出规则应用的目标,可以包含通配符,于普通规则的目标相同。TARGET-PATTERN和DEP-PATTERNS来表明目标的依赖关系如何计算:匹配TARGET-PATTERN的目标从名字中抽出一部分,叫做词干(stem),词干被替换到DEP-PATTERNS来形成依赖文件名。每个模式通常包含一个%字符。当TARGET-PATTER匹配一个目标时,%字符可以匹配目标名中的任何部分;这部分即是词干,模式的其余部分必须完全匹配。例如foo.o匹配.0,foo是词干;目标foo.c和foo.out并不匹配这个模式。目标的依赖文件名通过将DEP-PATTERNS

49、中的替换为词干形成:如果依赖模式为.c,在替换词干foo可以得到foo.c。依赖模式中不包含也是合法的,此依赖文件对所有的目标均有效。如果需要在模式规则中使用%字符,必须在其前面加字符,如果%前的字符是有实际意义的,必须在其前面加,其它的不必如此处理。如the%weird%pattern在有效的前是the%weird,其后是pattern。最后的保持原样是因为其并不影响字符。以下例子从相应的.c文件编译foo.o和bar.o:objects=foo.obar.o$(objects):%.o:%.c$(CC)-c$(CFLAGS)$-o$每个目标必须匹配目标模式,对于不匹配的目标会给出警告。如果

50、列表中只有部分文件匹配模式,可以使用filter函数移去不匹配的文件名:files=foo.elcbar.olose.o$(filter%.o,$(files):%.o:%.c$(CC)-c$(CFLAGS)$-o$(filter%.elc,$(files):%.elc:%.elemacs-fbatch-byte-compile$命令generate执行时,$*扩展为词干big或little。静态模式规则和隐式规则静态模式规则和隐式规则在作为模式规则是具有很多共同点,都有目标模式和构造依赖文件名的模式,不同之处在于make决定何时应用规则的方法。隐式规则可应用于匹配其模式的任何目标,但只限于没

51、有指定命令的目标,如果有多条可应用的隐式规则,只有一条被使用,取决于规则的顺序。反之,静态模式规则适用于规则中明确目标列表,不适用于其它目标且总是适用于指定的每个目标。如果有两条冲突的规则,且都有命令,这是一个错误。静态模式规则比隐式规则优越之处如下:可为一些不能按句法分类,但可以显式列出的文件重载隐式规则不能判定目录中的精确内容,些无关的文件可能导致make适用错误的隐式规则;最终结果可能依赖于隐式规则的次序。适用静态模式规则时,这种不确定性是不存在的:规则适用于明确指定的目标。双冒号规则双冒号规则(Double-colonrules)的目标后是::而不是:,当一个目标出现在多条规则中时,其

52、处理和普通规则的处理不同。当个目标出现在多条规则中时,所有规则必须是相同类型的:都是普通的或者都是双冒号的。如果是双冒号,规则之间相互独立;如果目标需要更新,则规则的命令被执行;结果可能是没有执行,或者执行了其中一些,或者所有的规则都执行了。同一目标的双冒号规则事实是完全孤立的,每条规则被被单独处理,就象不同目标的规则一样;规则按照在makefile中出现的次序被处理,此类规则真正有意义的是那些于命令执行次序无关的。这种规则有时比较晦涩不是特别有用;它提供了一种机制:通过不同依赖文件的更新来对目标进行不同的处理,这种情形很罕见。每个这种规则应当提供命令,如果没有,适用的隐式规则将使用。自动生成

53、依赖关系在makefile中,许多规则都是一些目标文件依赖于一些头文件。例如:main.c通过#include使用defs.h,这样规则:main.o:defs.h告诉make在defs.h变化时更新main.o。在程序比较大时,需要写许多这样的规则;而且当每次增删#include时,必须小心的更新makefile。许多现代的编译器可以帮你写这些规则,通常这是通过编译器的-M选项,例如命令:cc-Mmain.c输出以下内容:main.o:main.cdefs.h这样就不必写这些规则,有编译器代劳了。注意这样的依赖关系中提及main.o,不会被隐式规则认为是中间文件,这意味这make在使用过它之

54、后不会将其删除。使用老的make程序时,习惯做法是使用makedepend命令利用编译器的功能产生依赖关系,该命令会产生一个depend文件包含所有自动产生的依赖关系,然后在makefile中使用include将其读入。使用GNU的make时,重新生成makefile的功能使得这种做法变得过时:从不需要显式请求更新依赖关系,因为它总是重新生成任何过时的makefile。自动依赖关系生成推荐的做法是对每个源文件做一个makefile。对每个源文件NAME.c,有一个makefileNAME.d,其中列出了目标文件NAME.。依赖的所有文件,这样在源文件更新时,需要扫描来产生新的依赖关系。例子是一

55、个从NAME.c产生依赖关系文件NAME.d的模式规则:%.d:%.c$(SHELL)-ec$(CC)-M$(CPPFLAGS)$-e选项是当$(CC)命令失败时(exit状态非O),shell立刻退出。通常shell的返回值是管道中最后一条命令(sed)的返回值,这样make不会注意到编译器出错。使用GNU的C编译器时(gcc),可以用-MM选项来代替-M选项,这样省略系统头文件的依赖关系。sed命令的目的是将main.o:main.cdefs.h转换为main.omain.d:main.cdefs.h这样使得每个.d文件依赖于.o文件相应源文件和头文件,make则可以在原文间或头文件变化时

56、更新依赖关系文件。如果定义了生成.d文件的规则,可以使用include指令来读入所有的文件:sources=foo.cbar.cinclude$(sources:.c=.d)例中使用替换变量来将源文件列表foo.cbar.c转换为依赖关系文件的列表。因为.d文件和其它文件一样,不需要更多工作,make会在需要时重新生成它们。规则的命令是由一一执行的shell命令组成。除了以分号隔开写在依赖关系后的命令,每个命令行必须以tab字符开始空行和注释行可以出现在命令行中,处理时被忽略(注意:以tab字符开始的空行不是空行,是一条空命令)。可以在命令中使用任何程序,但这些程序是(SHELL)来执行的。4

57、.1.回显通常make打印出要执行的命令,称之为回显,这和亲自敲命令的现象是一样的。当行之前有字符时,命令不再回显,字符在传递给shell前丢弃。典型的用法是只对打印命令有效,比如echo命令:echoAbouttomakedistributionfiles当make使用-n或Tust-print选项时,显示要发生的一切,但不执行命令。只有在这种情况下,即使命令以开始,命令行仍然显示出来。这个选项对查看make实际要执行的动作很有用。-s或一silent选项阻止make所有回显,就象所有命令以开始一样;一条没有依赖关系的.SILENT规则有相同的作用,但是更加灵活。执行在需要执行命令更新目标时

58、,make为每一行创建一个子shell来执行。这意味着诸如为进程设置局部变量的shell命令cd(改变进程的当前目录)不会影响以后的命令。如果需要cd影响下一个命令,将它们放在一行上用分号隔开,这样make认为是一条命令传递给shell程序(注意:这需要shell支持):foo:bar/losecdbar;gobblelose./foo另一个形式使用续行符:foo:bar/losecdbar;gobblelose./fooshell程序的名字是通过SHELL变量来取得的。(*UNIX)不象大多数变量,SHELL变量不是通过环境来设置的(即需要在makefile中设置),因为SHELL环境是个人

59、选择的,如果不同人的选择会影响makefile的功能的话,这样很糟糕。并行执行GNUmake可以一次执行几条命令。通常make-次执行一条命令,等待其返回,再执行下一条。使用j或Tobs可以同时执行多条命令。如果j后梗一个正数,表示一次可以执行的命令条数;如果-j之后没有参数,则不限制可执行的命令数。缺省的数量是一。一个讨厌的问题是如果同时执行多条命令,它们的输出会混在一起;另一个问题是两个进程不能从同一个设备获得输入。错误每条shelI命令返回时,make会检查其返回状态。如果命令执行成功,则下一条命令被执行,最后一条命令执行完后,规则执行结束。如果有错误(返回非0状态),make放弃当前规

60、则,也可能是所有规则。有时候命令执行错误并不是问题,比如使用mkdir命令确保目录存在:如果目录一存在,贝mkdir会报告错误,但仍希望make继续。要忽略命令的错误,在命令之前使用-字符,-字符在传递给shell之前被丢弃:clean:-rm-f*.o如果使用-i或一ignore-errors选项,make会忽略所有命令产生的错误;一条没有依赖关系的.IGNORE规则有相同的作用,但-更灵活。在忽略错误时,make将错误也认为是成功,只是通知你命令的退出状态和和错误被忽略。如果make并未告知忽略错误,在错误发生时,表明该目标不能成功更新,直接或间接依赖于此的目标当然也不能成功;这些目标的命

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论