一、变量的基础
  变量在声明时需要给予初值,而在使用时,需要给在变量名前加上“$”符号,但最好用小括号“()”或是大括号“{}”把变量给包括起来。如果你要使用真实的“$”字符,那么你需要用“$$”来表示。
二、变量中的变量
  在定义变量的值时,我们可以使用其它变量来构造变量的值,在Makefile中有两种方式来在用变量定义变量的值。
  1)使用“=”号,在“=”左侧是变量,右侧是变量的值,右侧变量的值可以定义在文件的任何一处,也就是说,右侧中的变量不一定非要是已定义好的值,如:
        foo = $(bar)
        bar = $(ugh)
        ugh = Huh?
        all:
          echo $(foo)
  执行“make all”的输出结果 : 打出变量$(foo)的值是“Huh?”

  可见,变量是可以使用后面的变量来定义,这个功能好处就是可以把变量的真实值推到后面来定义,缺点就是在碰到递归调用时会让make运行效率慢或报错。更糟糕的是,他会使用得两个make的函数“wildcard” 和“shell”发生不可预知的
错误。因为你不会知道这两个函数会被调用多少次。为了避免上面的这种方法,我们可以使用第二种用变量来定义变量的方法。

  2)是“:=”操作符,如:
        x := foo
        y := $(x) bar
        x := later
    其等价于:
        y := foo bar
        x := later
  很显然,这种方法,前面的变量不能使用后面的变量,只能使用前面已定义好了的变量。如果是这样:
        y := $(x) bar
        x := foo
  那么,y的值是“bar”,而不是“foo bar”。
  小技巧:

  值为空格的变量,如
        nullstring :=
        space := $(nullstring) # end of the line
  nullstring是一个Empty变量,其中什么也没有,而操作符的右边是很难描述一个空格的,所以采用“#”注释符来表示变量定义的终止,这样就可以定义出其值是一个空格的变量。

三、变量高级用法
  1)变量值的替换。

  替换变量中的共有的部分,其格式是“$(var:a=b)”或是“${var:a=b}”,其意思是,把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束符”。还是看一个示例吧:

        foo := a.o b.o c.o
        bar := $(foo:.o=.c)
这个示例中,我们先定义了一个“$(foo)”变量,而第二行的意思是把“$(foo)”中所有以“.o”字串“结尾”全部替换成“.c”,所以我们的“$(bar)”的值就是“a.c b.c c.c”。
  另外一种变量替换的技术是以“静态模式”定义的,如:
        foo := a.o b.o c.o
        bar := $(foo:%.o=%.c)
  
这依赖于被替换字串中的有相同的模式,模式中必须包含一个“%”字符,$(bar)变量的值为“a.c b.c c.c”。
  2)把变量的值再当成变量。先看一个例子:
        x = y
        y = z
        a := $($(x))
  
在这个例子中,$(x)的值是“y”,所以$($(x))就是$(y),于是$(a)的值就是“z”。

  另一个例子:
        x = variable1
        variable2 := Hello
        y = $(subst 1,2,$(x))
        z = y
        a := $($($(z)))
  
这个例子中,“$($($(z)))”扩展为“$($(y))”,而其再次被扩展为 “$($(subst1,2,$(x)))”。$(x)的值是“variable1”,subst函数把“variable1”中的所有“1”字串替换成“2”字串,于是,“variable1”变成“variable2”,再取其值,所以,最终,
$(a)的值就是$(variable2)的值——“Hello”。
  
3)多个变量来组成一个变量的名字,然后再取其值:
        first_second = Hello
        a = first
        b = second
        all = $($a_$b)
  
这里的“$a_$b”组成了“first_second”,于是,$(all)的值就是“Hello”。
  
4)这种技术和“函数”与“条件语句”一同使用的例子:
        ifdef do_sort
          func := sort
        else
          func := strip
        endif
        bar := a d b g q c
        foo := $($(func) $(bar))
  
这个示例中,如果定义了“do_sort”,那么:foo := $(sort a d b g q c),于是$(foo)的值就是“a b c d g q”,而如果没有定义“do_sort”,那么:foo := $(strip a d b g q c),调用的就是strip函数。

四、追加变量值
   可以使用“+=”操作符给变量追加值,如:
        objects = main.o foo.o bar.o utils.o
        objects += another.o
  于是,我们的$(objects)值变成:“main.o foo.o bar.o utils.o  another.o”
  上面的两句等价于下面语句:
        objects = main.o foo.o bar.o utils.o
        objects := $(objects) another.o
  如果变量之前没有定义过,那么,“+=”会自动变成“=”,如果前面有变量定义,那么“+=”会继承于前次操作的赋值符。如果前一次的是“:=”,那么“+=”会以“:=”作为其赋值符。

