Chinaunix首页 | 论坛 | 博客
  • 博客访问: 103640759
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类: C/C++

2008-04-17 17:16:02

5在规则中使用命令
规则中的命令由一系列shell命令行组成,它们一条一条的按顺序执行。除第一条命令行可以分号为开始附属在目标-依赖行后面外,所有的命令行必须以TAB开始。空白行与注释行可在命令行中间出现,处理时它们被忽略。(但是必须注意,以TAB开始的‘空白行’不是空白行,它是空命令,参阅使用空命令。)

用户使用多种不同的shell程序,如果在makefile文件中没有指明其它的shell,则使用缺省的‘/bin/sh’解释makefile文件中的命令。参阅命令执行。

使用的shell种类决定了是否能够在命令行上写注释以及编写注释使用的语法。当使用‘/bin/sh’作为shell,以‘#’开始的注释一直延伸到该行结束。‘#’不必在行首,而且‘#’不是注释的一部分。

5.1 命令回显
正常情况下make在执行命令之前首先打印命令行,我们因这样可将您编写的命令原样输出故称此为回显。

以‘@’起始的行不能回显,‘@’在传输给shell时被丢弃。典型的情况,您可以在makefile文件中使用一个仅仅用于打印某些内容的命令,如echo命令来显示makefile文件执行的进程:

@echo About to make distribution files
当使用make时给出‘-n’或‘--just-print’标志,则仅仅回显命令而不执行命令。参阅选项概要。在这种情况下也只有在这种情况下,所有的命令行都回显,即使以‘@’开始的命令行也回显。这个标志对于在不使用命令的情况下发现make认为哪些是必要的命令非常有用。

‘-s’或‘--silent’标志可以使make阻止所有命令回显,好像所有的行都以‘@’开始一样。在makefile文件中使用不带依赖的特别目标‘.SILENT’的规则可以达到相同的效果(参阅内建的特殊目标名)。因为‘@’使用更加灵活以至于现在已基本不再使用特别目标.SILENT。

5.2执行命令
需要执行命令更新目标时,每一命令行都会使用一个独立的子shell环境,保证该命令行得到执行。(实际上,make可能走不影响结果的捷径。)

请注意:这意味着设置局部变量的shell命令如cd等将不影响紧跟着的命令行;如果您需要使用cd命令影响到下一个命令,请把这两个命令放到一行,它们中间用分号隔开,这样make将认为它们是一个单一的命令行,把它们放到一起传递给shell,然后按顺序执行它们。例如:

foo : bar/lose
        cd bar; gobble lose > ../foo
如果您喜欢将一个单一的命令分割成多个文本行,您必须用反斜杠作为每一行的结束,最后一行除外。这样,多个文本行通过删除反斜杠按顺序组成一新行,然后将它传递给shell。如此,下面的例子和前面的例子是等同的:

foo : bar/lose
        cd bar;  \
        gobble lose > ../foo
用作shell的程序是由变量SHELL指定,缺省情况下,使用程序‘/bin/sh’作为shell。

在MS_DOS上运行,如果变量SHELL没有指定,变量COMSPEC的值用来代替指定shell。

在MS_DOS上运行和在其它系统上运行,对于makefile文件中设置变量SHELL的行的处理也不一样。因为MS_DOS的shell,‘command.com’,功能十分有限,所以许多make用户倾向于安装一个代替的shell。因此,在MS_DOS上运行,make检测变量SHELL的值,并根据它指定的Unix风格或DOS风格的shell变化它的行为。即使使用变量SHELL指向‘command.com’ ,make依然检测变量SHELL的值。

如果变量SHELL指定Unix风格的shell,在MS_DOS上运行的make将附加检查指定的shell是否能真正找到;如果不能找到,则忽略指定的shell。在MS_DOS上,GNU make按照下述步骤搜寻shell:

