zoukankan      html  css  js  c++  java
  • [Unix]关于GNU make的工作方式

      我在这里记录一下对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 = def

      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)和echo $(Name)。最后输出"tar_abc 123 def"(空格是换行)。输出里有意思的是,由于一个是实时展开,一个是延迟展开,两个$(Name)分别是"abc"和"def"。

      继续在学习make中,根据使用情况不定期纠正本文中错误和增加新条款。

  • 相关阅读:
    English 2
    速算24点
    心理学1
    从微服务到函数式编程
    034 01 Android 零基础入门 01 Java基础语法 04 Java流程控制之选择结构 01 流程控制概述
    033 01 Android 零基础入门 01 Java基础语法 03 Java运算符 13 运算符和表达式知识点总结
    032 01 Android 零基础入门 01 Java基础语法 03 Java运算符 12 运算符和if-else条件语句的综合案例——闰年问题
    031 01 Android 零基础入门 01 Java基础语法 03 Java运算符 11 运算符的优先级
    030 01 Android 零基础入门 01 Java基础语法 03 Java运算符 10 条件运算符
    029 01 Android 零基础入门 01 Java基础语法 03 Java运算符 09 逻辑“非”运算符
  • 原文地址:https://www.cnblogs.com/cbscan/p/2276274.html
Copyright © 2011-2022 走看看