五、目标变量
  相当于局部变量,这种变量被称为“Target-specific Variable”,它可以和“全局变量”同名,因为它的作用范围只在这条规则以及连带规则中,所以其值也只在作用范围内有效,而不会影响规则链以外的全局变量的值。其语法是:
        <target ...> : <variable-assignment>
        <target ...> : overide <variable-assignment>
  <variable-assignment>可以是前面讲过的各种赋值表达式,如“=”、“:=”、“+=”或是“?=”。第二个语法是针对于make命令行带入的变量,或是系统环境变量。
这个特性非常的有用,当我们设置了这样一个变量,这个变量会作用到由这个目标所引发的所有的规则中去。如:
        prog : CFLAGS = -g
        prog : prog.o foo.o 
          $(CC) $(CFLAGS) prog.o foo.o 
        prog.o : prog.c
          $(CC) $(CFLAGS) prog.c
        foo.o : foo.c
          $(CC) $(CFLAGS) foo.c
  在这个示例中,不管全局的$(CFLAGS)的值是什么,在prog目标,以及其所引发的所有规则中(prog.o foo.o 的规则),$(CFLAGS)的值都是“-g”。

模式变量

  目标变量的应用扩展,模式变量的好处就是,我们可以给定一种“模式”,可以把变量定义在符合这种模式的所有目标上。
我们知道,make的“模式”一般是至少含有一个“%”的,所以,我们可以以如下方式给所有以[.o]结尾的目标定义一个统一的目标变量(也就是模式变量):
        %.o : CFLAGS = -O
  
同样,模式变量的语法和“目标变量”一样:
        <pattern ...> : <variable-assignment>
        <pattern ...> : override <variable-assignment>
  
override同样是针对于系统环境传入的变量,或是make命令行指定的变量。

六、自动化变量

  所谓自动化变量,就是这种变量会把模式中所定义的一系列的文件自动地挨个取出,直至所有的符合模式的文件都取完了。这种自动化变量只应出现在规则的命令中。下面是所有的自动化变量及其说明:
  1)$@
    ——表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。
  2)$%
    ——仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a(bar.o)",那么,"$%"就是"bar.o","$@"就 是"foo.a"。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。
  3)$<
    ——依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。
  4)$?
    ——所有比目标新的依赖目标的集合,以空格分隔。
  5)$^
    ——所有的依赖目标的集合。以空格分隔。如在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标。
  6)$+
    ——这个变量很像"$^",也是所有依赖目标的集合。只是它不去除重复的依赖目标。
  7)$*
    ——这个变量表示目标模式中"%"及其之前的部分。如果目标是"dir/a.foo.b",并且目标的模式是"a.%.b",那么,"$*"的值就是"dir/a.foo"。如果目标中没有模式的定义,那么"$*"也就不能被推导出,但是,如果目标文件的后缀是 make所识别的,那么"$*"就是除了后缀的那一部分。例如:如果目标是"foo.c",因为".c"是make所能识别的后缀名,所以,"$*"的值 就是"foo"。这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用"$*",除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么"$*"就是空值。
  在上述所列出来的自动量变量中。四个变量($@、$<、$%、$*)在扩展时只会有一个文件,
而另三个的值是一个文件列表。这七个自动化变量还可以取得文件的目录名或是在当前目录下的符合模式的文件名,只需要搭配上"D"或"F"字样。这是 GNU make中老版本的特性,在新版本中,我们使用函数"dir"或"notdir"就可以做到了。"D"的含义就是Directory,就是目录,"F"的含义就是File,就是文件。
  下面是对于上面的七个变量分别加上"D"或是"F"的含义:
    $(@D)
    表示"$@"的目录部分(不以斜杠作为结尾),如果"$@"值是"dir/foo.o",那么"$(@D)"就是"dir",而如果"$@"中没有包含斜杠的话,其值就是"."(当前目录)。
    $(@F)
    表示"$@"的文件部分,如果"$@"值是"dir/foo.o",那么"$(@F)"就是"foo.o","$(@F)"相当于函数"$(notdir $@)"。
    "$(*D)","$(*F)"
    同上,也是取文件的目录部分和文件部分。对于上面的那个例子,"$(*D)"返回"dir",而"$(*F)"返回"foo"
    "$(%D)" "$(%F)"
    分别表示了函数包文件成员的目录部分和文件部分。这对于形同"archive(member)"形式的目标中的"member"中包含了不同的目录很有用。
    "$(<D)" "$(<F)"
    分别表示依赖文件的目录部分和文件部分。
    "$(^D)" "$(^F)"
    分别表示所有依赖文件的目录部分和文件部分。(无相同的)
    "$(+D)" "$(+F)"
    分别表示所有依赖文件的目录部分和文件部分。(可以有相同的)
    "$(?D)" "$(?F)"
    分别表示被更新的依赖文件的目录部分和文件部分。

