分享

Makefile中的命令和变量

 流浪的蜉蝣 2014-09-21

5 在规则中使用命令

5.1命令回显

5.2执行命令

5.3并行执行

5.4命令错误

5.5中断或关闭make

5.6递归调用make

5.6.1变量MAKE的工作方式

5.6.2与子make通讯的变量

5.6.3与子make通讯的选项

5.6.4`--print-directory'选项

5.7定义固定次序命令

5.8使用空命令

6         使用变量

6.1变量引用基础

6.2变量的两个特色

6.3变量高级引用技术

6.3.1替换引用

6.3.2嵌套变量引用

6.4变量取值

6.5设置变量

6.6为变量值追加文本

6.7override指令

6.8定义多行变量

6.9环境变量

6.10特定目标变量的值

6.11特定格式变量的值

 

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字符开始的命令行定义一个空命令,但这由于看起来空白容易造成混乱。

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

也许您喜爱为实际不存在的目标文件定义空命令,因为这样它的依赖可以重建。然而这样做并不是一个好方法,因为如果目标文件实际存在,则依赖有可能不重建,使用假想目标是较好的选择,参阅假想目标

6 使用变量

变量是在makefile中定义的名字,其用来代替一个文本字符串,该文本字符串称为该变量的值。在具体要求下,这些值可以代替目标、依赖、命令以及makefile文件中其它部分。(在其它版本的make中,变量称为宏(macros)。)

在makefile文件读入时,除规则中的shell命令、使用‘=’定义的‘=’右边的变量、以及使用define指令定义的变量体此时不扩展外,makefile文件其它各个部分的变量和函数都将扩展。

变量可以代替文件列表、传递给编译器的选项、要执行的程序、查找源文件的目录、输出写入的目录,或您可以想象的任何文本。

变量名是不包括‘:',‘#',‘='、前导或结尾空格的任何字符串。然而变量名包含字母、数字以及下划线以外的其它字符的情况应尽量避免,因为它们可能在将来被赋予特别的含义,而且对于一些shell它们也不能通过环境传递给子make(参阅与子make通讯的变量)。变量名是大小写敏感的,例如变量名‘foo', ‘FOO', 和 ‘Foo'代表不同的变量。

使用大写字母作为变量名是以前的习惯,但我们推荐在makefile内部使用小写字母作为变量名,预留大写字母作为控制隐含规则参数或用户重载命令选项参数的变量名。参阅变量重载

一少部分的变量使用一个标点符号或几个字符作为变量名,这些变量是自动变量,它们又特定的用途。参阅自动变量

6.1 变量引用基础

写一个美元符号后跟用圆括号或大括号括住变量名则可引用变量的值:‘$(foo)'‘${foo}'都是对变量‘foo’的有效引用。‘$’的这种特殊作用是您在命令或文件名中必须写‘$$’才有单个‘$’的效果的原因。

变量的引用可以用在上下文的任何地方:目标、依赖、命令、绝大多数指令以及新变量的值等等。这里有一个常见的例子,在程序中,变量保存着所有OBJ文件的文件名:

objects = program.o foo.o utils.o

program : $(objects)

        cc -o program $(objects)

 

$(objects) : defs.h

变量的引用按照严格的文本替换进行,这样该规则

foo = c

prog.o : prog.$(foo)

        $(foo)$(foo) -$(foo) prog.$(foo)

可以用于编译C语言源程序‘prog.c’。因为在变量分配时,变量值前面的空格被忽略,所以变量foo的值是‘C’。(不要在您的makefile文件这样写!)

美元符号后面跟一个字符但不是美元符号、圆括号、大括号,则该字符将被处理为单字符的变量名。因此可以使用‘$x’引用变量x。然而,这除了在使用自动变量的情况下,在其它实际工作中应该完全避免。参阅自动变量

6.2 变量的两个特色

在GNU make中可以使用两种方式为变量赋值,我们将这两种方式称为变量的两个特色(two flavors)。两个特色的区别在于它们的定义方式和扩展时的方式不同。

变量的第一个特色是递归调用扩展型变量。这种类型的变量定义方式:在命令行中使用‘=’定义(参阅设置变量)或使用define指令定义(参阅定义多行变量)。变量替换对于您所指定的值是逐字进行替换的;如果它包含对其它变量的引用,这些引用在该变量替换时(或在扩展为其它字符串的过程中)才被扩展。这种扩展方式称为递归调用型扩展。例如:

foo = $(bar)

bar = $(ugh)

ugh = Huh?

 

all:;echo $(foo)

将回显‘Huh?':‘$(foo)’扩展为‘$(bar)’,进一步扩展为‘$(ugh)’,最终扩展为‘Huh?’。

这种特色的变量是其它版本make支持的变量类型,有缺点也有优点。大多数人认为的该类型的变量的优点是:

CFLAGS = $(include_dirs) -O

include_dirs = -Ifoo -Ibar

即能够完成希望它完成的任务:当‘CFLAGS’在命令中扩展时,它将最终扩展为‘-Ifoo -Ibar’。其最大的缺点是不能在变量后追加内容,如在:

CFLAGS = $(CFLAGS) -O

在变量扩展过程中可能导致无穷循环(实际上make侦测到无穷循环就会产生错误信息)。

它的另一个缺点是在定义中引用的任何函数时(参阅文本转换函数)变量一旦展开函数就会立即执行。这可导致make运行变慢,性能变坏;并且导致通配符与shell函数(因不能控制何时调用或调用多少次)产生不可预测的结果。

为避免该问题和递归调用扩展型变量的不方便性,出现了另一个特色变量:简单扩展型变量。

简单扩展型变量在命令行中用‘:=’定义(参阅设置变量)。简单扩展型变量的值是一次扫描永远使用,对于引用的其它变量和函数在定义的时候就已经展开。简单扩展型变量的值实际就是您写的文本扩展的结果。因此它不包含任何对其它变量的引用;在该变量定义时就包含了它们的值。所以:

x := foo

y := $(x) bar

x := later

等同于:

y := foo bar

x := later

引用一个简单扩展型变量时,它的值也是逐字替换的。这里有一个稍复杂的例子,说明了‘:=’和shell函数连接用法(参阅函数shell)。该例子也表明了变量MAKELEVEL的用法,该变量在层与层之间传递时值发生变化。(参阅与子make通讯的变量,可获得变量MAKELEVEL关于的信息。)

ifeq (0,${MAKELEVEL})

cur-dir   := $(shell pwd)

whoami    := $(shell whoami)

host-type := $(shell arch)

MAKE := ${MAKE} host-type=${host-type} whoami=${whoami}

endif

按照这种方法使用‘:=’的优点是看起来象下述的典型的‘下降到目录’的命令:

${subdirs}:

      ${MAKE} cur-dir=${cur-dir}/$@ -C $@ all

简单扩展型变量因为在绝大多数程序设计语言中可以象变量一样工作,因此它能够使复杂的makefile程序更具有预测性。它们允许您使用它自己的值重新定义(或它的值可以被一个扩展函数以某些方式处理),它们还允许您使用更有效的扩展函数(参阅文本转换函数)。

您可以使用简单扩展型变量将控制的前导空格引入到变量的值中。前导空格字符一般在变量引用和函数调用时被丢弃。简单扩展型变量的这个特点意味着您可以在一个变量的值中包含前导空格,并在变量引用时保护它们。象这样:

nullstring :=

space := $(nullstring) # end of the line

这里变量space的值就是一个空格,注释‘# end of the line’包括在这里为了让人更易理解。因为尾部的空格不能从变量值中分离出去,仅在结尾留一个空格也有同样的效果(但是此时相当难读),如果您在变量值后留一个空格,象这样在行的结尾写上注释清楚表明您的打算是很不错的主意。相反,如果您在变量值后不要空格,您千万记住不要在行的后面留下几个空格再随意放入注释。例如:

dir := /foo/bar    # directory to put the frobs in

这里变量dir的值是‘/foo/bar ’(四个尾部空格),这不是预期的结果。(假设‘/foo/bar’是预期的值)。

另一个给变量赋值的操作符是‘?=’,它称为条件变量赋值操作符,因为它仅仅在变量还没有定义的情况下有效。这声明:

FOO ?= bar

和下面的语句严格等同(参阅函数origin

ifeq ($(origin FOO), undefined)

  FOO = bar

endif

注意,一个变量即使是空值,它仍然已被定义,所以使用‘?=’定义无效。

6.3变量引用高级技术

本节内容介绍变量引用的高级技术。

6.3.1替换引用

替换引用是用您指定的变量替换一个变量的值。它的形式‘$(var:a=b)’(或‘${var:a=b}’),它的含义是把变量var的值中的每一个字结尾的a用b替换。

我们说‘在一个字的结尾’,我们的意思是a一定在一个字的结尾出现,且a的后面要么是空格要么是该变量值的结束,这时的a被替换,值中其它地方的a不被替换。例如:

foo := a.o b.o c.o

bar := $(foo:.o=.c)

将变量‘bar’的值设为‘a.c b.c c.c’。参阅变量设置

替换引用实际是使用扩展函数patsubst的简写形式(参阅字符串替换和分析函数)。我们提供替换引用也是使扩展函数patsubst与make的其它实现手段兼容的措施。

另一种替换引用是使用强大的扩展函数patsubst。它的形式和上述的‘$(var:a=b)’一样,不同在于它必须包含单个‘%’字符,其实这种形式等同于‘$(patsubst a,b,$(var))’。有关于函数patsubst扩展的描述参阅字符串替换和分析函数。例如:

foo := a.o b.o c.o

bar := $(foo:%.o=%.c)

社值变量‘bar'的值为‘a.c b.c c.c'

6.3.2嵌套变量引用(计算的变量名)

嵌套变量引用(计算的变量名)是一个复杂的概念,仅仅在十分复杂的makefile程序中使用。绝大多数情况您不必考虑它们,仅仅知道创建名字中含有美元标志的变量可能有奇特的结果就足够了。然而,如果您是要把一切搞明白的人或您实在对它们如何工作有兴趣,请认真阅读以下内容。

变量可以在它的名字中引用其它变量,这称为嵌套变量引用(计算的变量名)。例如:

x = y

y = z

a := $($(x))

定义阿a为‘z’:‘$(x)’在‘$($(x))’中扩展为‘y’,因此‘$($(x))’扩展为‘$(y)’,最终扩展为‘z’。这里对引用的变量名的陈述不太明确;它根据‘$(x)’的扩展进行计算,所以引用‘$(x)’是嵌套在外层变量引用中的。

前一个例子表明了两层嵌套,但是任何层次数目的嵌套都是允许的,例如,这里有一个三层嵌套的例子:

x = y

y = z

z = u

a := $($($(x)))

这里最里面的‘$(x)’ 扩展为‘y’,因此‘$($(x))’扩展为‘$(y)’,‘$(y)’ 扩展为‘z’,最终扩展为‘u’。

在一个变量名中引用递归调用扩展型变量,则按通常的风格再扩展。例如:

x = $(y)

y = z

z = Hello

a := $($(x))

定义的a是‘Hello’:‘$($(x))’扩展为‘$($(y))’,‘$($(y))’变为‘$(z)’, $(z)’最终扩展为‘Hello’。

嵌套变量引用和其它引用一样也可以包含修改引用和函数调用(参阅文本转换函数)。例如,使用函数subst(参阅字符串替换和分析函数):

x = variable1

variable2 := Hello

y = $(subst 1,2,$(x))

z = y

a := $($($(z)))

定义的a是‘Hello’。任何人也不会写象这样令人费解的嵌套引用程序,但它确实可以工作:‘$($($(z)))’ 扩展为‘$($(y))’,‘$($(y))’变为‘$(subst 1,2,$(x))’。它从变量‘x’得到值‘variable1’,变换替换为‘variable2’,所以整个字符串变为‘$( variable2)’,一个简单的变量引用,它的值为‘Hello’。

嵌套变量引用不都是简单的变量引用,它可以包含好几个变量引用,同样也可包含一些固定文本。例如,

a_dirs := dira dirb

1_dirs := dir1 dir2

 

a_files := filea fileb

1_files := file1 file2

 

ifeq "$(use_a)" "yes"

a1 := a

else

a1 := 1

endif

 

ifeq "$(use_dirs)" "yes"

df := dirs

else

df := files

endif

 

dirs := $($(a1)_$(df))

根据设置的use_a和use_dirs的输入可以将dirs这个相同的值分别赋给

a_dirs

,

1_dirs

,

a_files

1_files。

嵌套变量引用也可以用于替换引用:

a_objects := a.o b.o c.o

1_objects := 1.o 2.o 3.o

 

sources := $($(a1)_objects:.o=.c)

根据a1的值,定义的sources可以是`a.c b.c c.c'`1.c 2.c 3.c'。