1、在变量SHELL指定的目录中。例如,如果makefile指明`SHELL = /bin/sh',make将在当前路径下寻找子目录‘/bin’。

2、在当前路径下。

3、按顺序搜寻变量PATH指定的目录。

在所有搜寻的目录中,make首先寻找指定的文件(如例子中的‘sh’)。如果该文件没有存在,make将在上述目录中搜寻带有确定的可执行文件扩展的文件。例如:‘.exe', ‘.com', ‘.bat', ‘.btm', ‘.sh'文件和其它文件等。

如果上述过程中能够成功搜寻一个shell,则变量SHELL的值将设置为所发现shell的全路径文件名。然而如果上述努力全部失败,变量SHELL的值将不改变,设置shell的行的有效性将被忽略。这是在make运行的系统中如果确实安装了Unix风格的shell,make仅支持指明的Unix风格shell特点的原因。

注意这种对shell的扩展搜寻仅仅限制在makefile文件中设置变量SHELL的情况。如果在环境或命令行中设置,希望您指定shell的全路径文件名,而且全路径文件名需象在Unix系统中运行的一样准确无误。

经过上述的DOS特色的处理,而且您还把 ‘sh.exe’安装在变量PATH指定的目录中,或在makefile文件内部设置`SHELL = /bin/sh' (和多数Unix的makefile文件一样),则在MS_DOS上的运行效果和在Unix上运行完全一样。

不像其它大多数变量,变量SHELL从不根据环境设置。这是因为环境变量SHELL是用来指定您自己选择交互使用的shell程序。如果变量SHELL在环境中设置,它将影响makefile文件的功能,这是非常不划算的,参阅环境变量。然而在MS-DOS和MS-WINDOWS中在环境中设置变量SHELL的值是要使用的,因为在这些系统中,绝大多数用户并不设置该变量的值,所以make很可能特意为该变量指定要使用的值。在MS-DOS上,如果变量SHELL的设置对于make不合适,您可以设置变量MAKESHELL用来指定make使用的shell;这种设置将使变量SHELL的值失效。

5.3 并行执行
GNU make可以同时执行几条命令。正常情况下,make一次执行一个命令,待它完成后在执行下一条命令。然而,使用‘-j’和‘--jobs’选项将告诉make同时执行多条命令。在MS-DOS上,‘-j’选项没有作用,因为该系统不支持多进程处理。

如果‘-j’选项后面跟一个整数,该整数表示一次执行的命令的条数;这称为job slots数。如果‘-j’选项后面没有整数,也就是没有对job slots的数目限制。缺省的job slots数是一,这意味着按顺序执行(一次执行一条命令)。同时执行多条命令的一个不太理想的结果是每条命令产生的输出与每条命令发送的时间对应,即命令产生的消息回显可能较为混乱。

另一个问题是两个进程不能使用同一设备输入,所以必须确定一次只能有一条命令从终端输入,make只能保证正在运行的命令的标准输入流有效,其它的标准输入流将失效。这意味着如果有几个同时从标准输入设备输入的话,对于绝大多数子进程将产生致命的错误(即产生一个‘Broken pipe’信号)。

命令对一个有效的标准输入流(它从终端输入或您为make改造的标准输入设备输入)的需求是不可预测的。第一条运行的命令总是第一个得到标准输入流,在完成一条命令后第一条启动的另一条命令将得到下一个标准输入流,等等。

如果我们找到一个更好替换方案,我们将改变make的这种工作方式。在此期间,如果您使用并行处理的特点,您不应该使用任何需要标准输入的命令。如果您不使用该特点,任何需要标准输入的命令将都能正常工作。

最后,make的递归调用也导致出现问题。更详细的内容参阅与子make通讯的选项。

如果一个命令失败(被一个信号中止,或非零退出),且该条命令产生的错误不能忽略(参阅命令错误),剩余的构建同一目标的命令行将会停止工作。如果一条命令失败,而且‘-k’或‘--keep-going’选项也没有给出(参阅选项概要),make将放弃继续执行。如果make由于某种原因(包括信号)要中止,此时又子进程正在运行,它将等到这些子进程结束之后再实际退出。

