• [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中,根据使用情况不定期纠正本文中错误和增加新条款。

  • 相关阅读:
    NOI 2016 区间 解题报告
    有关莫队
    [JSOI2008]最大数 线段树解法
    HDU P3341 Lost's revenge 题解+数据生成器
    BZOJ P1212 [HNOI2004] L语言
    洛谷P3168 [CQOI2015]任务查询系统
    普通平衡树Tyvj1728、luogu P3369 (splay)
    洛谷P3384 树链剖分
    BZOJ P2157 旅游
    【算法导论】第6章,堆排序
  • 原文地址:https://www.cnblogs.com/cbscan/p/2276274.html
Copyright © 2020-2023  润新知