Linux操作系统应用及编程
第3节 GNU make项目管理
更新于2008-05-17 15:44:43

通过上面的介绍,知道通常编译一个文件需要很多的选项。如果一个项目包含的源代码文件较多,比如Linux内核,包含数百个源代码文件,把它编译成一个二进制文件,需要很复杂的过程。GNU提供了项目管理工具make,能够解决这个问题,使程序员能集中精力于代码编写,不用为项目的编译过多的浪费精力。
   使用make实际上有两个原因:编译每一个源代码文件的命令行可以写在make文件中,不用重新输入,避免了烦琐和错误。另外,make可以根据依赖规则只对需要重新编译的源文件进行编译,这样可以节省编译时间。
   make运行时首先查找名为GNUmakefile的文件,其次是makefile或Makefile,只要找到任何一个,就会以它的内容为依据,进行编译工作。一般Linux程序员都采用Makefile文件。下面的工作主要讨论make文件的编写方法。


8.3.1 编写make文件
   make文件是一个文本文件,有一些规则组成,这些规则加在一起,组成产生最终文件的方法。每一个规则由三部分组成:目标,依赖文件,命令列表。就象下面这样:
   target : dep1 dep2 dep3 [...]
           command1
           command2
           ...
   上面的target就是目标文件,是要生成的文件。dep1 dep2 dep3等是生成目标文件需要依赖的文件。就是说,生成目标文件必须dep1 dep2 dep3等依赖文件存在并且是最新的。下面的command1、command2等是生成目标文件需要的命令列表。需要注意的是,每一个命令前面必须是制表符,不能用空格代替,否则make会出错。这些听起来很抽象,下面举一个例子说明:
   ipphone : ipphone.o
       gcc –o ipphone ipphone.o
   ipphone.o : ipphone.c ipphone.h
       gcc –c ipphone.c
   ctl : ctl.o
       gcc –o ctl ctl.o
   ctl.o : ctl.c ipphone.h
       gcc –c ctl.c
   上面的文件包含4条规则。目标文件ipphone依赖文件ipphone.o,如果ipphone.o不存在或者比较旧,就需要首先生成文件ipphone.o。如果ipphone.o较新,不需重新生成,就比较目标文件ipphone和依赖文件ipphone.o的新旧。如果目标文件ipphone比较旧或者不存在,就要重新生成。要生成ipphone文件,需要执行下面的命令gcc –o ipphone ipphone.o。怎样知道文件的新旧呢?make程序只是简单的比较目标文件和依赖文件的时间。比如,我们修改了文件ctl.c,它的时间就会变的较新。按照上面的规则,ctl.o文件就会重新生成。这时ctl.o文件又会变得较新,按照规则,ctl文件又会重新生成。make就是按照描述的规则管理项目的编译和连接的。这是自动化的思想。可是,make是否要检查make文件中所有的规则呢?如果make命令行中没有包含规则名,就会检查所有规则。如果包含规则名,就只检查相应规则和相联系的规则。比如,只想生成最新的ctl文件,需要用命令make ctl即可。make会检查ctl和其依赖的文件是否最新,如果是就不再做任何操作,否则重新编译生成最新文件。


8.3.2 伪目标
   有时候目标不一定是一个文件。比如,下面的规则:
   all : ctl ipphone
   因为all并不是一个实际存在的文件,所以总是要被认为需要执行该规则。但该规则并不包含任何命令。所以,make all只是起一个检查ctl和ipphone的作用。
   在另外的情况下,不仅目标文件不是真正的文件,而且没有依赖文件。比如:
   clean :
       rm –f *.o
   因为没有依赖文件,所以缺省的情况下总不会被执行。但是执行make clean时,就会执行下面的命令rm –f *.o用来清除所有编译过程的中间文件。这对软件的重新编译很有用。
   这会带来一个问题。如果恰好存在一个文件名字是all或clean,哪会怎么样呢?对于all,不会带来什么影响,但是对于clean,就会影响命令的执行。这时,需要使用make的特殊目标.PHONY,.PHONY目标的相关文件的含义和通常一样,但是不检查文件是否存在,而直接执行文件规则。
   .PHONE : clean
   clean :
       rm –f *.o


8.3.3 变量
   为了对make文件的编辑和维护更加方便,可以在make文件中定义和使用变量。和shell程序一样,make文件中的变量内容实际上是一个字符串,定义和使用的规则同shell程序差不多。比如:
   CC=gcc –c
   使用变量时和shell程序类似,用括号把变量名括起来,前面加$符号。
   $(CC) source.c
   和gcc –c source.c有相同的作用。如果变量定义时引用了自己,make会把它递归展开,从而引起问题,如:
   CC=gcc
   CC=$(CC) –o
   上面的语句目的是让CC定义为gcc –o,不幸的是,make会把CC的定义不断地带入上面的定义中,从而永远得不到结果。解决的办法是定义简单展开变量,从而告诉make不要递归展开:
   CC := gcc –o
   CC += -O2
   上面的定义就避免了递归调用。
   除了用户自己定义的变量外,make文件中还允许使用系统的环境变量。实际上,在make启动时为系统的环境变量定义了同名的变量,并赋予和环境变量相同的值。当然,如果有必要,可以从新定义该变量的值。
   另外,就象shell程序中一样,make也提供了一些只读的变量,它代表一些固定的信息,是用户不能重新定义的。如:


      make还有一些予定义变量,可以根据需要改变其缺省值。下面仅列出少数一些:


   8.3.4 隐含规则和规则模式
   看下面的make文件:
   ipphone : ipphone.o
       gcc –o ipphone ipphone.o
   它指出了目标文件ipphone依赖的文件是ipphone.o,但是并没有指出ipphone.o怎样得来。这时候就适合make的隐含规则,make会从ipphone.c中用命令gcc –c ipphone.c –o ipphone编译生成这个文件。实际上,make不仅支持源文件是C语言文件,也同样支持Pascal,Fortran等文件,它会自动去寻找相关的源文件,并用适当的编译命令去编译它。
   这种规则模式也可以用命令重新定义。比如:
   %.o : %.c
   表示所有扩展名.o的文件都是由扩展名.c的文件编译而来。编译的方法除了缺省值外,还可以从新定义:
   %.o : %.c
       $(CC) –c $< -o $@
   上面使用了自动变量$<和$@。
8.3.5 make命令行参量
   make命令也有很多命令行选项。下面给出常用的部分:

 

网友留言