当系统正满负荷运行时,您或许希望在负荷轻的时再添加任务。这时,您可以使用‘-|’选项告诉make根据平均负荷限制同一时刻运行的任务数量。‘-|’或‘--max-load’选项一般后跟一个浮点数。例如:

-| 2.5

将不允许make在平均负荷高于2.5时启动一项任务。‘-|’选项如果没有跟数据,则取消前面‘-|’给定的负荷限制。

更精确地讲,当make启动一项任务时,而它此时已经有至少一项任务正在运行,则它将检查当前的平均负荷;如果不低于‘-|’选项给定的负荷限制时,make将等待直到平均负荷低于限制或所有其它任务完成后再启动其它任务。

缺省情况下没有负荷限制。

5.4命令错误
在每一个shell命令返回后,make检查该命令退出的状态。如果该命令成功地完成,下一个命令行就会在新的子shell环境中执行,当最后一个命令行完成后,这条规则也宣告完成。如果出现错误(非零退出状态),make将放弃当前的规则,也许是所有的规则。

有时一个特定的命令失败并不是出现了问题。例如:您可能使用mkdir命令创建一个目录存在,如果该目录已经存在,mkdir将报告错误,但您此时也许要make继续执行。

要忽略一个命令执行产生的错误,请使用字符‘-’(在初始化TAB的后面)作为该命令行的开始。字符‘-’在命令传递给shell执行时丢弃。例如:

clean:
        -rm -f *.o
这条命令即使在不能删除一个文件时也强制rm继续执行。

在运行make时使用‘-i’或‘--ignore-errors’选项,将会忽略所有规则的命令运行产生的错误。在makefile文件中使用如果没有依赖的特殊目标.IGNORE规则,也具有同样的效果。但因为使用字符‘-’更灵活,所以该条规则已经很少使用。

一旦使用‘-’或‘-i’选项,运行命令时产生的错误被忽略,此时make象处理成功运行的命令一样处理具有返回错误的命令,唯一不同的地方是打印一条消息,告诉您命令退出时的编码状态,并说明该错误已经被忽略。如果发生错误而make并不说明其被忽略,则暗示当前的目标不能成功重新构造,并且和它直接相关或间接相关的目标同样不能重建。因为前一个过程没有完成,所以不会进一步执行别的命令。

在上述情况下,make一般立即放弃任务,返回一个非零的状态。然而,如果指定‘-k’或‘--keep-goning’选项,make则继续考虑这个目标的其它依赖,如果有必要在make放弃返回非零状态之前重建它们。例如,在编译一个OBJ文件发生错误后,即使make已经知道将所有OBJ文件连接在一起是不可能的,‘make -k'选项也继续编译其它OBJ文件。详细内容参阅选项概要。通常情况下,make的行为基于假设您的目的是更新指定的目标,一旦make得知这是不可能的,它将立即报告失败。‘-k’选项是告诉make真正的目的是测试程序中所有变化的可行性,或许是寻找几个独立的问题以便您可以在下次编译之前纠正它们。这是Emacs编译命令缺省情况下传递‘-k’选项的原因。

通常情况下,当一个命令运行失败时,如果它已经改变了目标文件,则该文件很可能发生混乱而不能使用或该文件至少没有完全得到更新。但是,文件的时间戳却表明该文件已经更新到最新,因此在make下次运行时,它将不再更新该文件。这种状况和命令被发出的信号强行关闭一样,参阅中断或关闭make。因此,如果在开始改变目标文件后命令出错,一般应该删除目标文件。如果.DELETE_ON_ERROR作为目标在makefile文件中出现,make将自动做这些事情。这是您应该明确要求make执行的动作,不是以前的惯例;特别考虑到兼容性问题时,您更应明确提出这样的要求。

