继续makefile变量的学习
7.1多行变量
使用define关键字设置变量的值可以有换行,有利于定义一系列的命令,例如前面的“命令包”,define 指示符后面跟的是变量的名字,而重起一行定义变量的值,定义是以 endef 关键字结束。其工作方式和“=”操作符一样。变量的值可以包含函数、命令、文字,或是其它变量。因为命令需要以[Tab]键开头,所以如果你用 define 定义的命令变量中没有以[Tab]键开头,那么 make 就不会把其认为是命令。
下面的这个示例展示了 define 的用法:
define two-lines
echo foo
echo $(bar)
endef
foo += "lujw"
bar += "this is a test"
.PHONY:print
print:
@$(two-lines)
可以自己尝试运行下看能输出什么结果。
7.2环境变量
并不推荐把许多的变量都定义在系统环境中,这样,在我们执行不用的 Makefile 时,拥有的是同一套系统变量,这可能会带来更多的麻烦。这里只讲解下环境变量的作用。
make 运行时的系统环境变量可以在 make 开始运行时被载入到 Makefile 文件中,但是如果 Makefile 中已定义了这个变量,或是这个变量由 make 命令行带入,那么系统的环境变量的值将被覆盖。(如果 make 指定了“-e”参数,那么,系统环境变量将覆盖 Makefile 中定义的变量),可以与之前的override指示符配合使用达到某种特定的情况需求
因此,在环境变量中设置了“CFLAGS”环境变量,就可以在所有的 Makefile 中使用这个变量了。这对于使用统一的编译参数有比较大的好处。如果 Makefile 中定义了 CFLAGS,那么则会使用 Makefile中的这个变量,如果没有定义则使用系统环境变量的值,一个共性和个性的统一,很像“全局变量”和“局部变量”的特性。当 make 嵌套调用时(参见前面的“嵌套调用”章节),上层 Makefile 中定义的变量会以系统环境变量的方式传递到下层的 Makefile 中。当然,默认情况下,只有通过命令行设置的变量会被传递。而定义在文件中的变量,如果要向下层 Makefile 传递,则需要使用 exprot 关键字来声明。
7.3目标变量
如果说前面的变量都是“全局变量”,那么目标变量就相当于“局部变量”,它可以和“全局变量同名”。作用范围只在这条规则以及连带规则中,所以其值也只在作用范围内有效。而不会影响规则链以外的全局变量的值。
语法为:
<target...>:<variable-assignment>
<target...>:override <variable-assignment>
<variable-assignment>可以是前面讲过的各种赋值表达式,如“=”、“:=”、“+=”或是“?=”。第二个语法是针对于 make 命令行带入的变量,或是系统环境变量。给个例子:
CC =gcc
main:CFLAGS=-g -o
main:main.o fun.o
$(CC) $(CFLAGS) main.o fun.o
main.o:CFLAGS=-g -c
main.o:main.c
$(CC) $(CFLAGS) main.c
fun.o:CFLAGS=-g -c
fun.o:fun.c
$(CC) $(CFLAGS) fun.c
当我执行make -n只输出打印时
可以看出CFLAGS的不同,例子本身没有实际意义,只是看目标变量的使用。
7.4模式变量
在 GNU 的 make 中,还支持模式变量(Pattern-specificVariable),通过上面的目标变量中,变量可以定义在某个目标上。模式变量的好处就是,可以给定一种“模式”,可以把变量定义在符合这种模式的所有目标上。make 的“模式”一般是至少含有一个“%”的,所以,我们可以以如下方式给所有以[.o]结尾的目标定义目标变量:
%.o:CFLAGS=-O
同样,模式变量的语法和“目标变量”一样:
<pattern...>:<variable-assignment>
<pattern...>:override<variable-assignment>
override 同样是针对于系统环境传入的变量,或是 make 命令行指定的变量。
7.5条件判断
使用条件判断,可以让 make 根据运行时的不同情况选择不同的执行分支。条件表达式可以是比较变量的值,或是比较变量和常量的值。
libs_for_gcc=-lgnu
normal_libs=
foo:$(objects)
ifeq($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif
上例子中“foo”可以根据变量“$(CC)”值来选取不同的函数库来编译程序,和c语言中if else语句类似。
另外一种写法
libs_for_gcc=-lgnu
normal_libs=
ifeq($(CC),gcc)
libs=$(libs_for_gcc)
else
libs=$(normal_libs)
endif
foo:$(objects)
$(CC) -o foo $(objects) $(libs)
这样子更简洁
语法
<conditional-directive>
<text-if-true>
endif
以及:
<conditional-directive>
<text-if-true>
else
<text-if-false>
endif
其中<conditional-directive>表示条件关键字,这个关键字有四个
第一个就是"ifeq"
具体写法为以下5种中的任意一种
ifeq(<arg1>,<arg2>)
ifeq'<arg1>''<arg2>'
ifeq"<arg1>""<arg2>"
ifeq"<arg1>"'<arg2>'
ifeq'<arg1>'"<arg2>"
比较参数“arg1”和“arg2”的值是否相同。当然,参数中我们还可以使用 make 的函数。如:
ifeq($(strip$(foo)),)
<text-if-empty>
endif
这个示例中使用了“strip”函数,如果这个函数的返回值是空(Empty),那么<text-if-empty>就生效。
第二个就是"ifneq"
其比较参数“arg1”和“arg2”的值是否相同,如果不同,则为真。和“ifeq”类似。
第三个条件关键字是“ifdef”
语法是:
ifdef<variable-name>
如果变量<variable-name>的值非空,那到表达式为真。否则,表达式为假。当然,<variable-name>同样可以是一个函数的返回值。注意,ifdef 只是测试一个变量是否有值,其并不会把变量扩展到当前位置。看例子
示例一:
bar=
foo=$(bar)
ifdef foo
frobozz=yes
else
frobozz=no
endif
示例二:
foo=
ifdef foo
frobozz=yes
else
frobozz=no
endif
第一个例子中,“$(frobozz)”值是“yes”,第二个则是“no”。
第四个条件关键字是“ifndef”, 和“ifdef”是相反的意思。
在<conditional-directive>,“else”,“endif”这一行上,只要不是以[Tab]键开始,多余的空格是被允许的。注释符“#”同样也是安全的。
值得注意的是不要把自动化变量(如“$@”等)放入条件表达式中,因为自动化变量是在运行时才有的。而且,为了避免混乱,make 不允许把整个条件语句分成两部分放在不同的文件中。