Makefile系列之三 : 变量的更多相关文章

  1. 完毕port(CompletionPort)具体解释 - 手把手教你玩转网络编程系列之三

       手把手叫你玩转网络编程系列之三    完毕port(Completion Port)具体解释                                                    ...

  2. 单元测试系列之三:JUnit单元测试规范

    更多原创测试技术文章同步更新到微信公众号 :三国测,敬请扫码关注个人的微信号,感谢! 原文链接:http://www.cnblogs.com/zishi/p/6762032.html Junit测试代 ...

  3. TCP/IP网络编程系列之三(初级)

    TCP/IP网络编程系列之三-地址族与数据序列 分配给套接字的IP地址和端口 IP是Internet Protocol (网络协议)的简写,是为首发网络数据而分配给计算机的值.端口号并非赋予计算机值, ...

  4. DataSnap 2009 系列之三 (生命周期篇)

    DataSnap 2009 系列之三 (生命周期篇) DataSnap 2009的服务器对象的生命周期依赖于DSServerClass组件的设置 当DSServer启动时从DSServerClass组 ...

  5. Sql Server来龙去脉系列之三 查询过程跟踪

    我们在读写数据库文件时,当文件被读.写或者出现错误时,这些过程活动都会触发一些运行时事件.从一个用户角度来看,有些时候会关注这些事件,特别是我们调试.审核.服务维护.例如,当数据库错误出现.列数据被更 ...

  6. makefile中使用变量

    makefile里的变量就像一个变量,变量的作用主要如下: (1)保存文件名列表. (2)保存编译器的参数. makefile中的变量是用一个字符串在makefile中定义的,这个文本串就是变量的值. ...

  7. Red Gate系列之三 SQL Server 开发利器 SQL Prompt 5.3.4.1 Edition T-SQL智能感知分析器 完全破解+使用教程

    原文:Red Gate系列之三 SQL Server 开发利器 SQL Prompt 5.3.4.1 Edition T-SQL智能感知分析器 完全破解+使用教程 Red Gate系列之三 SQL S ...

  8. .NET 4 并行(多核)编程系列之三 从Task的取消

    原文:.NET 4 并行(多核)编程系列之三 从Task的取消 .NET 4 并行(多核)编程系列之三 从Task的取消 前言:因为Task是.NET 4并行编程最为核心的一个类,也我们在是在并行编程 ...

  9. javascript系列之变量对象

    原文:javascript系列之变量对象 引言 一般在编程的时候,我们会定义函数和变量来成功的构造我们的系统.但是解析器该如何找到这些数据(函数,变量)呢?当我们引用需要的对象时,又发生了什么了? 很 ...

随机推荐

  1. Springboot2.x+shiro+redis(Lettuce)整合填坑

    主要记录关键和有坑的地方 前提: 1.SpringBoot+shiro已经集成完毕,如果没有集成,先查阅之前的Springboot2.0 集成shiro权限管理 2.redis已经安装完成 3.red ...

  2. 【刷题】洛谷 P3804 【模板】后缀自动机

    题目描述 给定一个只包含小写字母的字符串 \(S\) , 请你求出 \(S\) 的所有出现次数不为 \(1\) 的子串的出现次数乘上该子串长度的最大值. 输入输出格式 输入格式: 一行一个仅包含小写字 ...

  3. BZOJ2427:[HAOI2010]软件安装——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=2427 https://www.luogu.org/problemnew/show/P2515 现在 ...

  4. HDU1007:Quoit Design——题解

    http://acm.hdu.edu.cn/showproblem.php?pid=1007 题目大意:给n个点,求点对最短距离/2. —————————————————————— 平面分治裸题. 暂 ...

  5. SPOJ1825/FTOUR2:Free tour II——包看得懂/看不懂题解

    http://www.spoj.com/problems/FTOUR2/en/ 题目大意:给一棵黑白染色的树,求边权和最大且经过黑点不超过K的路径. ———————————————————— 前排膜拜 ...

  6. BZOJ4539 [Hnoi2016]树 【倍增 + 主席树】

    题目链接 BZOJ4539 题解 我们把每次复制出来的树看做一个点,那么大树实际上也就是一棵\(O(M)\)个点的树 所以我们只需求两遍树上距离: 大树上求距离,进入同一个点后在模板树上再求一次距离 ...

  7. 51nod 1273 旅行计划(思维题)

    一开始看到这题真的有点懵逼...一直在想着套算法,结果题解除了sort和dfs其他什么都没用到 显然每次到达的一定都是叶子,先从根节点dfs一遍,按深度对叶子降序排序,按这个顺序向根节点dfs,路径上 ...

  8. 【DP】【P1586】四方定理

    传送门 Description Input 第一行为一个整数T代表数据组数,之后T行每行一个数n代表要被分解的数 Output 对于每个n输出一行,为方案个数 Sample Input Sample ...

  9. 解决webstrom 输入法光标不跟随问题

    参考博客地址 https://blog.csdn.net/wang414300980/article/details/79537875 原因是jdk版本问题,下载jdk jbsdk8u152b1036 ...

  10. CentOS安装pip

    环境 操作系统:CentOS 6.7 32-bit Python:2.6.6 安装 先安装setuptools和wget yum -y install wget wget https://pypi.p ...