5.5中断或关闭make
如果make在一条命令运行时得到一个致命的信号, 则make将根据第一次检查的时间戳和最后更改的时间戳是否发生变化决定它是否删除该命令要更新的目标文件。

删除目标文件的目的是当make下次运行时确保目标文件从原文件得到更新。为什么?假设正在编译文件时您键入Ctrl-c,而且这时已经开始写OBJ文件‘foo.o’,Ctrl-c关闭了该编译器,结果得到不完整的OBJ文件‘foo.o’的时间戳比源程序‘foo.c’的时间戳新,如果make收到Ctrl-c的信号而没有删除OBJ文件‘foo.o’,下次请求make更新OBJ文件‘foo.o’时,make将认为该文件已更新到最新而没有必要更新,结果在linker将OBJ文件连接为可执行文件时产生奇怪的错误信息。

您可以将目标文件作为特殊目标.PRECIOUS的依赖从而阻止make这样删除该目标文件。在重建一个目标之前,make首先检查该目标文件是否出现在特殊目标.PRECIOUS的依赖列表中,从而决定在信号发生时是否删除该目标文件。您不删除这种目标文件的原因可能是:目标更新是一种原子风格,或目标文件存在仅仅为了记录更改时间(其内容无关紧要),或目标文件必须一直存在,用来防止其它类型的错误等。

5.6递归调用make
递归调用意味着可以在makefile文件中将make作为一个命令使用。这种技术在包含大的系统中把makefile分离为各种各样的子系统时非常有用。例如,假设您有一个子目录‘subdir’,该目录中有它自己的makefile文件,您希望在该子目录中运行make时使用该makefile文件,则您可以按下述方式编写:

  subsystem:
         cd subdir && $(MAKE)
或, 等同于这样写 (参阅选项概要): 

subsystem:
        $(MAKE) -C subdir
您可以仅仅拷贝上述例子实现make的递归调用,但您应该了解它们是如何工作的,它们为什么这样工作,以及子make和上层make的相互关系。

为了使用方便,GNU make把变量CURDIR的值设置为当前工作的路径。如果‘-C’选项有效,它将包含的是新路径,而不是原来的路径。该值和它在makefile中设置的值有相同的优先权(缺省情况下,环境变量CURDIR不能重载)。注意,操作make时设置该值无效。

5.6.1 变量MAKE的工作方式
递归调用make的命令总是使用变量MAKE,而不是明确的命令名‘make’,如下所示:

subsystem:
         cd subdir && $(MAKE)
