专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »嵌入式开发 » gnumake:善用GNU Make做开发 »正文

gnumake:善用GNU Make做开发

来源: 发布时间:星期四, 2008年12月11日 浏览:8次 评论:0
=tf width="98%" align=center border=0>
=bw width="100%">=htd id=font_word style="FONT-SIZE: 14px; FONT-FAMILY: 宋体, Verdana, Arial, Helvetica, sans-ser">作者:肖文鹏
dCYfbaiducukf6o在编写小型Linux应用般情况下只会有少数几个源文件这样员能够很容易地理清它们之间包含和引用关系但随着软件项目逐渐变大对源文件处理也将变得越来越复杂起来此时单纯依赖手工方式进行管理做法就显得有些力不从心了为此Linux专门为软件开发提供了个自动化管理工具 GNU make通过它员可以很方便地管理软件编译内容、方式和时机从而使员能够把主要精力集中在代码编写上 
dCYfbaiducukf6o
dCYfbaiducukf6o   & nbspmake将整个软件项目代码分开放在几个小源文件里在改动其中个文件时候可以只对该文件重新进行编译然后重新连接所有目标文件对于那些由许多源文件组成大型软件项目来说全部重新进行编译需要花费很长时间而采用这种项目管理方法则可以极大地提高工作效率让原本复杂繁琐开发工作变简单 
dCYfbaiducukf6o
dCYfbaiducukf6o    Makefile文件 
dCYfbaiducukf6o
dCYfbaiducukf6o    GNU make 是个用来控制软件构建过程自动工具员通过定义构建规则来控制代码创建过程这些规则通常定义在个名为Makefile文件中 Makefile被用来告诉make编译哪些文件、怎样编译和何时编译Makefile中每条规则事实上都包含如下些内容:
dCYfbaiducukf6o    ◆ 目标(target)是make最终需要创建对象;
dCYfbaiducukf6o    ◆ 依赖(dependency)通常是个列表指明编译目标时需要用到其它文件;
dCYfbaiducukf6o    ◆ 命令(command)也是个列表指明从依赖文件创建出目标对象所需要执行命令 
dCYfbaiducukf6o
dCYfbaiducukf6o    虽然Makefile中目标通常都是可执行但事实上可以是诸如文本文件和HTML页面等任何内容甚至能够用来测试或设置环境变量Makefile中命令则不仅可以是编译命令还可以是任何Shell命令 
dCYfbaiducukf6o
dCYfbaiducukf6o    先来看个例子假设整个软件项目是由control.c、io.c和.c三个源文件所构成编写Makefile文件内容如下: 
dCYfbaiducukf6o
dCYfbaiducukf6oall : program
dCYfbaiducukf6oprogram : control.o ui.o .o
dCYfbaiducukf6ogcc -o program control.o ui.o .o
dCYfbaiducukf6ocontrol.o : control.c
dCYfbaiducukf6ogcc -Wall -c -o control.o control.c
dCYfbaiducukf6oui.o : ui.c
dCYfbaiducukf6ogcc -Wall -c -o ui.o ui.c
dCYfbaiducukf6o.o : .c
dCYfbaiducukf6ogcc -Wall -c -o .o .c
dCYfbaiducukf6oclean :
dCYfbaiducukf6orm -f program *.o
dCYfbaiducukf6o 
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o     在将上述Makefile文件与源文件保存到同目录之后就可以在命令行中输入“make”命令来编译整个项目了make在执行过程中首先会查找到 Makefile文件第条规则中目标即上述文件中all根据设定好规则该目标需要依赖于program由于all并不是个已经存在文件所以每次在make被时候显然都需要先检查program继续往下不难发现program目标是依赖于control.o、ui.o和 .o这就意味着如果其中任何个比生成可执行文件要新那么就需要重新构建可执行文件program否则就没有必要执行这步了 
dCYfbaiducukf6o
dCYfbaiducukf6o    在Makefile文件其余部分为每个中间生成目标文件都专门定义了条规则用来指明创建过程中它们与C源文件依赖性也就是说如果个特定C源文件被更新了那么与之对应目标文件也必须重新生成下面是make在构建项目过程中输出结果: 
dCYfbaiducukf6o
dCYfbaiducukf6o#make
dCYfbaiducukf6ogcc -Wall -c -o control.o control.c
dCYfbaiducukf6ogcc -Wall -c -o ui.o ui.c
dCYfbaiducukf6ogcc -Wall -c -o .o .c
dCYfbaiducukf6ogcc -o program control.o ui.o .o
dCYfbaiducukf6o 
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o     不难看出首先是C源文件被编译成目标文件然后才是目标文件被连接成最终可执行文件由于相互间依赖关系制约这些步骤会被有条不紊地依次执行最终可执行文件要求目标文件都被更新过而每个目标文件则要求C源文件被更新过如果此时重新执行“make”命令会出现下面结果原因是已经被编译过了并且没有做过任何改动所以就没有再编译必要了: 
dCYfbaiducukf6o
dCYfbaiducukf6o# make
dCYfbaiducukf6omake: Nothing to be done for 'all'.
dCYfbaiducukf6o 
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o    如果只是改变了其中部分文件那么make会自动检测出需要对哪些源文件重新进行编译并连接成最后可执行文件用户可以参考下面过程: 
dCYfbaiducukf6o
dCYfbaiducukf6o#touch .c
dCYfbaiducukf6o# make
dCYfbaiducukf6ogcc -Wall -c -o .o .c
dCYfbaiducukf6ogcc -o program control.o ui.o .o
dCYfbaiducukf6o 
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o     当make检测到.o目标时发现.c文件已经被更新于是.o文件必须被重新编译相应地program需要被重新连接 make魅力就在于能够自动进行条件检测并采取适当行动它永远也不会去编译那些没有改动过源文件因此大大节省了在开发大型软件项目时所浪费在编译上时间 
dCYfbaiducukf6o
dCYfbaiducukf6o    变量 
dCYfbaiducukf6o
dCYfbaiducukf6o    为了简化Makefile编写make引入了变量变量实际上是为文本串在 Makefile中定义个便于记忆名称变量定义和应用与Linux环境变量变量名大写变量旦定义之后就可以通过将变量名用圆括号包起来并在前面加上“$”符号来进行引用 
dCYfbaiducukf6o
dCYfbaiducukf6o    变量般都在Makefile头部定义如果变量值发生了改变很显然只需在个地方进行修改就可以了从而大大简化了Makefile维护下面是将前面用到Makefile利用变量进行改写后结果: 
dCYfbaiducukf6o
dCYfbaiducukf6oOBJS = control.o ui.o .o
dCYfbaiducukf6oCC = GCC
dCYfbaiducukf6oCFLAGS = -Wall
dCYfbaiducukf6oall : program
dCYfbaiducukf6oprogram : $(OBJS)
dCYfbaiducukf6o$(CC) $(OBJS) -o program
dCYfbaiducukf6ocontrol.o : control.c
dCYfbaiducukf6o$(CC) $(CFLAGS) -c -o control.o control.c
dCYfbaiducukf6oui.o : ui.c
dCYfbaiducukf6o$(CC) $(CFLAGS) -c -o ui.o ui.c
dCYfbaiducukf6o.o : .c
dCYfbaiducukf6o$(CC) $(CFLAGS) -c -o .o .c
dCYfbaiducukf6oclean :
dCYfbaiducukf6o    rm -f program $(OBJS)
dCYfbaiducukf6o 
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o   & nbspmake将其使用变量细分为两类:递归展开变量和简单展开变量递归展开变量在被引用时会逐层展开即如果在展开式中包含了对其它变量引用则这些变量也会被展开直到没有需要被展开变量为止假设变量TOPDIR和SUBDIR定义如下: 
dCYfbaiducukf6o
dCYfbaiducukf6oTOPDIR = /home/xiaowp
dCYfbaiducukf6oSUBDIR = $(TOPDIR)/project
dCYfbaiducukf6o 
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o    此时变量SUBDIR值在解析时会被正确地展开为/home/xiaowp/project但对于下面定义: 
dCYfbaiducukf6o
dCYfbaiducukf6oTOPDIR = /home/xiaowp
dCYfbaiducukf6oSUBDIR = $(TOPDIR)/project
dCYfbaiducukf6oSUBDIR = $(SUBDIR)/src
dCYfbaiducukf6o 
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o    很清楚希望得到结果是/home/xiaowp/project/src但实际并非如此SUBDIR在引用时会被递归展开从而陷入个无限循环当中make能够检测到这个问题并报告如下
dCYfbaiducukf6o    *** Recursive variable 'SUBDIR' references itself (eventually). Stop 
dCYfbaiducukf6o
dCYfbaiducukf6o    为了避免这个问题可以使用简单展开变量与递归展开变量在引用时展开不同简单展开变量是在定义处展开并且只展开从而消除了变量嵌套引用在定义时其语法与递归展开变量有细微不同: 
dCYfbaiducukf6o
dCYfbaiducukf6oTOPDIR = /home/xiaowp
dCYfbaiducukf6oSUBDIR := $(TOPDIR)/project
dCYfbaiducukf6oSUBDIR  /src
dCYfbaiducukf6o 
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o   & nbspSUBDIR在第次定义时使用“:=”将其值设置为“/home/xiaowp/project”而在第二次定义时则使用“”在已有基础上添加“/src”这样就使得SUBDIR最终值变为“/home/xiaowp/project/src”许多员在Makefile中只使用简单展开变量以避免可能出现 
dCYfbaiducukf6o
dCYfbaiducukf6o    除了用户自定义变量之外在Makefile中还可以使用环境变量、自动变量和预定义变量使用环境变量方法相对来讲比较简单make在启动时会自动读取系统当前已经定义了环境变量并且会创建与之具有相同名称和数值变量需要注意如果用户在Makefile中定义了相同名称变量那么用户自定义变量将会覆盖同名环境变量 
dCYfbaiducukf6o 此外make还提供了些预定义变量和自动变量但它们看起来都不如自定义变量那么直观之所以称为自动变量是make会自动用特定、熟知值来替换它们表1给出了常用部分自动变量 
dCYfbaiducukf6o
dCYfbaiducukf6o    利用make自动变量和预定义变量可以简化前面给出那个Makefile文件: 
dCYfbaiducukf6o
dCYfbaiducukf6oOBJS = control.o ui.o .o
dCYfbaiducukf6oCC = GCC
dCYfbaiducukf6oCFLAGS = -Wall
dCYfbaiducukf6oall : program
dCYfbaiducukf6oprogram : $(OBJS)
dCYfbaiducukf6o$(CC) $(OBJS) -o $@
dCYfbaiducukf6ocontrol.o : control.c
dCYfbaiducukf6o$(CC) $(CFLAGS) -c -o $@ $<
dCYfbaiducukf6oui.o : ui.c
dCYfbaiducukf6o$(CC) $(CFLAGS) -c -o $@ $<
dCYfbaiducukf6o.o : .c
dCYfbaiducukf6o$(CC) $(CFLAGS) -c -o $@ $<
dCYfbaiducukf6oclean :
dCYfbaiducukf6o    $(RM) program $(OBJS)
dCYfbaiducukf6o 
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o    伪目标 
dCYfbaiducukf6o
dCYfbaiducukf6o    在Makefile中并不是所有目标都对应于磁盘上文件目标存在只是为了形成条规则从而完成特定工作并不生成新目标文件这样目标称为伪目标它并不是真正意义上目标文件只是为了满足Makefile语法规则而存在 
dCYfbaiducukf6o
dCYfbaiducukf6o     在已经给出Makefile文件中最后个目标clean就是伪目标它规定了make应该执行命令当make处理到目标clean时会先查看其对应依赖对象由于clean没有任何依赖对象所以make会认为该目标是最新而不会执行任何操作为了编译这个目标体必须手工执行如下命令: # make clean 
dCYfbaiducukf6o
dCYfbaiducukf6o    作为惯例clean目标般用于删除最终生成可执行文件和在编译过程中产生所有目标文件问题是如果恰巧有个名为clean文件存在时该怎么办呢?此时在这个规则里没有任何依赖对象所以目标文件肯定是最新规则中命令无论如何也不会被执行即使用命令“make clean”也无济于事解决这问题方法是标明该规则中目标是伪目标并不对应于任何文件这可以通过.PHONY目标实现它告诉make不检查规则目标文件是否存在于磁盘上也不查找任何隐含规则而直接假设指定目标需要被更新就行了在使用了.PHONY之后前面给出Makefile文件就将变为如下内容: 
dCYfbaiducukf6o
dCYfbaiducukf6oOBJS = control.o ui.o .o
dCYfbaiducukf6oCC = GCC
dCYfbaiducukf6oCFLAGS = -Wall
dCYfbaiducukf6oall : program
dCYfbaiducukf6oprogram : $(OBJS)
dCYfbaiducukf6o$(CC) $(OBJS) -o $@
dCYfbaiducukf6ocontrol.o : control.c
dCYfbaiducukf6o$(CC) $(CFLAGS) -c -o $@ $<
dCYfbaiducukf6oui.o : ui.c
dCYfbaiducukf6o$(CC) $(CFLAGS) -c -o $@ $<
dCYfbaiducukf6o.o : .c
dCYfbaiducukf6o$(CC) $(CFLAGS) -c -o $@ $<
dCYfbaiducukf6o.PHONY : clean
dCYfbaiducukf6oclean :
dCYfbaiducukf6o    $(RM) program $(OBJS)
dCYfbaiducukf6o 
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o    其它规则 
dCYfbaiducukf6o
dCYfbaiducukf6o     除了可以在Makefile中明确指定规则(显示规则)之外make还维护了整套隐式规则隐式规则可以在用户没有完整地给出某些命令时候自动执行恰当操作隐式规则最大好处是可以简化Makefile编写和维护例如前面给出Makefile运用隐式规则后可以简化为如下内容: 
dCYfbaiducukf6o
dCYfbaiducukf6oOBJS = control.o ui.o .o
dCYfbaiducukf6oprogram : $(OBJS)
dCYfbaiducukf6o$(CC) $(OBJS) -o $@
dCYfbaiducukf6o.PHONY : clean
dCYfbaiducukf6oclean :
dCYfbaiducukf6o    $(RM) program $(OBJS)
dCYfbaiducukf6o 
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o     默认目标program依赖于control.o、ui.o和.o三个目标文件但Makefile中并没有给出怎样编译生成这些目标规则此时make就会使用隐式规则对每个名为foo.o目标文件找到与之对应源代码foo.c然后使用“gcc -c& nbspfoo.c -o foo.o”命令来生成对应目标文件 
dCYfbaiducukf6o
dCYfbaiducukf6o    除了系统预定义隐式规则外在Makefile中还可以定义自己隐式规则这种规则也被称为模式规则模式规则类似于普通规则但它目标必须含有“%”这通配符以便能与任何非空相匹配与目标对应依赖文件中也必须使用通配符例如下面规则: 
dCYfbaiducukf6o
dCYfbaiducukf6o%.o : %.c
dCYfbaiducukf6o    $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
dCYfbaiducukf6o 
dCYfbaiducukf6o
dCYfbaiducukf6o
dCYfbaiducukf6o    上面规则将告诉make所有形为foo.o目标文件都应该根据指定命令从源文件foo.c编译而来 
dCYfbaiducukf6o
dCYfbaiducukf6o    小结 
dCYfbaiducukf6o
dCYfbaiducukf6o    在构建大型软件项目时make是个优秀持续集成工具它对于软件开发过程来讲非常重要本文介绍了基本make命令以及如何编写简单实用Makefile文件相信用户已经能够使用make来管理软件项目创建和维护过程了

相关文章

读者评论

  • 共0条 分0页

发表评论

  • 昵称:
  • 内容: