在 Makefile 中,变量是一个名字(像是 C 语言中的宏),代表一个文本字符串(变量的值)。在 Makefile 的目标、依赖、命令中引用变量的地方,变量会被它的值所取代(与 C 语言中宏引用的方式相同,因此其他版本的 make 也把变量称之为“宏”)。在Makefile 中变量有以下几个特征:
1. Makefile 中变量和函数的展开 (除规则命令行中的变量和函数以外),是在 make读取 makefile 文件时进行的,这里的变量包括了使用“ =”定义和使用指示符“ define”定义的。
2. 变量可以用来代表一个文件名列表、编译选项列表、程序运行的选项参数列表、搜索源文件的目录列表、编译输出的目录列表和所有我们能够想到的事物。
3. 变量名是不包括“ :”、“ #”、“ =”、前置空白和尾空白的任何字符串。需要注意的是,尽管在GNU make中没有对变量的命名有其它的限制,但定义一个包含除字母、数字和下划线以外的变量的做法也是不可取的,因为除字母、数字和下划线以外的其它字符可能会在make的后续版本中被赋予特殊含义,并且这样命名的变量对于一些shell来说是不能被作为环境变量来使用的。
4. 变量名是大小写敏感的。变量“ foo”、“ Foo”和“ FOO”指的是三个不同的变量。 Makefile 传统做法是变量名是全采用大写的方式。推荐的做法是在对于内部定义定义的一般变量(例如:目标文件列表 objects)使用小写方式,而对于一些参数列表(例如:编译选项 CFLAGS)采用大写方式,但这并不是要求的。但需要强调一点:对于一个工程,所有 Makefile 中的变量命名应保持一种风格,否则会显得你是一个蹩脚的程序员(就像代码的变量命名风格一样)。
5. 另外有一些变量名只包含了一个或者很少的几个特殊的字符(符号)。称它们为自动化变量。像“ $<”、“ $@”、“ $?”、“ $*”等。
当我们定义了一个变量之后,就可以在 Makefile 的很多地方使用这个变量。变量的引用方式是:“ $( VARIABLE_NAME)”或者“ ${ VARIABLE_NAME }”来引用一个变量的定义。例如:“ $(foo) ”或者“ ${foo}”就是取变量“ foo”的值。
美元符号“ $”在 Makefile 中有特殊的含义,所有在命令或者文件名中使用“ $”时需要用两个美元符号“ $$”来表示。
注意: Makefile 中在对一些简单变量的引用,我们也可以不使用“()”和“ {}”来标记变量名,而直接使用“ $x”的格式来实现,此种用法仅限于变量名为单字符的情况。另外自动化变量也使用这种格式。对于一般多字符变量的引用必须使用括号了标记,否则 make 将把变量名的首字母作为作为变量而不是整个字符串 (“ $PATH”在 Makefile中实际上是“ $(P)ATH”)。这一点和 shell 中变量的引用方式不同。 shell 中变量的引用可以是 “ ${xx}”或者“ $xx”格式。但在 Makefile 中多字符变量名的引用只能是 “ $(xx)”或者“ ${xx}”格式。
第一种风格的变量是递归方式扩展的变量。这一类型变量的定义是通过“ =”或者使用指示符“ define”定义的。这种变量的引用,在引用的地方是严格的文本替换过程,此变量值的字符串原模原样的出现在引用它的地方。如果此变量定义中存在对其他变量的引用,这些被引用的变量会在它被展开的同时被展开。就是说在变量定义时,变量值中对其他变量的引用不会被替换展开;而是变量在引用它的地方替换展开的同时,它所引用的其它变量才会被一同替换展开。
为了避免“递归展开式”变量存在的问题和不方便。 GNU make 支持另外一种风格的变量,称为“直接展开”式。这种风格的变量使用“ :=”定义。在使用“ :=”定义变量时,变量值中对其他量或者函数的引用在定义变量时被展开(对变量进行替换)。所以变量被定义后就是一个实际需要的文本串,其中不再包含任何变量的引用。
GNU make 中,还有一个被称为条件赋值的赋值操作符“ ?=”。被称为条件赋值是因为:只有此变量在之前没有赋值的情况下才会对这个变量进行赋值。
通常,一个通用变量在定义之后的其他一个地方,可以对其值进行追加。这是非常有用的。我们可以在定义时(也可以不定义而直接追加)给它赋一个基本值,后续根据需要可随时对它的值进行追加(增加它的值)。在 Makefile 中使用“ +=”(追加方式)来实现对一个变量值的追加操作。
通常在执行 make 时,如果通过命令行定义了一个变量,那么它将替代在 Makefile中出现的同名变量的定义。就是说,对于一个在 Makefile 中使用常规方式(使用“ =”、“ :=”或者“ define”)定义的变量,我们可以在执行 make 时通过命令行方式重新指定这个变量的值,命令行指定的值将替代出现在 Makefile 中此变量的值。如果不希望命令行指定的变量值替代在 Makefile 中的变量定义,那么我们需要在 Makefile 中使用指示符“ override”来对这个变量进行声明,变量在定义时使用了“ override”,则后续对它值进行追加时,也需要使用带有“ override”指示符的追加方式。否则对此变量值的追加不会生效。
定义变量的另外一种方式是使用“ define”指示符。它定义一个包含多行字符串的变量,我们就是利用它的这个特点实现了一个完整命令包的定义。
define”定义变量的语法格式:以指示符“ define”开始,“ endif”结束,之间的所有内容就是所定义变量的值。所要定义的变量名字和指示符“ define”在同一行,使用空格分开;指示符所在行的下一行开始一直到“ endif”所在行的上一行之间的若干行,是变量值。 define two-lines
echo foo
echo $(bar)
endef
如果将变量“ two-lines”作为命令包执行时,其相当于:
two-lines = echo foo; echo $(bar)
使用“ define”定义的变量和使用“ =”定义的变量一样,属于“递归展开”式的变量,两者只是在语法上不同。因此“ define”所定义的变量值中,对其它变量或者函数引用不会在定义变量时进行替换展开,其展开是在“ define”定义的变量被展开的同时完成的。
sds