GNUMakefiles之Makefiles变量的使用

    参考文献:The GNU Make Manual v0.70(可从http://download.csdn.net/detail/npy_lp/7544225 上下载)

 

    因为Makefile文件是以文本文件的形式来编辑和保存的,所以这里所说的变量仅仅是用来代表一段文本形式的字符(这段字符也就是这个变量的值)而已。简单地说,就是为了Makefile文件语法的清晰和灵活,将一小段的文本(变量名)来代替一段其中的字符可能会改变的文本(变量的值)。反过来,在所有使用变量的地方都将被它的值所替代。

    一、变量的定义

    在makefile文件中,使用“=”或“:=”等赋值操作符就可以定义一个变量,并且赋值操作符之后所在行的所有文本都当作该变量的值。变量名前后和赋值操作符之后的空白字符都是被忽略的。如果一个变量的定义过长,可以使用反斜线将定义分成多行。从来没有赋值的变量将具有一个空值,除了一些具有默认值的变量或者在规则中自动赋值的变量。

    (1)、两种不同风格的变量

    在GNU Makefiles文件中,有两种基本的并具有不同风格的变量,它们的区别主要在于定义和展开时的方式。一种是递归展开式变量(recursively expanded variable),另一种是简单展开式变量(simply expanded variable)。

    递归展开式变量使用字符‘=’或指示符define来定义。这种变量所引用的其它变量在它定义时不会被展开,只有在make程序执行某个目标而使用到该变量时,其中的变量的引用才会递归地全部展开,直到没有任何变量的引用为止。这样子有个好处就是递归展开式变量可以引用在它之后所定义的变量。但是坏处也不少,一是引用自身会导致展开的死循环(不过在make程序遇到这样的死循环时都会报错并终止执行),二是在每次展开变量时都会执行它所包含的函数(如果有的话),这样会使make程序执行变慢,更严重的是,它还会导致wildcard和shell函数返回一个不可预测的值,因为很难控制它们调用的时机和次数。

    简单展开式变量使用“:=”来定义。这种变量的值在它定义的时候就已经最终确定,其中所有的变量的引用和函数都已经全部展开。这时候,该变量不再包含任何其它的变量的引用和函数,而只是包含这些变量的引用和函数展开或执行完成以后的值,所以这种变量的执行效率更高,语法更为清晰。简单展开式变量还允许使用变量原有的值来重新定义它本身,如:

   

    (2)、使用条件赋值操作符来定义

    条件赋值操作符使用“?=”两个字符来表示。只有在该变量尚未定义的时候,条件赋值语句才有效。

   

    注意,对于条件赋值操作符来说,具有空值的变量是表示已经定义的,所以此时它是无效的,不会去改变其中变量的值。

    (3)、使用追加赋值操作符来定义

    追加赋值操作符使用“+=”两个字符来表示,用来为已定义的变量增加更多的值(新增值与旧的值之间使用一个空格来分隔)。如果在使用“+=”之前该变量并没有定义,则“+=”的行为与“=”相同;如果在使用“+=”之前该变量已经定义,则“+=”的行为与“=”和“:=”之中的某个操作符相同,具体取决于之前定义时所使用的操作符。

    (4)、使用指示符override来定义

    通过make程序命令行参数所设置的变量会覆盖它所执行的makefile文件中定义的相同变量,除非在makefile文件中使用指示符override来定义该变量。

    使用指示符override来定义变量的基本形式有以下几种:

   

    通过指示符override定义的变量可以修改或增加命令行参数所设置的相同变量的值。

    (5)、使用指示符define来定义

    通过指示符define定义的变量的值中允许使用换行符,也就是说变量的值可以是多行的,这样很方便于定义命令序列或使用eval函数处理的功能段。它的语法格式分为三个部分,首行是指示符define与紧随其后的变量名,其次是一行或多行的变量值,最后一行是指示符endef,用来表示变量值的结束。

   

    其中,所定义的变量名中可以使用变量的引用或函数(在make读取makefile文件时它们就已经展开或执行);变量值包括其中的换行符,除了endef之前的那个。

    其实,除了语法上的差异之外,指示符define的行为与“=”相同,用来定义一个递归展开式变量。

    (6)、使用make程序运行环境中的变量

    在make程序开始运行时,当前的每个环境变量都会分别转变成为具有相同名字和值的make的变量,除非在makefile文件中使用赋值操作符重新定义了该变量,或者通过make程序的命令行参数重新设置了该变量(但是可以通过-e选项指定使用相应的环境变量,而不是使用makefile文件中定义的,不过不推荐这样做)。

    为了向子make程序调用传递变量,可以使用环境变量或来自make程序命令行参数的变量,也可以通过指示符export来实现,除此之外,makefile文件的实现都不应该依赖于环境变量,因为这样子做很可能会造成同样的makefile文件在不同的运行环境下会产生不同的结果。

    (7)、特定目标的变量

    通常情况下,makefile文件中的变量的值都是全局的,也就是说,在所有使用该变量的地方它们的值都应该是相同的(除非当中有修改),但也有两个例外,一个是自动变量的值(只在所属目标的命令中或者其他特定目标变量的定义中有效),另一个就是特定目标变量的值(target-specific variable values)。

    特定目标变量的定义使用如下几种格式:

   

    其中,variable-assignment表达式可以由“=”,“:=”,“?=”和“+=”这四种赋值操作符中的任意一种来构成。如果这里的target具有多个,则分别为其中的每个目标创建一个特定目标变量。

    特定目标变量的有效性只局限于它所属目标的上下文中,与之前在makefile文件中所定义的普通变量或其他相同的特定目标变量完全不同,互不影响。因此对于不同的目标,相同的特定目标变量可以具有不同的值。这一点正是特定目标变量的用处。

    特定目标变量与其他在makefile文件中定义的变量一样,具有相同的优先级,所以通过make程序命令行参数设置的变量或者通过-e选项强制使用的环境变量会覆盖特定目标变量,除非使用指示符override来定义特定目标变量。

    特定目标变量对所属目标的依赖条件以及将这些依赖条件作为目标的依赖条件等等所有层级的依赖条件同样有效,除非其中某级的依赖条件重新定义了该特定目标变量。

    由于每次调用make程序时一个给定的依赖条件只能创建一次,所以对于具有相同依赖条件的多个目标,其中相同的特定目标变量将使用相同的值,也就是创建第一个目标时所产生的值。

    (8)、特定模式的变量

    特定模式变量与特定目标变量的意义完全相同,只是它们的定义稍有区别,特定目标变量是为所列的所有目标创建变量,而特定模式变量是为匹配指定模式的任意目标创建变量。特定模式变量的定义格式如下:

   

    如果pattern具有多个,则分别为其中的每个模式创建一个特定模式变量。如果一个目标匹配多种模式,则为这个目标创建所有匹配模式的变量,按照它们在makefile文件中定义的次序。

    举例说明,“%.o : CFLAGS = -O”的意思是为匹配“%.o”模式的所有目标创建一个值为“-o”的变量CFLAGS。

    (9)、自动变量

    根据规则的目标和依赖条件,在每次执行规则的命令时都将重新计算自动变量的值。通常情况下,自动变量(automatic variable)只能使用在规则的命令中,除非使用GNU make的二次展开功能(在这种情况下,自动变量可以使用在规则的依赖条件中)。

    各种自动变量的含义解释如下:

    “$@”代表规则的目标的文件名。如果目标是一个archive(member),则“$@”就代表其中archive的名字;在具有多个目标的规则中,“$@”就代表引起规则的命令执行的那个目标的名字。

    “$%”代表目标archive(member)中member的名字。如果一个目标不是archive(member),则“$%”的值为空。

    “$<”代表第一个依赖条件的名字。

    “$?”代表所有比目标更新的依赖条件的名字(依赖条件之间使用空格分隔)。对于archive(member)形式的依赖条件,“$?”仅代表其中member的名字。

    “$^”代表所有依赖条件的名字(依赖条件之间使用空格分隔)。对于archive(member)形式的依赖条件,“$^”仅代表其中member的名字。对于同一目标,相同的依赖条件可以出现多次,但“$^”的值中只包含其中的一份。“$^”不包含任何order-only依赖条件(依赖条件分为两种类型,一种是普通依赖条件,一种是order-only依赖条件)。

    “$+”与“$^”类似,只不过对于同一目标,多次出现的相同的依赖条件按照在makefile文件中出现的次序全部包含在“$+”的值中。对于链接命令来说,按照特定顺序重复出现的库文件名是有意义的。

    “$|”代表所有order-only依赖条件的名字(依赖条件之间使用空格分隔)。

    “$*”代表隐式规则中匹配的那部分名字(包含目录部分)。例如,对于目标“dir/a.foo.b”和模式“a.%.b”,“$*”的值就是“dir/foo”。在静态模式规则中,“$*”就是代表与目标模式中的“%”匹配的那部分文件名。

    在显式规则中,“$*”的值是不确定的。如果目标名字的末尾未携带众所周知的扩展名,则“$*”的值为空,否则,“$*”的值就是除扩展名之外的那部分。

    GNUmake程序这样做仅仅是为了兼容其它实现的make程序。通常需要避免在隐式规则和静态模式规则之外的规则中使用。

    在GNU make程序中,由于函数dir和notdir提供了相似的功能,因此上面所列的自动变量的变体(它们分别为“$(@D)”,“$(@F)”,“$(%D)”,“$(%F)”,“$(<D)”,“$(<F)”,“$(?D)”,“$(?F)”,“$(^D)”,“$(^F)”,“$(+D)”,“$(+F)”,“$(*D)”,“$(*F)”,其中D表示不包含最后斜杠的目录名,F表示文件名)基本上处于废弃状态。

    (10)、常用的预定义变量

    可以通过在makefile文件中重新定义,或者通过make程序的命令行参数设置,或者通过系统中的环境变量来修改等方式来修改预定义变量的值。也可以通过make程序的-R或—no-builtin-variables选项来取消所有的预定义变量。

    通过执行“make -p”命令可以获知GNU make程序当前所使用的所有的预定义变量。

    二、变量的引用

    在Makefile文件中,以$(variable-name)或${variable-name}这两种形式来使用一个变量,将其称之为变量的引用。如果$字符后不带小括号或大括号,则表示该变量名只有一个字符($$用来表示$字符本身),不过实践中不推荐这样使用,除非是自动变量。

    变量的引用可以使用在规则的目标、依赖条件和命令中,大多数的指示符中,以及一个变量的值中。

    将变量值替换变量的引用的过程称之为展开。在GNU make程序读取makefile文件时,其中所有的变量的引用和函数都将被展开或执行,除了在规则的命令和递归展开式变量的值里面的。

    (1)、替换引用

    替换引用(substitution reference)使用$(var:a=b)或者${var:a=b}这两种格式来表示,意思是说将变量var值中的每个单词末尾的a(或者a本身就是一个单词)都被b所替换,然后以替换后的所有单词组成一组新的值。例如,下面的例子表示将变量bar的值设置为“a.c b.c c.c”。

   

    事实上,替换引用就是patsubst函数的简化形式。提供替换引用是为了兼容其他实现的make程序。另外,包含单个字符“%”的$(var:a=b)完全等效于$(patsubsta,b,$(var)),于是上例中的替换引用就可以写成$(foo:%.o=%.c)这样的形式。

    (2)、嵌套的变量引用

    嵌套的变量引用(nested variable reference),也叫做尚未确定的变量名(computed variable name),简单地说,就是变量名中包含了其他变量的引用或函数。它的使用非常灵活和繁杂,使用不当就会产生一些奇怪的结果,所以大多数情况下都不建议使用。

    其中的嵌套可以是两层的(如a := $($(x))所示),也可以是多层的(如a := $($($(x)))所示),可以包含函数的调用(如$($(subst 1,2,$(x)))所示),也可以包含多个变量的引用(如$($(a1)_$(df))所示),可以使用在替换引用之中(如sources := $($(a1)_objects:.o=.c)所示),也可以使用在赋值符号左边的变量名中(如$(dir)_sources := $(wildcard $(dir)/*.c)所示),或者使用在指示符define所定义的变量名中(如define $(dir)_print所示)。

    对GNU make 3.81来说,嵌套的变量引用的唯一限制是不能用来引用函数名,这是因为在展开嵌套的变量引用之前函数名都已经确定。在未来的版本中可能会取消这个限制。如下所示:

   

     其中,foo的值是变量“sort a d b g q c”或“strip a d b g q c”的值,而不是将“a d b g q c”作为参数传给函数sort或strip所得到的值。

    三、注意事项

    (1)、在变量名中不能使用‘:’、‘#’和‘=’等字符,变量名的首位和末位不能使用空白字符(因为这些空白字符在使用时会被忽略)。在实践中,变量名最好只使用字母、数字和下划线。

    (2)、变量名是区分大小写的。推荐使用小写字母。

    (3)、在变量值中,第一个非空白字符前面所有的空白字符都是被忽略的,因此如果打算在一个变量值中使用前导空白字符的话,可以按照下例的方式来实现(变量space就拥有了一个空格(在字符‘)’和‘#’之间))。

   

    (4)、在变量值中,最后一个非空白字符之后的所有空格都被当作变量值的一部分,因此下面示例的效果未必是你想要的(在bar和字符‘#’之间具有几个空格)。

   

    因此,如果不希望变量值的末尾使用空格的话,最好不要将注释和变量的定义写在同一行。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tanglinux

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值