存档文件格式简介
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
善用GNU Make做开发
2005-04-05 09:43:00.0 推荐:353 收藏:224 评论:0 来源:赛迪网-开放系统世界在编写小型的Linux应用程序时,一般情况下只会有少数几个源文件。这样程序员能够很容易地理清它们之间的包含和引用关系。但随着软件项目逐渐变大,对源文件的处理也将变得越来越复杂起来。此时单纯依赖手工方式进行管理的做法就显得有些力不从心了。为此,Linux专门为软件开发提供了一个自动化管理工具GNU make。通过它,程序员可以很方便地管理软件编译的内容、方式和时机,从而使程序员能够把主要精力集中在代码
的编写上。
make将整个软件项目的代码分开放在几个小的源文件里,在改动其中一个文件的时候,可以只对该文件重新进行编译,然后重新连接所有的目标文件。对于那些由许多源文件组成的大型软件项目来说,全部重新进行编译需要花费很长的时间,而采用这种项目管理方法则可以极大地提高工作效率,让原本复杂繁琐的开发工作变简单。
Makefile文件
GNU make是一个用来控制软件构建过程的自动工具,程序员通过定义构建规则来控制代码的创建过程。这些规则通常定义在一个名为Makefile的文件中。Makefile被用来告诉make编译哪些文件、怎样编译和何时编译。Makefile中的每条规则事实上都包含如
下一些内容:
目标(target)是make最终需要创建的对象;
依赖(dependency)通常是一个列表,指明编译目标时需要用到的其它文件;
命令(command)也是一个列表,指明从依赖文件创建出目标对象所需要执行的命
令。
虽然Makefile中的目标通常都是可执行程序,但事实上可以是诸如文本文件和HTML 页面等任何内容,甚至能够用来测试或设置环境变量。Makefile中的命令则不仅可以是编
译命令,还可以是任何Shell命令。
先来看一个例子。假设整个软件项目是由control.c、io.c和main.c三个源文件所构
成的,编写的Makefile文件内容如下:
在将上述Makefile文件与源文件保存到同一目录之后,就可以在命令行中输入“make”命令来编译整个项目了。make在执行过程中,首先会查找到Makefile文件第
一条规则中的目标,即上述文件中的all。根据设定好的规则,该目标需要依赖于program。由于all并不是一个已经存在的文件,所以每次在make被调用的时候,显然都需要先检查program。继续往下不难发现,program目标是依赖于control.o、ui.o和main.o 的。这就意味着如果其中任何一个比生成的可执行文件要新,那么就需要重新构建可执行文件program,否则就没有必要执行这一步了。
在Makefile文件的其余部分,为每一个中间生成的目标文件都专门定义了一条规则,用来指明创建过程中它们与C源文件的依赖性。也就是说,如果一个特定的C源文件被更新了,那么与之对应的目标文件也必须重新生成。下面是make在构建项目过程中的输出结果:
不难看出,首先是C源文件被编译成目标文件,然后才是目标文件被连接成最终的可执行文件。由于相互间依赖关系的制约,这些步骤会被有条不紊地依次执行。最终可执行文件要求目标文件都被更新过,而每个目标文件则要求C源文件被更新过。如果此时重新执行“make”命令,会出现下面的结果。原因是程序已经被编译过了,并且没有做过任何改动,所以就没有再编译的必要了:
如果只是改变了其中的部分文件,那么make会自动检测出需要对哪些源文件重新进行编译,并连接成最后的可执行文件。用户可以参考下面的过程:
当make检测到main.o目标时,发现main.c文件已经被更新,于是main.o文件必须被重新编译,相应地program需要被重新连接。make的魅力就在于能够自动进行条件检测,并采取适当的行动。它永远也不会去编译那些没有改动过的源文件
变量
为了简化Makefile的编写,make引入了变量。变量实际上是为文本串在Makefile 中定义一个便于记忆的名称。变量的定义和应用与Linux的环境变量一样,变量名大写,
变量一旦定义之后,就可以通过将变量名用圆括号包起来,并在前面加上“$”符号来进行
引用。
变量一般都在Makefile的头部定义。如果变量的值发生了改变,很显然只需在一个地方进行修改就可以了,从而大大简化了Makefile的维护。下面是将前面用到的Makefile
利用变量进行改写后的结果:
make将其使用的变量细分为两类:递归展开变量和简单展开变量。递归展开变量在被引用时会逐层展开,即如果在展开式中包含了对其它变量的引用,则这些变量也会被展开,直到没有需要被展开的变量为止。假设变量TOPDIR和SUBDIR的定义如下:
此时变量SUBDIR的值在解析时会被正确地展开为/home/xiaowp/project,但对于下面的定义:
很清楚,希望得到的结果是/home/xiaowp/project/src,但实际并非如此。SUBDIR 在引用时会被递归展开,从而陷入一个无限循环当中,make能够检测到这个问题并报告如下错误:
*** Recursive variable 'SUBDIR' references itself (eventually). Stop
为了避免这个问题,可以使用简单展开变量。与递归展开变量在引用时展开不同,简单展开变量是在定义处展开的,并且只展开一次,从而消除了变量的嵌套引用。在定义时,其语法与递归展开变量有细微的不同:
SUBDIR在第一次定义时使用“:=”将其值设置为“/home/xiaowp/project”,而在第二次定义时则使用“+=”在已有的基础上添加“/src”,这样就使得SUBDIR的最终值变为“/home/xiaowp/project/src”。许多程序员在Makefile中只使用简单展开变量,以避免可能出现的错误。
除了用户自定义变量之外,在Makefile中还可以使用环境变量、自动变量和预定义变量。使用环境变量的方法相对来讲比较简单,make在启动时会自动读取系统当前已经定义了的环境变量,并且会创建与之具有相同名称和数值的变量。需要注意的是,如果用户在Makefile中定义了相同名称的变量,那么用户自定义变量将会覆盖同名的环境变量。
表1 常用自动变量
此外,make还提供了一些预定义变量和自动变量,但它们看起来都不如自定义变量那么直观。之所以称为自动变量是因为make会自动用特定的、熟知的值来替换它们,表1给出了常用的部分自动变量。
利用make的自动变量和预定义变量,可以简化前面给出的那个Makefile文件:
伪目标