使用嵌套变量引用唯一的限制是它们不能只部分指定要调用的函数名,这是因为用于识别函数名的测试在嵌套变量引用扩展之前完成。例如:

ifdef do_sort

func := sort

else

func := strip

endif

 

bar := a d b g q c

 

foo := $($(func) $(bar))

则给变量‘foo’的值赋为‘sort a d b g q c'‘strip a d b g q c',而不是将‘a d b g q c’作为函数sort或strip的参数。如果在将来去掉这种限制是一个不错的主意。

您也可以变量赋值的左边使用嵌套变量引用,或在define指令中。如:

dir = foo

$(dir)_sources := $(wildcard $(dir)/*.c)

define $(dir)_print

lpr $($(dir)_sources)

endef

该例子定义了变量‘dir',‘foo_sources', 和‘foo_print'

注意:虽然嵌套变量引用和递归调用扩展型变量都是用在复杂的makefile文件中,但二者不同(参阅变量的两个特色)。

6.4变量取值

变量有以下几种方式取得它们的值:

l         您可以在运行make时为变量指定一个重载值。参阅变量重载

l         您可以在makefile文件中指定值,即变量赋值(参阅设置变量)或使用逐字定义变量(参阅定义多行变量)。

l         把环境变量变为make的变量。参阅环境变量

l         自动变量可根据规则提供值,它们都有简单的习惯用法,参阅自动变量

l         变量可以用常量初始化。参阅隐含规则使用的变量

6.5设置变量

在makefile文件中设置变量,编写以变量名开始后跟‘=’或‘:=’的一行即可。任何跟在‘=’或‘:=’后面的内容就变为变量的值。例如:

objects = main.o foo.o bar.o utils.o

定义一个名为objects的变量,变量名前后的空格和紧跟‘=’的空格将被忽略。

使用‘=’定义的变量是递归调用扩展型变量;以‘:=’定义的变量是简单扩展型变量。简单扩展型变量定义可以包含变量引用,而且变量引用在定义的同时就被立即扩展。参阅变量的两种特色

变量名中也可以包含变量引用和函数调用,它们在该行读入时扩展,这样可以计算出能够实际使用的变量名。

变量值的长度没有限制,但受限于计算机中的实际交换空间。当定义一个长变量时,在合适的地方插入反斜杠,把变量值分为多个文本行是不错的选择。这不影响make的功能,但可使makefile文件更加易读。

绝大多数变量如果您不为它设置值,空字符串将自动作为它的初值。虽然一些变量有内建的非空的初始化值,但您可随时按照通常的方式为它们赋值(参阅隐含规则使用的变量。)另外一些变量可根据规则自动设定新值,它们被称为自动变量。参阅自动变量

如果您喜欢仅对没有定义过的变量赋给值,您可以使用速记符‘?=’代替‘=’。下面两种设置变量的方式完全等同(参阅函数origin):

FOO ?= bar

ifeq ($(origin FOO), undefined)

FOO = bar

endif

6.6 为变量值追加文本

为已经定以过的变量的值追加更多的文本一般比较有用。您可以在独立行中使用‘+=’来实现上述设想。如:

objects += another.o

这为变量objects的值添加了文本‘another.o’(其前面有一个前导空格)。这样:

objects = main.o foo.o bar.o utils.o

objects += another.o

变量

objects

设置为‘main.o foo.o bar.o utils.o another.o'

使用 `+=' 相同于:

objects = main.o foo.o bar.o utils.o

objects := $(objects) another.o

对于使用复杂的变量值,不同方法的差别非常重要。如变量在以前没有定义过,则‘+=’的作用和‘=’相同:它定义一个递归调用型变量。然而如果在以前有定义,‘+=’的作用依赖于您原始定义的变量的特色,详细内容参阅变量的两种特色

当您使用‘+=’为变量值附加文本时,make的作用就好象您在初始定义变量时就包含了您要追加的文本。如果开始您使用‘:=’定义一个简单扩展型变量,再用‘+=’对该简单扩展型变量值追加文本,则该变量按新的文本值扩展,好像在原始定义时就将追加文本定义上一样,详细内容参阅设置变量。实际上,

variable := value

variable += more

等同于:

variable := value

variable := $(variable) more

另一方面,当您把‘+=’和首次使用无符号‘=’定义的递归调用型变量一起使用时,make的运行方式会有所差异。在您引用递归调用型变量时,make并不立即在变量引用和函数调用时扩展您设定的值;而是将它逐字储存起来,将变量引用和函数调用也储存起来,以备以后扩展。当您对于一个递归调用型变量使用‘+=’时,相当于对一个不扩展的文本追加新文本。

variable = value

variable += more

粗略等同于:

temp = value

variable = $(temp) more

当然,您从没有定义过叫做temp的变量,如您在原始定义变量时,变量值中就包含变量引用,此时可以更为深刻地体现使用不同方式定义的的重要性。拿下面常见的例子,

CFLAGS = $(includes) -O

...

CFLAGS += -pg # enable profiling

第一行定义了变量CFLAGS,而且变量CFLAGS引用了其它变量,includes。(变量CFLAGS用于C编译器的规则,参阅隐含规则目录。)由于定义时使用‘=’,所以变量CFLAGS是递归调用型变量,意味着‘$(includes) -O’在make处理变量CFLAGS定义时是不扩展的;也就是变量includes在生效之前不必定义,它仅需要在任何引用变量CFLAGS之前定义即可。如果我们试图不使用‘+=’为变量CFLAGS追加文本,我们可能按下述方式:

CFLAGS := $(CFLAGS) -pg # enable profiling

这似乎很好,但结果绝不是我们所希望的。使用‘:=’重新定义变量CFLAGS为简单扩展型变量,意味着make在设置变量CFLAGS之前扩展了‘$(CFLAGS) -pg’。如果变量includes此时没有定义,我们将得到‘-0 -pg’,并且以后对变量includes的定义也不会有效。相反,使用‘+=’ 设置变量CFLAGS我们得到没有扩展的‘$(CFLAGS) –0 -pg’,这样保留了对变量includes的引用,在后面一个地方如果变量includes得到定义,‘$(CFLAGS)’仍然可以使用它的值。

6.7 override指令

如果一个变量设置时使用了命令参数(参阅变量重载),那么在makefile文件中通常的对该变量赋值不会生效。此时对该变量进行设置,您需要使用override指令,其格式如下:

override variable = value

override variable := value

为该变量追加更多的文本,使用:

override variable += more text

参阅为变量值追加文本

override指令不是打算扩大makefile和命令参数冲突,而是希望用它您可以改变和追加哪些设置时使用了命令参数的变量的值。

例如,假设您在运行C编译器时总是使用‘-g’开关,但您允许用户像往常一样使用命令参数指定其它开关,您就可以使用override指令:

override CFLAGS += -g

您也可以在define指令中使用override指令,下面的例子也许就是您想要得:

override define foo

bar

endef

关于define指令的信息参阅下节。

6.8定义多行变量

设置变量值的另一种方法时使用define指令。这个指令有一个特殊的用法,既可以定义包含多行字符的变量。这使得定义命令的固定次序十分方便(参阅定义固定次序命令)。

在define指令同一行的后面一般是变量名,当然,也可以什么也没有。变量的值由下面的几行给出,值的结束由仅仅包含endef的一行标示出。除了上述在语法上的不同之外,define指令象‘=’一样工作:它创建了一个递归调用型变量(参阅变量的两个特色)。变量的名字可以包括函数调用和变量引用,它们在指令读入时扩展,以便能够计算出实际的变量名。

define two-lines

echo foo

echo $(bar)

endef

变量的值在通常的赋值语句中只能在一行中完成,但在define指令中在define指令行以后endef行之前中间所有的行都是变量值的一部分(最后一行除外,因为标示endef那一行不能认为是变量值的一部分)。前面的例子功能上等同于:

two-lines = echo foo; echo $(bar)

因为两命令之间用分号隔开,其行为很接近于两个分离的shell命令。然而,注意使用两个分离的行,意味着make请求shell两次,每一行都在独立的子shell中运行。参阅执行命令

如果您希望使用define指令的变量定义比使用命令行定义的变量优先,您可以把define指令和override指令一块使用:

override define two-lines

foo

$(bar)

endef

参阅override指令

6.9 环境变量

make使用的变量可以来自make的运行环境。任何make能够看见的环境变量,在make开始运行时都转变为同名同值的make变量。但是,在makefile文件中对变量的具体赋值,或使用带有参数的命令,都可以对环境变量进行重载(如果明确使用‘-e’标志,环境变量的值可以对makefile文件中的赋值进行重载,参阅选项概要,但是这在实际中不推荐使用。)

这样,通过在环境中设置变量CFLAGS,您可以实现在绝大多数makefile文件中的所有C源程序的编译使用您选择的开关。因为您知道没有makefile将该变量用于其它任务,所以这种使用标准简洁含义的变量是安全的(但这也是不可靠的,一些makefile文件可能设置变量CFLAGS,从而使环境中变量CFLAGS的值失效)。当使用递归调用的make时,在外层make环境中定义的变量,可以传递给内层的make(参阅递归调用make)。缺省方式下,只有环境变量或在命令行中定义的变量才能传递给内层make。您可以使用export指令传递其它变量,参阅与子make通讯的变量

环境变量的其它使用方式都不推荐使用。将makefile的运行完全依靠环境变量的设置、超出makefile文件的控制范围,这种做法是不明智的,因为不同的用户运行同一个makefile文件有可能得出不同的结果。这和大部分makefile文件的意图相违背。

变量SHELL在环境中存在,用来指定用户对交互的shell的选择,因此使用变量SHELL也存字类似的问题。这种根据选定值影响make运行的方式是很不受欢迎的。所以,make将忽略环境中变量SHELL的值(在MS-DOS 和 MS-Windows中运行例外,但此时变量SHELL通常不设置值,参阅执行命令)。

6.10 特定目标变量的值

make中变量的值一般是全局性的;既,无论它们在任何地方使用,它们的值是一样的(当然,您重新设置除外);自动变量是一个例外(参阅自动变量)。

另一个例外是特定目标变量的值,这个特点允许您可以根据make建造目标的变化改变变量的定义。象自动变量一样,这些值只能在一个目标的命令脚本的上下文起作用。

可以象这样设置特定目标变量的值:

target ... : variable-assignment

或这样:

target ... : override variable-assignment

target ...’中可含有多个目标,如此,则设置的特定目标变量的值可在目标列表中的任一个目标中使用。‘variable-assignment’使用任何赋值方式都是有效的:递归调用型(‘=’)、静态(‘:=’)、追加(‘+=’)或条件(‘?=’)。所有出现在‘variable-assignment’中的变量能够在特定目标target ...的上下文中使用:也就是任何以前为特定目标target ...定义的特定目标变量的值在这些特定目标中都是有效的。注意这种变量值和全局变量值相比是局部的值:这两种类型的变量不必有相同的类型(递归调用vs.静态)。

特定目标变量的值和其它makefile变量具有相同的优先权。一般在命令行中定义的变量(和强制使用‘-e’情况下的环境变量)的值占据优先的地位,而使用override指令定义的特定目标变量的值则占据优先地位。

特定目标变量的值有另外一个特点:当您定义一个特定目标变量时,该变量的值对特定目标target ...的所有依赖有效,除非这些依赖用它们自己的特定目标变量的值将该变量重载。例如:

prog : CFLAGS = -g

prog : prog.o foo.o bar.o

将在目标prog的命令脚本中设置变量CFLAGS的值为‘-g’,同时在创建`prog.o', `foo.o', 和 `bar.o'的命令脚本中变量CFLAGS的值也是‘-g’,以及prog.o',‘foo.o', 和‘bar.o'的依赖的创建命令脚本中变量CFLAGS的值也是‘-g’。

6.11 特定格式变量的值

除了特定目标变量的值(参阅上小节)外,GNU make也支持特定格式变量的值。使用特定格式变量的值,可以为匹配指定格式的目标定义变量。在为目标定义特定目标变量后将搜寻按特定格式定义的变量,在为该目标的父目标定义的特定目标变量前也要搜寻按特定格式定义的变量。

设置特定格式变量格式如下:

pattern ... : variable-assignment

或这样:

pattern ... : override variable-assignment

这里的‘pattern’是%-格式。象特定目标变量的值一样,‘pattern ...’中可含有多个格式,如此,则设置的特定格式变量的值可在匹配列表中的任一个格式中的目标中使用。‘variable-assignment’使用任何赋值方式都是有效的,在命令行中定义的变量的值占据优先的地位,而使用override指令定义的特定格式变量的值则占据优先地位。例如:

%.o : CFLAGS = -O

搜寻所有匹配格式%.o的目标,并将它的变量CFLAGS的值设置为‘-0’

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多