该变量的值是调用make的文件名。如果这个文件名是‘/bin/make’,则执行的命令是`cd subdir && /bin/make'。如果您在上层makefile文件时用特定版本的make,则执行递归调用时也使用相同的版本。

在命令行中使用变量MAKE可以改变‘-t' (‘--touch'), ‘-n' (‘--just-print'), 或 ‘-q' (‘--question')选项的效果。如果在使用变量MAKE的命令行首使用字符‘+’也会起到相同的作用。参阅代替执行命令。

设想一下在上述例子中命令‘make -t’的执行过程。(‘-t’选项标志目标已经更新,但却不执行任何命令,参阅代替执行命令。)按照通常的定义,命令‘make –t’在上例中仅仅创建名为‘subsystem’的文件而不进行别的工作。您实际要求运行‘cd subdir && make –t’干什么?是执行命令或是按照‘-t’的要求不执行命令?

Make的这个特点是这样的:只要命令行中包含变量MAKE,标志`-t', `-n' 和 `-q'将不对本行起作用。虽然存在标志不让命令执行,但包含变量MAKE的命令行却正常运行,make实际上是通过变量MAKEFLAGS将标志值传递给了子make(参阅与子make通讯的选项)。所以您的验证文件、打印命令的请求等都能传递给子系统。

5.6.2与子make通讯的变量
通过明确要求,上层make变量的值可以借助环境传递给子make,这些变量能在子make中缺省定义,在您不使用‘-e’开关的情况下,传递的变量的值不能代替子make使用的makefile文件中指定的值(参阅命令概要)。

向下传递、或输出一个变量时,make将该变量以及它的值添加到运行每一条命令的环境中。子make,作为响应,使用该环境初始化它的变量值表。参阅环境变量。

除了明确指定外,make仅向下输出在环境中定义并初始化的或在命令行中设置的变量,而且这些变量的变量名必须仅由字母、数字和下划线组成。一些shell不能处理名字中含有字母、数字和下划线以外字符的环境变量。特殊变量如SHELL和MAKEFLAGS一般总要向下输出(除非您不输出它们)。即使您把变量MAKEFILE设为其它的值,它也向下输出。

Make自动传递在命令行中定义的变量的值,其方法是将它们放入MAKEFLAGS变量中。详细内容参阅下节。Make缺省创造的变量的值不能向下传递,子make可以自己定义它们。如果您要将指定变量输出给子make,请用export指令,格式如下:

export variable ...
您要将阻止一些变量输出给子make,请用unexport指令,格式如下:

unexport variable ...
为方便起见,您可以同时定义并输出一个变量:

export variable = value
下面的格式具有相同的效果: 

variable = value
export variable
以及 

export variable := value
具有相同的效果:
variable := value
export variable
同样, 

export variable += value
亦同样: 

variable += value
export variable
参阅为变量值追加文本。

您可能注意到export和unexport指令在make与shell中的工作方式相同,如sh。

如果您要将所有的变量都输出,您可以单独使用export:

export

这告诉make 把export和unexport没有提及的变量统统输出,但任何在unexport提及的变量仍然不能输出。如果您单独使用export作为缺省的输出变量方式,名字中含有字母、数字和下划线以外字符的变量将不能输出,这些变量除非您明确使用export指令提及才能输出。

单独使用export的行为是老板本GNU make缺省定义的行为。如果您的makefile依靠这些行为,而且您希望和老板本GNU make兼容,您可以为特殊目标.EXPORT_ALL_VARIABLES 编写一条规则代替export指令,它将被老板本GNU make忽略,但如果同时使用export指令则报错。

同样,您可以单独使用unexport告诉make缺省不要输出变量,因为这是缺省的行为,只有前面单独使用了export(也许在一个包括的makefile中)您才有必要这样做。您不能同时单独使用export和unexport指令实现对某些命令输出对其它的命令不输出。最后面的一条指令(export或unexport)将决定make的全部运行结果。

作为一个特点,变量MAKELEVEL的值在从一个层次向下层传递时发生变化。该变量的值是字符型,它用十进制数表示层的深度。‘0’代表顶层make,‘1’代表子make,‘2’代表子-子-make,以此类推。Make为一个命令建立一次环境,该值增加1。

该变量的主要作用是在一个条件指令中测试(参阅makefile文件的条件语句);采用这种方法,您可以编写一个makefile,如果递归调用采用一种运行方式,由您控制直接执行采用另一种运行方式。

您可以使用变量MAKEFILES使所有的子make使用附加的makefile文件。变量MAKEFILES的值是makefile文件名的列表,文件名之间用空格隔开。在外层makefile中定义该变量,该变量的值将通过环境向下传递;因此它可以作为子make的额外的makefile文件,在子make读正常的或指定的makefile文件前,将它们读入。参阅变量MAKEFILES。

5.6.3与子make通讯的选项
诸如‘-s’和‘-k’标志通过变量MAKEFLAGS自动传递给子make。该变量由make自动建立,并包含make收到的标志字母。所以,如果您是用‘make –ks’变量MAKEFLAGS就得到值‘ks’。

作为结果,任一个子make都在它的运行环境中为变量MAKEFLAGS赋值;作为响应,make使用该值作为标志并进行处理,就像它们作为参数被给出一样。参阅选项概要。

同样,在命令行中定义的变量也将借助变量MAKEFLAGS传递给子make。变量MAKEFLAGS值中的字可以包含‘=’,make将它们按变量定义处理,其过程和在命令行中定义的变量一样。参阅变量重载。

选项`-C', `-f', `-o', 和 ‘-W’不能放入变量MAKEFLAGS中;这些选项不能向下传递。

‘-j’选项是一个特殊的例子(参阅并行执行)。如果您将它设置为一些数值‘N’,而且您的操作系统支持它(大多数Unix系统支持,其它操作系统不支持),父make和所有子make通讯保证在它们中间同时仅有‘N’个任务运行。注意,任何包含递归调用的任务(参阅代替执行命令)不能计算在总任务数内(否则,我们仅能得到‘N’个子make运行,而没有多余的时间片运行实在的工作)。

如果您的操作系统不支持上述通讯机制,那么‘-j 1’将放到变量MAKEFLAGS中代替您指定的值。这是因为如果‘-j’选项传递给子make,您可能得到比您要求多很多的并行运行的任务数。如果您给出‘-j’选项而没有数字参数,意味着尽可能并行处理多个任务,这样向下传递,因为倍数的无限制性所以至多为1。

如果您不希望其它的标志向下传递,您必须改变变量MAKEFLAGS的值,其改变方式如下:

subsystem:
        cd subdir && $(MAKE) MAKEFLAGS=
该命令行中定义变量的实际上出现在变量MAKEOVERRIDES中,而且变量MAKEFLAGS包含了该变量的引用值。如果您要向下传递标志,而不向下传递命令行中定义的变量,这时,您可以将变量MAKEOVERRIDES的值设为空,格式如下:

MAKEOVERRIDES =
这并不十分有用。但是,一些系统对环境的大小有固定限制,而且该值较小,将这么多的信息放到变量MAKEFLAGS的值中可能超过该限制。如果您看到‘Arg list too long'的错误信息,很可能就是由于该问题造成的。(按照严格的POSIX.2的规定,如果在makefile文件定义特殊目标‘.POSIX’,改变变量MAKEOVERRIDES的值并不影响变量MAKEFLAGS。也许您并不关心这些。)

为了和早期版本兼容,具有相同功能的变量MFLAGS也是存在的。除了它不能包含命令行定义变量外,它和变量MAKEFLAGS有相同的值,而且除非它是空值,它的值总是以短线开始(MAKEFLAGS只有在和多字符选项一起使用时才以短线开始,如和‘--warn-undefined-variables’连用)。变量MFLAGS传统的使用在明确的递归调用make的命令中,例如:

subsystem:
        cd subdir && $(MAKE) $(MFLAGS)
但现在,变量MAKEFLAGS使这种用法变得多余。如果您要您的makefile文件和老版本的make程序兼容,请使用这种方式;这种方式在现代版本make中也能很好的工作。

如果您要使用每次运行make都要设置的特定选项,例如‘-k’选项(参阅选项概要),变量MAKEFLAGS十分有用。您可以简单的在环境中将给变量MAKEFLAGS赋值,或在makefile文件中设置变量MAKEFLAGS,指定的附加标志可以对整个makefile文件都起作用。(注意:您不能以这种方式使用变量MFLAGS,变量MFLAGS存在仅为和早期版本兼容,采用其它方式设置该变量make将不予解释。)

当make解释变量MAKEFLAGS值的时候(不管在环境中定义或在makefile文件中定义),如果该值不以短线开始,则make首先为该值假设一个短线;接着将该值分割成字,字与字间用空格隔开,然后将这些字进行语法分析,好像它们是在命令行中给出的选项一样。(‘-C', ‘-f',‘-h',‘-o',‘-W'选项以及它们的长名字版本都将忽略,对于无效的选项不产生错误信息。)

如果您在环境中定义变量MAKEFLAGS,您不要使用严重影响make运行,破坏makefile文件的意图以及make自身的选项。例如‘-t', ‘-n', 和‘-q'选项,如果将它们中的一个放到变量MAKEFLAGS的值中,可能产生灾难性的后果,或至少产生让人讨厌的结果。

5.6.4 ‘--print-directory’选项
如果您使用几层make递归调用,使用‘-w’或‘--print-directory’选项,通过显示每个make开始处理以及处理完成的目录使您得到比较容易理解的输出。例如,如果使用‘make –w’命令在目录‘/u/gnu/make’中运行make,则make将下面格式输出信息:

make: Entering directory `/u/gnu/make'.
说明进入目录中,还没有进行任何任务。下面的信息:

