Chinaunix首页 | 论坛 | 博客
  • 博客访问: 183044
  • 博文数量: 34
  • 博客积分: 869
  • 博客等级: 准尉
  • 技术积分: 375
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-08 15:55
文章分类

全部博文(34)

文章存档

2012年(34)

我的朋友

分类: 项目管理

2012-04-11 17:18:06

about makefile variable assignment i have a big question that what's different between := and =
today i saw a brilliant article to explain that, alright let's start  it.
-------------------------------------------------------------
|example1:
|var1 = abc
|var2 = $(var1)
|var1 = def
|output:
|        echo "$(var2)"
|-----------------------------------------------------------
command output -----def
-------------------------------------------------------------
|example2:
|var1 = abc
|var2 := $(var1)
|var1 = def
|output:
|        echo "$(var2)"
|-----------------------------------------------------------
command output -----abc

"="是直接给变量赋值
":="是将":="右边中包含的变量直接展开给左边的变量赋值。
how to used the two ways of operation and which way is the best.
for example
A = $(B)
B = $(A)
make陷入无限的变量展开过程中去,当然,我们的make是有能力检测这样的定义,并会报错。还有就是如果在变量中使用函数,那么,这种方式会让我们的make运行时非常慢
to void the situation we often use the := operation
这种方法,前面的变量不能使用后面的变量,只能使用前面已定义好了的变量。
example2  var is abc not the def
y := $(x) bar
x := foo     y is bar not foo bar

GNU make处理makefile过程:
1.
执行make命令后,GNU make扫描整个makefile,将其中出现的变量赋值和目标依赖关系记录到数据库(就是make解析makefile得到的所有数据集合,用make -p查看)中,其中每个变量记录变量名及其字面值用$(value)可以查看,即如myVar = $(CFLAG)的字面值就是$(CFLAG),暂不进行展开。这个过程中对$所标记的变量或函数不进行任何展开,除非遇到几种特殊情况(后文提到)。

2. 扫描整个数据库,展开所有$引导的变量和函数(这里至少有两趟扫描,首先会扫描$(value)调用并展开,第二次扫描才展开其他$),然后按照拓扑顺序,从尾端依赖到主目标,逐个检查时间戳或文件存在性,执行相应的命令。

上面描述的重点是,在make完成对整个文件的扫描,执行步骤2前,是不会进行$展开的(后文提到的几个例外会实时展开$)。

基于上述描述,再来看一下惰性变量赋值:

  Var1 = abc

  Var2 = $(Var1)

  Var1 = def

  逐行看,第1行,在数据库中记录Var1="abc";第2行,记录Var2="$(Var1)";第3行,修改数据库中的记录Var1为"def"。最后扫描makefile完毕,对数据库中变量进行$展开求值:Var1的值"def"中不包含$,不用展开,Var2的值"$(Var1)"展开为"def"。这就完成了惰性赋值,保证了Var1和Var2同步。


再来陈述几个在扫描makefile中就会进行$展开的例外:
1. 实时展开:=。分析如下代码:
  Var1 = abc 
  Var2 := $(Var1)

第1行记录Var1="abc"到数据库;第2行,检测到:=,故先将"$(Var1)"展开为"abc",再记录Var2="abc"。对比上面的惰性赋值,make工具扫描完makefile后执行最终$展开时,Var2的字面值中是已经没有$的了。

2. $(eval)调用。当make扫描makefile时,如果发现包含$(eval)的语句,会实时展开eval的参数,然后将展开的结果字符串插入到脚本当前位置,作为脚本还没扫描的一部分继续扫描。下面是一段测试脚本:
  Name = a1
  Step1 := $(Name)

  $(eval Name=a2)
  Step2 := $(Name)

  Name = a3
  Step3 := $(Name)

  all:

   echo $(Name)
       echo $(Step1)
   echo $(Step2)
   echo $(Step3)

然后来分析(注意这里的措辞):第1行在数据库中添加Name="a1";第2行添加Step1="a1"(进行过一次实时展开);第3行等价于脚本Name=a2,因此是修改数据库,将Name的字面值从"a1"改为"a2";第4行实时展开后记录Step2="a2"; 第5行修改Name为"a3";第6行记录Step3="a3"。最后运行make打印"a3 a1 a2 a3"(这里的空格实际是换行)。

3.目标或变量名中出现的$会被实时展开。毕竟,扫描脚本的时候实时展开名称中的$,才能判断该变量值或者目标依赖和命令,应该被记录到数据库中的哪个位置。虽说在实现make时采用延迟展开名称中的$的做法并非办不到,但一则这种实现更复杂,二则,延迟展开名称中的$这种做法会和前两种实时展开相冲突,如:

Name = abc
$(Name) = 123
Name3 := $(abc)
Name = abc
all:
        echo $(Name3)

由于第3行有一个实时展开(这个实时展开规则是make保证的),因此第3行之前应该有一个叫做abc的变量存在,这样最后才会打印一个更符合只觉的123,如果名称中的$是延迟展开的话,最后打印空行,恐怕会让很多人惊诧了。

上面这个例子只是为了说明为什么有必要实时展开名称中的$,下面再加一个测试例外3的简单例子:

Name = abc
$(Name) = 123
tar_$(Name):
   echo $@
   echo $(abc)
   echo $(Name)
Name = def

分析:
第1行记录Name="abc";第2行实时展开后,记录abc="123";3、4、5行进行实时展开后,添加目标记录 tar_abc,命令为echo $@和echo $(abc)。
最后输出"
tar_abc
123
def"
输出里有意思的是,由于一个是实时展开,一个是延迟展开,两个$(Name)分别是"abc"和"def"

阅读(886) | 评论(0) | 转发(0) |
0

上一篇:git index

下一篇:vim delete command

给主人留下些什么吧!~~