Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15609
  • 博文数量: 5
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2014-01-25 14:16
文章分类
文章存档

2014年(5)

我的朋友

分类: 嵌入式

2014-04-01 16:51:42

原文地址:makefile 规则的命令(下) 作者:LYZC11

1.6.3       命令行选项和递归

make的递归执行过程中。最上层(可以称之为主控)make的命令行选项“-k”、“-s”等被自动的通过环境变量“MAKEFLAGS”传递给子make进程。变量“MAKEFLAGS”的值会被主控make自动的设置为包含执行make时的命令行选项的字符串。在主控执行make时使用“-k”和“-s”选项,那么“MAKEFLAGS”的值就为“ks”。子make进程处理时,会把此环境变量的值作为执行的命令行选项,因此子make进程就使用“-k”和“-s”这两个命令行选项。

同样,在执行make时命令行中给定了一个变量的定义(如“make CFLAGS+=-g”),此变量和它的值(CFLAGS+=-g)也会借助环境变量“MAKEFLAGS”传递给子make进程。可以借助make的环境变量“MAKEFLAGS 传递我们在主控make所使用的命令行选项给子make进程。需要注意的是有几个特殊的命令行选项例外,分别是:“-C”、“-f”、“-o”和“-W”。这些命令行选项不会被赋值给变量“MAKEFLAGS”。

Make命令行选项中一个比较特殊的是“-j”选项。在支持这个选项的操作系统上,如果给它指定了一个数值“N”(多任务的系统unixLinux支持,MS-DOS不支持),那么主控make和子make进程会在执行过程使用通信机制来限制系统在同一时刻(包括所有的递归调用的make进程,否则,将会导致make任务的数目数目无法控制而使别的任务无法到的执行)的任务的执行数目不大于“N”。另外,当使用的操作系统不能支持make执行过程中的父子间通信,那么无论在执行主控make时指定的任务数目“N”是多少,变量“MAKEFLAGS”中选项“-j”的数目会都被设置为“1,这样可以确保系统正常运转。

执行多级的make调用时,如果不希望“MAKEFLAGS”的值传递给子make,就需要在执行子make时对它重新进行赋值。例如:

 

subsystem:

      cd subdir && $(MAKE) MAKEFLAGS=

 

此规则取消了子make执行式的命令行选项(将变量的值赋为空)。

在执行make的同时可以通过命令行来定义一个变量,像上例中的那样;前边已经提到过,这种变量是借助环境“MAKEFLAGS”来传递给多级调用的子make进程的。其实真正的命令行中的 变量定义 是通过另外一个变量“MAKEOVRRIDES”来记录的,变量“MAKEFLAGS”引用此变量,因而命令行中的变量定义就可以被记录在环境变量“MAKEFLAGS”中被传递下去。当不希望将上层make在命令行中定义的变量传递给子make时,就可以在上层Makefile中把“MAKEOVERRIDES”赋空来实现(MAKEOVERRIDES=)。这种方式一般很少使用,建议非万不得已您还是最好不使用这种方式(为了和POSIX2.0兼容,当Makefile中出现“.POSIX”这个特殊的目标时,在上层Makefile中修改变量“MAKEOVERRIDES”对子make不会产生任何影响)。另外一些系统中对环境变量的长度存在一个上限,因此当“MAKEFLAGS”的值超过一定的数目时,执行过程出现了类似“Arg list too long”的错误提示。

历史原因,在make中存在另外一个和“MAKEFLAGS”相类似的变量“MFLAGS”。现行版本中此变量保留的原因是和老版本的兼容需要。和“MAKEFLAGS”不同点是:1. 此变量在make的递归调用时不包含命令行选项中的变量定义部分(就是说此变量的定义没有包含对“MAKEOVERRIDES”的引用);2. 此变量的值(除为空的情况)是以“-”开始的,而“MAKEFLAGS”的值只有在长命令选项格式(如:“--warn-undefined-variables”)时才以“-”开头。传统得此变量一般被明确的使用在make递归调用命令中。像下边那样:

 

subsystem:

      cd subdir && $(MAKE) $(MFLAGS)

 

在现行的make版本中,变量“MFLAGS”已经成为一个多余部分。在书写和老版本make兼容的Makefile时可能需要这个变量。当然它在目前的版本上也能够正常的工作。

在某些特殊的场合,可能需要为所有的make进程指定一个统一的命令行选项。比如说需要给所有的运行的make指定“-k”选项(参考 8.7 make的命令行选项 一节)。实现这个目的,我们可以在执行make之前设置一个系统环境变量(存在于当前系统的环境中)“MAKEFLAGS=k”,或者在主控Makefile中将它的值赋为“k”。需要注意的是:不能通过变量“MFLAGS”来实现。

make在执行时,首先将会对变量“MAKEFLAGS”的值(系统环境中或者在Makefile中设置的)进行分析。当变量的值不是以连字符(“-”)开始时,将变量的值按字分开,字之间使用空格分开。将这些字作为命令行的选项对待(除了选项“-C”、“-f”、“-h”、“-o”和“-W”以及他们的长格式,如果其中包含无效的选项也不会提示错误)。

最后需要强调的是:当把“MAKEFLAGS”设置到你的系统环境变量中时,要小心谨慎!将一些调试选项或者特殊选项设置为此变量值的一部分,在执行make时,会对make的正常执行产生影响,甚至是破坏性的影响。例如变量“MAKEFLAGS”中包含选项“t”、“n”、“q”这三个的任何一个,你在执行make时产生的结果可能并不是你所要达到的目的。建议大家最好不要随便更改“MAKEFLAGS”的值,更不要把它设置为系统的环境变量来使用。否则可能会产生一些奇怪甚至让你感到不解的现象。