make: Leaving directory `/u/gnu/make'.
说明任务已经完成。

通常情况下,您不必具体指明这个选项,因为make已经为您做了:当您使用‘-C’选项时,‘-w’选项已经自动打开,在子make中也是如此。如果您使用‘-s’选项,‘-w’选项不会自动打开,因为‘-s’选项是不打印信息,同样使用`--no-print-directory'选项‘-w’选项也不会自动打开。

5.7定义固定次序命令
在创建各种目标时,相同次序的命令十分有用时,您可以使用define指令定义固定次序的命令,并根据这些目标的规则引用固定次序。固定次序实际是一个变量,因此它的名字不能和其它的变量名冲突。

下面是定义固定次序命令的例子:

define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef
run-yacc是定义的变量的名字;endef标志定义结束;中间的行是命令。define指令在固定次序中不对变量引用和函数调用扩展;字符‘$’、圆括号、变量名等等都变成您定义的变量的值的一部分。定义多行变量一节对指令define有详细解释。

在该例子中,对于任何使用该固定次序的规则,第一个命令是对其第一个依赖运行Yacc命令,Yacc命令执行产生的输出文件一律命名为‘y.tab.c’;第二条命令,是将该输出文件的内容移入规则的目标文件中。

在使用固定次序时,规则中命令使用的变量应被确定的值替代,您可以象替代其它变量一样替代这些变量(详细内容参阅变量引用基础)。因为由define指令定义的变量是递归扩展的变量,所以在使用时所有变量引用才扩展。例如:

