变量定义

  1. 前言

    • 变量

      • 替换是一对一的替换。
      • 有的版本管变量叫做宏。和C/C++的宏类似。
    • 读取

      • 读取完make并构成图后,变量值已确定。
      • recipe中变量执行时展开。
    • 变量定义

      • 变量可包含除了: # =及空白字符以外的字符。
      • 常用为字母数字下划线。
      • 父子SHELL传递的变量需要考虑兼容。毕竟SHELL也会用到。
      • 大小写敏感。
      • 小写变量内部使用,大写变量用于父子传递。
    • 特殊变量

      • 点开头的保留为make内置变量。
      • 单字符的自变量和普通变量。
      • 自变量普通变量都是变量,都可以使用相同的方式引用。$(VAR),${VAR}
    • recipe中的变量

      • 自变量的解析略微不同。自变量一般是在recipe部分,执行时解析。
      • 也可以通过声明二次解析,让prerequisites也支持执行时解析。
  2. 变量引用

    • 类型

      • 分为三类${X},$(X),$X.
      • 前两个的变量名不限长,不带括号的只使用第一个字符作为变量名。
    • 美元符

      • 引用一个美元符普通是$$.在二次扩张prerequisites中引用需要$$$$
  3. 两类变量

    • 分类

      • 延迟扩张和立即扩张两种类型。
      • 前者可以延迟递归扩张容易出错,后者是立即扩张更精确。
    • 延迟扩张缺点一

      • OK=$(OK) -o这个使用的时候则会导致死循环。
      • 这种延迟扩张会等到使用的时候才演算值,用的时候会报错,这个可以看成没有尽头的递归。
    • 延迟扩张缺点二

      • 在每次使用内置函数的时候,每次都会执行调用。
      • 如果是通配符或者是SHELL这种。可能时时刻刻都在发生变化。
      • 每次都进行调用会降低效率。
    • 立即扩张

      • 两种格式都一样:=,::=,后者符合POSIX标准。
      • 在定义的时候值就已经确定了。没有所谓的引用。
      • 更快更准确。
    • 使用空表字符

      • 变量定义前置空白去除。
      • 后置空白也去除。
      • 中间空白合并成一个。
      • 可以使用在末尾添加注释的方式定义多个空白字符的变量。
      • aaa:=表示一个空白字符。
      • aaa:= # comment也是一个字符。
      • bbb:= $(aaa) #comment表示两个字符。
      • bbb:= /usr/bin $comment表示四个字符。
    • 条件定义

      • ?=的含义是,如果变量没有定义就定义,定义了就忽略。
      • ifeq ($(origin var),undefined)一样的效果。
  4. 灵活定义变量

    • 替换

      • $(var:a=b)表示将空格前的a,和最末尾的一个字符为a的最后一个字符a替换成b.
      • var=da ca bab sssa后的结果就是db cb bab sssb
      • ${var:a=b}一样的。
      • ${var:.o=.c}常用。
    • 更灵活的替换

      • 这个是借助patsubstr.更加灵活。
      • $(var:lib%.a=%.exe).这种将匹配到的原样替换。
    • 动态推演变量名

      • 比如$($(func) $(arg1),$(arg2))
      • 还比如$(var$(num)),$(DEBUG$(bits)).
      • $(var)_aaa = bbb也是可以的。
  5. 变量来源

    • 指令

      • 指令中的变量将会覆盖内置的所有的自定义。
      • 自定义就说明需要覆盖内部的值。
      • 不需要自定义就不需要传值。
    • 内部定义

      • 使用=普通定义。
      • 使用define定义多行指令。
    • 环境变量

      • shell环境变量。
      • 如果要添加自己的可以通过在shell中执行export添加。
    • 自变量

      • 执行时知晓。
    • 内置变量

      • 固定默认值。
  6. 变量定义

    • =递归扩张变量。
    • :=,::=立即扩张变量。
    • CURDIR,MAKE内置初始化变量。
    • $@执行时确定自变量。
    • ?=没定义变量则定义这个变量。
    • !=右边是脚本,值是执行结果,这个是递归变量,结果不能包括$,不然最好还是使用$(shell cmd).对于shell函数可以使用.SHELLSTATUS获取指令的执行结果。不过好像是版本原因!=没用。
  7. +=

    • 说明

      • 末尾添加。
    • 延迟扩张

      • 如果之前没有定义就是延迟扩张。
      • 如果之前定义了且是一个延迟扩张类型,就是延迟扩张。
    • 立即扩张

      • 之前定义了且是一个理解扩张类型,就是立即扩张。
    • 延迟和非延迟

      • 延迟会在真正使用的时候,也就是执行或者是在立即扩张的上下文下递归搜索。
      • 非延迟扩张会在定义变量前就计算好值。
      • 非延迟就是定义了变量后再填充对应的值,如果引用自身,就会造成死循环。不过只要不在立即扩张或者是执行中使用,就没有问题,使用了就出现死循环错误。
      • 立即扩张中使用延迟变量,如果有延迟变量还没有设置,那么就会出现空值。
      • VAR=$(VAR)+1等价于exp=maps.get(exp)再递归从左往右解析又解析自身,不停的死循环。
  8. 指令变量和重载

    • 前言

      • 从指令来的变量将会忽略定义的变量。
      • 使用override可以规避这种行为,毕竟有的指令可能是恶意行为。
      • 使用override可以规避恶意行为。
    • 三种格式

      • override var = value
      • override var := value
      • override var += value
    • 重复定义和忽略

      • 对于普通的都会忽略,不管在哪儿,都会忽略。
      • 只使用override的值。
    • 作用

      • 可以屏蔽用户传递的参数。
      • 也可以固定为用户添加参数。比如override var += -g
      • 因为提供了三种。递归扩张,立即扩张,末尾追加的方式。
      • 单行模式可能不顶用,还要有多行模式。
  9. 多行变量

    • 需要换行

      • 默认情况下是没有换行的,多行也需要用\\n的方式连接,而且还会被转意为空格。
      • 为了弥补这样的缺陷就通过指令define来定义真正意义的多行变量。
    • 多行变量

      • 多行变量的语法不同。
      • 但是定义的运算符都一样。=,:=,::=,?=,+=,!=
    • 用处

      • 可以定义recipe,真正的多行指令,可以看成自定义的.ONESHELL
      • 可以定义长变量,而不用每行末尾加\
      • 可以创建变量调用eval内置函数来动态生成rule和变量定义。
    • 格式

      • define var,名字可以包含函数变量引用等。
      • 先吃一个非空白字符,如果第一个是+=,:=这些运算符,则吃掉,按照对应类型解析,如果没有,默认使用=.所以复制运算符是可选。
      • 吃的第一个非空字符不是运算符,那么就是第一个值。直到一行仅仅包含endef位置。
    • 案例

      define varname
      asdfasdlfjsad
      asdfasjd  asdfas = adfaksdfja
      endef
      
    • 说明

    • 复制运算符为可选,这种除了支持真正的多行外,其他的和普通的变量赋值一样。
    • 这些都是值,即使里面有#开头的,也是值。
    • 具体是什么用,根据其扩张后的位置有关。
    • 说明

    • 这种也是支持override的,反正普通变量支持的这个都支持。
    • 不支持的这个也支持。
  10. 取消定义

    • 普通场景

      • 未定义的变量默认值为空。
    • 特殊场景

      • flavor函数和origin函数不一样。
      • undef真正意义上的取消定义。
      • override undefine name也是支持的。和定义相反。
  11. 环境变量

    • 优先级

      • default:export = env < inner < define < cmd < override
      • -e:inner < export = env < define < cmd < override
    • 陷阱

      • 默认没啥问题。
      • 如果使用-e需要注意会影响一些隐式规则。
      • 尽量不要使用-e,不安全,在哪儿使用都不安全。
    • 默认传递

      • 环境变量默认情况下总是在父子之间传递。
      • 也可以通过显示的export自定义的变量。
  12. 规则特定值

    • 默认

      • 除了自变量意外的变量都是全局变量。
      • 自变量属于局部变量。
    • 自定义局部变量

      • 复制应该单独的,target应该定义,所以如果要定义局部变量,最少要定义两条规则。
      • 可以使用export override private这些局部的。
      • 如何将prerequisites和变量定义混合呢?可以使用多次定义无recipe的方式。
      • 操作符可以是=,:=,::=,+=,?=.
      • prereuiqistes中定义仅仅是定义一个局部变量,和prerequisites无关。
      • 如果是多次定义也会按照局部处理逻辑来处理。规则也是一样的,也存在覆盖,递归扩张等。
      • 如果是-e也会适用。
    • 案例

      a.out: CFLAGS = -g
      a.out: a.cpp
         @gcc $(CFLAGS) $< -o $@
      
    • 须知

      • 一个prerequisites只计算一次。所以是通用。
      • targetprerequisitesrule也可以访问这个变量。
      • 一次make中,一个target只会执行一次。具体可以执行如下案例。
    • 案例

      .PHONY:all always
      all:a.cpp b.cpp
             @echo all
      a.cpp:OK=a.cpp
      a.cpp: a.h a.out
             @echo a.cpp
      b.cpp:OK=b.cpp
      b.cpp: a.h b.out
             @echo b.cpp
      a.h:always;
             @echo a.h:$(OK)
      a.out b.out:;
      always:;
      
    • 说明

      • a.hmake标记为最新了之后就不会再次执行。
      • 因为make一次只会执行一个target一次。
      • 所以OK的值和先后顺序有关。
    • 额外

      • 对于正则规则也可以使用类似的规则进行添加。
    • 案例二

      .PHONY:release debug
      release:CFLAGS=-o3
      release:a.out
             @echo release
      debug:CFLAGS=-g
      debug:a.out
             @echo debug
      a.out:a.cpp
             @g++ $(CFLAGS) $@ -o $< 
      
  13. 禁止传递

    • 概要

      • 前面讲述了,临时变量会传递给所有其他的prerequisites。子子孙孙的prerequisites.
      • 因此也提供了一种禁止的机制,仅仅当前rule可用。
    • 格式

      • 只需要声明的时候在变量前面添加private.
      • target:private var=value.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值