选项

在多级make递归调用过程中,选项“-w”或者“--print-directory”可以让make在开始编译一个目录之前和完成此目录的编译之后给出相应的提示信息,方便开发人员能够跟踪make的执行过程。例如,在目录“/u/gnu/make”目录下执行“make -w”,将会看到如下的一些信息:

在开始执行之前我们将看到:

 

make: Entering directory `/u/gnu/make'.

 

而在完成之后我们同样将会看到:

 

make: Leaving directory `/u/gnu/make'.

 

通常,选项“-w”会被自动打开。在主控Makefile中当如果使用“-C”参数来为make指定一个目录或者使用“cd”进入一个目录时,“-w”选项会被自动打开。主控make可以使用选项“-s”(“--slient”)来禁止此选项的自动打开。另外,make的命令行选项“--no-print-directory”,将禁止所有关于目录信息的打印。可参考 8.7 make的命令行选项 一节

1.7        定义命令包

在书写Makefile时,可能有多个规则会使用相同的一组命令。就像c语言程序中需要经常使用到函数“printf”。这时我们就会想能不能将这样一组命令进行类似c语言函数一样的封装,以后在我们需要用到的地方可以通过它的名字(c语言中的函数名)来对这一组命令进行引用。这样就可减少重复工作,提高了效率。在GNU make中,可以使用指示符“define”来完成这个功能(关于指示符“define”可参考 5.8 多行定义 一节)。通过“define”来定义这样一组命令,同时用一个变量(作为一个变量,不能和Makefile中其它常规的变量命名出现冲突)来代表这一组命令。通常我们把使用“define”定义的一组命令称为一个命令包。定义一个命令包的语法以“define”开始,以“endef”结束,例如:

 

define run-yacc

yacc $(firstword $^)

mv y.tab.c $@

endef

 

这里,“run-yacc”是这个命令包的名字。在“define”和“endef”之间的命令就是命令包的主体。需要说明的是:使用“define”定义的命令包中,命令体中变量和函数的引用不会展开。命令体中所有的内容包括“$”、“(”、“)”等都是变量“run-yacc”的定义。它和c语言中宏的使用方式一样。关于变量可参考 5 Makefile中的变量

例子中,命令包中第一个命令是对引用它所在规则中的第一个依赖文件(函数“firstword”,可参考 7.2 文本处理函数 一节)运行yacc程序。yacc程序总是生成一个命名为“y.tab.c”的文件。第二行的命令就是把这个文件名改为规则目标的名字。

定义了这样一个命令包后,后续应该如何使用它?前面已经提到,命令包是使用一个变量来表示的。因此我们就可以按使用变量的方式来使用它。当在规则的命令行中使用这个变量时,命令包所定义的命令体就会对它进行替代。由于使用“define”定义的变量属于递归展开式变量(参考 5.2 两种变量定义(赋值) 一节),因此,命令包中所有命令中对其它变量的引用,在规则被执行时会被完全展开。例如这样一个规则:

 

foo.c : foo.y

      $(run-yacc)

 

此规则在执行时,我们来看一下命令包中的变量的替换过程:1. 命令包重中的“$^”会被“foo.y”替换;2. $@”被“foo.c”替换。大家应该对“$<”和“$@”不陌生吧,如果陌生可以 参考 9.5.1 自动化变量 一小节。

当在一个规则中引用一个已定义的命令包时,命令包中的命令体会被原封不动的展开在引用它的地方(和 c语言中的宏一样)。这些命令就成为规则的命令。因此我们也可在定义命令包时使用前缀来控制单独的一个命令行(例如“@”,“-”和“+”)。例如:

 

define frobnicate

     @echo "frobnicating target $@"

     frob-step-1 $< -o $@-step-1

     frob-step-2 $@-step-1 -o $@

endef

 

此命令包的第一行命令执行前不会被回显,其它的命令在执行前都被回显。

另一方面,如果一个规则在引用此命令包之前使用了命令的前缀字符。那么这个前缀字符将会被添加到命令包定义的所有命令行之中。例如:

 

frob.out: frob.in

      @$(frobnicate)

 

这个规则执行时不会回显任何将要执行的命令。命令行回显可参考 4.1 命令回显 一节

1.8        空命令

有时可能存在这样的一个需求,需要定义一个什么也不做的规则(没有命令行)。前面我们已经看到过这样的用法(参考 2.8 重载另外Makefile)。这样的规则,只有目标文件(可以存在依赖文件)而没有命令行。可以像这样定义:

 

target: ;

 

这就是一个空命令的规则,为目标“target”定义了一个空命令。也可以命令行的格式来定义空命令,需要注意的是命令行必须以[Tab]字符开始。一般在定义空命令时,建议不使用命令行的方式,因为看起来空命令行和空行在感觉上没有区别。

大家会奇怪为什么要定义一个没有命令的规则。其唯一的原因是,使用空命令行可以防止make在执行时图重建这个目标而查找隐含命令(包括了使用隐含规则中的命令和“.DEFAULT”指定的命令。关于隐含规则可参考 9 使用隐含规则)。这一点和伪目标有相同之处(可参考3.6 Makefile伪目标一节)。在使用空命令的目标时,需要说明的是:实现一个没有实际文件的目标,这个目标只是作为一个标签,来完成它的依赖文件的重建动作。实现这个目的,首先应该想到伪目标而不是空命令目标。因为一个实际不存在的目标文件的依赖文件,可能不会被正确的重建。

所以,对于空命令规则,最好不要给它指定依赖文件。避免特殊情况下产生错误的情况。定义一个空命令规则,建议使用上例的格式。

阅读(4388) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~