foo.c : foo.y
        $(run-yacc)
当固定次序‘run-yacc’运行时,‘foo.y’将代替变量‘$^’,‘foo.c’将代替变量‘$@’。

这是一个现实的例子,但并不是必要的,因为make有一条隐含规则可以根据涉及的文件名的类型确定所用的命令。参阅使用隐含规则。

在命令执行时,固定次序中的每一行被处理为和直接出现在规则中的命令行一样,前面加上一个Tab,make也特别为每一行请求一个独立的子shell。您也可以在固定次序的每一行上使用影响命令行的前缀字符(`@', `-',和 `+'),参阅在规则中使用命令。例如使用下述的固定次序:

@echo "frobnicating target $@"
frob-step-1 $< -o $@-step-1
frob-step-2 $@-step-1 -o $@
endef
make将不回显第一行,但要回显后面的两个命令行。

另一方面,如果前缀字符在引用固定次序的命令行中使用,则该前缀字符将应用到固定次序的每以行中。例如这个规则:

frob.out: frob.in
        @$(frobnicate)
将不回显固定次序的任何命令。具体内容参阅命令回显。

5.8 使用空命令
定义什么也不干的命令有时很有用,定义空命令可以简单的给出一个仅仅含有空格而不含其它任何东西的命令即可。例如:

target: 
为字符串‘target’定义了一个空命令。您也可以使用以Tab字符开始的命令行定义一个空命令,但这由于看起来空白容易造成混乱。

也许您感到奇怪,为什么我们定义一个空命令?唯一的原因是为了阻止目标更新时使用隐含规则提供的命令。(参阅使用隐含规则以及定义最新类型的缺省规则)

也许您喜爱为实际不存在的目标文件定义空命令,因为这样它的依赖可以重建。然而这样做并不是一个好方法,因为如果目标文件实际存在,则依赖有可能不重建,使用假想目标是较好的选择,参阅假想目标。
阅读(238) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~