makefile之变量
2015-12-25 11:07
302 查看
一、变量定义
makefile中的变量其实本身是字符串,它的值同样是字符串。在引用变量时,变量会被其值所取代(类似于C语言中的宏变量,是严格按文本替换来进行的)。
二、变量引用
对于变量的引用,推荐只使用$(变量名)和${变量名}这两种方式。
三、变量的分类
makefile中的变量根据获得其值方式的不同大致可以分为两类:递归扩展变量(recursively expanded variable)和简单扩展变量(simply expanded variables)。它们主要的区别在于两点:1.定义方式的不同。2.引用时的展开方式不同。
递归扩展变量:递归扩展变量是用”=“或者指示符define定义的变量。
特点:这种类型的变量所引用的其它变量在定义时是不会被展开的,只有在make程序在生成某个目标使用到这个变量时,其中的变量引用才会被递归地全部展开,直到没有任何变量引用为止。
优点:
1. 可以引用在其之后定义的变量。例如:
![](https://img-blog.csdn.net/20151225152555972?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
那么在后续规则中使用CFLAGS变量的地方,会将变量include_dirs展开,即最后CFLAGS的值为-Ifoo -lbar -O,而不是-O。
缺点:
1. 如果引用自身就会导致递归死循环(最新版本的make都会发现这种错误而提示错误退出)。
2. 如果变量定义中有引用函数,那么每次扩展时都会调用该函数,这势必影响make的运行效率。此外,还会使得wildcard和shell函数返回不可预期的结果。
简单扩展变量:简单扩展变量使用“:=”或者“::=”定义的变量。
特点:这种类型的变量在定义时就会一次性完成所有引用变量的扩展和引用函数的运行,即变量的值在定义时就已经确定。
优点:
1. make程序执行效率高,语法更简洁。
2. 允许使用变量原有的值来重新定义自己。例如: CFLAGS := $(CFLAGS) -O,完全没有问题的。
缺点:
1. 不可以引用在其之后定义的变量。例如:
CFLAGS := $(include_dirs) -O
include_dirs := -lfoo -lbar
因为include_dirs的定义出现在CFLAGS之后,因此在CFLAGS的定义中,变量include_dirs的值为空,那么CFLAGS的值为-O,而不是-lfoo -lbar -O,这是与递归扩展变量最大的区别。
综合以上两者的比较,建议在makefile中使用简单扩展变量。因为简单扩展变量的使用方式和绝大多数编程语言中的变量使用方式基本上相同。它可以使一个比较复杂的makefile在一定程度上具有可预测性。而且这种变量允许我们利用之前所定义的值来重新定义它(比如使用某一个函数来对它以前的值进行处理并重新赋值),此方式在makefile中经常用到。尽量避免和减少递归扩展变量的使用。
三、条件赋值操作符(conditional variable assignment operator)
?=操作符在makefile中被称为条件赋值操作符,其只有在变量没有定义的情况下才会对变量进行定义并赋值。例如
FOO ?= bar
完全等同于
ifeq ($(origin FOO), undefined)
FOO = bar
endif
再来具体看一个例子如下:
![](https://img-blog.csdn.net/20151225175610872?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
输入make -s,结果如下:
i2c_support=yes
i2c_support is undefined
如果去掉第三行的注释,make -s,结果如下:
i2c_support=
i2c_support has no right value
从这个例子可以得出如下结论:
(1)?=只有在变量没有定义的情况下才会定义变量并赋值
(2)ifdef var表达式只有在var的值不为空(只要赋值符号后面有值即可)的情况下才为真。
四、追加赋值操作符
我们可以使用“+=”来给变量追加值。例如:
![](https://img-blog.csdn.net/20151227195237577?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
等同于如下过程:
![](https://img-blog.csdn.net/20151227195255040?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
只不过前者更简洁。
关于使用追加操作符”+=“,我们只需了解下面三点就OK。
(1)如果被追加的变量之前是没有定义过的,那么此时“+=”相当于“=”,即变量默认被make认为是递归扩展变量。
(2)如果变量是简单扩展变量,那么“+=”会首先展开变量的值,然后在末尾添加需要追加的值,并使用“:=”重新给变量赋值。过程如下:
![](https://img-blog.csdn.net/20151227202628733?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
等同于:
![](https://img-blog.csdn.net/20151227202729704?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
(3)如果变量是递归扩展变量,那么“+=”不会对变量中的任何引用进行展开,而是严格按照文本方式替换,然后在末尾添加需要追加的值,并使用“=”重新给变量赋值。但是,等等,这样不是会出现引用自身从而导致出现无限循环的问题吗?不用担心,make会通过引入中间变量的方式解决这个问题。过程如下:
![](https://img-blog.csdn.net/20151227205555606?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
等同于:
![](https://img-blog.csdn.net/20151227205621221?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
可以看到,make通过引入从未使用过的temp变量解决无限循环的问题。
重要结论:通过(2)(3)两点我们发现,“+=”操作符会继承变量的类型(即不会改变变量的属性)。
可以通过以下一个简单例子来理解“+=”操作符的这种继承特性:
例如当前makefile中有如下内容:
![](https://img-blog.csdn.net/20151227211415352?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
定义递归扩展变量CFLAGS,并且引用变量includes。根据递归扩展变量的特性,知道定义CFLAGS时不会发生任何扩展,即此时可以实际没有定义变量includes,只需要保证在第一次引用CFLAGS之前定义includes即可。
那么下面的两种追加方式有到底有什么区别呢,会引发什么问题呢?
![](https://img-blog.csdn.net/20151227211839748?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](https://img-blog.csdn.net/20151227211654693?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
在这里使用“:=”操作符最大的问题是导致CFLAGS变量从递归扩展变量变成了简单扩展变量。导致的直接后果就是make会扩展“$(CFLAGS) -pg”,因为变量includes还未定义,因此最终扩展结果是“-O -pg”。如果在这之后定义includes变量,已经不会影响CFLAGS了。相反,使用“+=”操作符就不会出现这种问题,因为它会保留变量的引用。
参考链接:https://www.xdty.org/1286 《makefile变量》
http://www.cnblogs.com/yyangblog/p/4159778.html 《说说makefile那些事儿》
http://blog.csdn.net/npy_lp/article/details/39130521 《GNUMakefiles之Makefiles变量的使用》
http://blog.csdn.net/maopig/article/details/6832293 《Makefile中的变量》
makefile中的变量其实本身是字符串,它的值同样是字符串。在引用变量时,变量会被其值所取代(类似于C语言中的宏变量,是严格按文本替换来进行的)。
二、变量引用
对于变量的引用,推荐只使用$(变量名)和${变量名}这两种方式。
三、变量的分类
makefile中的变量根据获得其值方式的不同大致可以分为两类:递归扩展变量(recursively expanded variable)和简单扩展变量(simply expanded variables)。它们主要的区别在于两点:1.定义方式的不同。2.引用时的展开方式不同。
递归扩展变量:递归扩展变量是用”=“或者指示符define定义的变量。
特点:这种类型的变量所引用的其它变量在定义时是不会被展开的,只有在make程序在生成某个目标使用到这个变量时,其中的变量引用才会被递归地全部展开,直到没有任何变量引用为止。
优点:
1. 可以引用在其之后定义的变量。例如:
那么在后续规则中使用CFLAGS变量的地方,会将变量include_dirs展开,即最后CFLAGS的值为-Ifoo -lbar -O,而不是-O。
缺点:
1. 如果引用自身就会导致递归死循环(最新版本的make都会发现这种错误而提示错误退出)。
2. 如果变量定义中有引用函数,那么每次扩展时都会调用该函数,这势必影响make的运行效率。此外,还会使得wildcard和shell函数返回不可预期的结果。
简单扩展变量:简单扩展变量使用“:=”或者“::=”定义的变量。
特点:这种类型的变量在定义时就会一次性完成所有引用变量的扩展和引用函数的运行,即变量的值在定义时就已经确定。
优点:
1. make程序执行效率高,语法更简洁。
2. 允许使用变量原有的值来重新定义自己。例如: CFLAGS := $(CFLAGS) -O,完全没有问题的。
缺点:
1. 不可以引用在其之后定义的变量。例如:
CFLAGS := $(include_dirs) -O
include_dirs := -lfoo -lbar
因为include_dirs的定义出现在CFLAGS之后,因此在CFLAGS的定义中,变量include_dirs的值为空,那么CFLAGS的值为-O,而不是-lfoo -lbar -O,这是与递归扩展变量最大的区别。
综合以上两者的比较,建议在makefile中使用简单扩展变量。因为简单扩展变量的使用方式和绝大多数编程语言中的变量使用方式基本上相同。它可以使一个比较复杂的makefile在一定程度上具有可预测性。而且这种变量允许我们利用之前所定义的值来重新定义它(比如使用某一个函数来对它以前的值进行处理并重新赋值),此方式在makefile中经常用到。尽量避免和减少递归扩展变量的使用。
三、条件赋值操作符(conditional variable assignment operator)
?=操作符在makefile中被称为条件赋值操作符,其只有在变量没有定义的情况下才会对变量进行定义并赋值。例如
FOO ?= bar
完全等同于
ifeq ($(origin FOO), undefined)
FOO = bar
endif
再来具体看一个例子如下:
输入make -s,结果如下:
i2c_support=yes
i2c_support is undefined
如果去掉第三行的注释,make -s,结果如下:
i2c_support=
i2c_support has no right value
从这个例子可以得出如下结论:
(1)?=只有在变量没有定义的情况下才会定义变量并赋值
(2)ifdef var表达式只有在var的值不为空(只要赋值符号后面有值即可)的情况下才为真。
四、追加赋值操作符
我们可以使用“+=”来给变量追加值。例如:
等同于如下过程:
只不过前者更简洁。
关于使用追加操作符”+=“,我们只需了解下面三点就OK。
(1)如果被追加的变量之前是没有定义过的,那么此时“+=”相当于“=”,即变量默认被make认为是递归扩展变量。
(2)如果变量是简单扩展变量,那么“+=”会首先展开变量的值,然后在末尾添加需要追加的值,并使用“:=”重新给变量赋值。过程如下:
等同于:
(3)如果变量是递归扩展变量,那么“+=”不会对变量中的任何引用进行展开,而是严格按照文本方式替换,然后在末尾添加需要追加的值,并使用“=”重新给变量赋值。但是,等等,这样不是会出现引用自身从而导致出现无限循环的问题吗?不用担心,make会通过引入中间变量的方式解决这个问题。过程如下:
等同于:
可以看到,make通过引入从未使用过的temp变量解决无限循环的问题。
重要结论:通过(2)(3)两点我们发现,“+=”操作符会继承变量的类型(即不会改变变量的属性)。
可以通过以下一个简单例子来理解“+=”操作符的这种继承特性:
例如当前makefile中有如下内容:
定义递归扩展变量CFLAGS,并且引用变量includes。根据递归扩展变量的特性,知道定义CFLAGS时不会发生任何扩展,即此时可以实际没有定义变量includes,只需要保证在第一次引用CFLAGS之前定义includes即可。
那么下面的两种追加方式有到底有什么区别呢,会引发什么问题呢?
在这里使用“:=”操作符最大的问题是导致CFLAGS变量从递归扩展变量变成了简单扩展变量。导致的直接后果就是make会扩展“$(CFLAGS) -pg”,因为变量includes还未定义,因此最终扩展结果是“-O -pg”。如果在这之后定义includes变量,已经不会影响CFLAGS了。相反,使用“+=”操作符就不会出现这种问题,因为它会保留变量的引用。
参考链接:https://www.xdty.org/1286 《makefile变量》
http://www.cnblogs.com/yyangblog/p/4159778.html 《说说makefile那些事儿》
http://blog.csdn.net/npy_lp/article/details/39130521 《GNUMakefiles之Makefiles变量的使用》
http://blog.csdn.net/maopig/article/details/6832293 《Makefile中的变量》
相关文章推荐
- 课程设计-学生成绩管理系统
- UIImageJPEGRepresentation和UIImagePNGRepresentation
- 华为oj:字符串加解密
- 【HDU】4888 Redraw Beautiful Drawings 网络流【推断解是否唯一】
- Java进程间通信
- 2015数据结构课程设计——职工信息管理系统
- LVM配置与管理
- iOS app上架流程
- iOS通知传值
- open()参数宏的意义
- resquest.getHeader()以及response.setContentType()
- 总结的一些公用函数库!
- 使用Javah 生成C/C++头文件的误区
- 分数约分算法
- 细说Android事件传递机制(dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent)
- spring aop
- linux 用户管理
- form表单的target标签配合iframe实现不跳转刷新
- Windows下AndroidStudio 中使用Git(AndroidStudio项目于GitHub关联)
- gcc编译出现的问题