makefile总览

  1. 五大元素

    • 显示规则

      • 用户定义
    • 隐式规则

      • make内置
    • 变量定义

      • 字符串表示字符串数组,方便统一修改.
    • 特殊指令

      • include
      • ifeq
      • define
    • 注释

      • 处理

        • 后面的字符都被忽略删除.
      • 原字符

        • \#
      • make

        • 行首字符不是.RECIPEPREFIX.
        • 行位字符是\表示下一行也是注释.
      • 变量和函数中

        • 字符处理
      • recipe

        • 原封不动传递.
      • define

        • 根据变量扩张位置决定.
      • make

        • make一行行读取解析.
        • 一行字符长度不限.
      • 问题

        • 太长难阅读,采用末尾\的方式多行拼接为一行
      • \\n

        • recipe外将会被转义为空格
        • recipe外规避,$ ,$\,变量解析为未定义,结果为空.var := one$ word;var :=one $\\nword
        • recipe中转义由SHELL决定.bash则是删除.
      • 多空格处理

        • 默认,连续多空格合并为一个空格.
        • 特殊,定义.POSIX:;特殊规则规避合并.
  2. 指定读取文件

    • 不指定

      • 默认搜索

        • GNUmakefile,makefile,Makefile
        • 从左往右一次搜索.
      • 推荐

        • makefile,Makefile.
        • GNUmakefile其他make不搜索.
      • 没搜到

        • make target将采用隐式规则进行处理.
        • 某些情况makefile并不是必须的;这种情况很少
    • 显式声明

      • 说明

        • 不采用默认搜索,自己指定.
      • 格式

        • make -f name | make --file=name
      • 特殊

        • 可以同时指定多个,make按照先后顺序拼接成一个后处理.
    • 指明target

      • 常规

        • make all;make clean;make target
      • 另类

        • make clean && make all等价于make clean all
        • 支持多参数顺序处理.
  3. include引用其他文件

    • 处理

      • 指令处理

        • 暂停下来读include声明的文件
      • 格式

        • include filenames; -include filenames; sinclude filenames
      • filenames

        • 可以是shell的文件扩张格式.
        • *.mk,*.sh
      • filenames空串

        • 不处理,不报错.
    • 注意

      • 空白处理

        • 行首字符不能是.RECIPEPREFIX的若干个空白字符被忽略.文件名之间的空白被合并为一个.
        • 行首字符是.RECIPEPREFIX被当成recipe处理.
      • 包含注释

        • 可以包涵#字符,后面的字符被忽略.
      • 变量或函数

        • 立即解析扩张并获取值
    • 使用场景

      • 公共定义

        • 很可能多个make或不同文件夹下的make共用一部分规则或变量.
      • 源文件的依赖

        • g++ -MM xx.cpp生成了xx.cpp的依赖.
        • 可以通过include来检测这些文件和依赖是否发生更新,然后是否需要重新编译.
    • 搜索

      • 路径

        • 工作路径
        • -I dir,--include-dir=dir的方式声明的一个或多个搜索路径
        • 默认prefix/include:(/usr/local/include,/usr/gnu/include,/usr/local/include,/usr/include)
      • 无法搜索

        • 先输出警告;
        • 继续读取,读完以后;
        • 再尝试搜索规则生成对应文件;
        • 没有规则可以生成也不存在则出错返回.
      • 说明

        • include的文件都会在读取完了之后尝试搜索规则更新或生成.
        • 也就是文件也是target.一般会定义这样一个规则.
        • 比如include xx.mk,这个xx.mk是由g++ -MM xx.cpp -o xx.mk生成.那么xx.mk就可以建立规则,依赖xx.cpp.
      • 安全模式

        • -include,sinclude最后无规则生成不报错.继续执行.
  4. MAKEFILES预读

    • 说明

      • 功能

        • make最先加载这个变量罗列的文件.
      • 特殊

        • 不从中选择默认规则.
        • 找不到也不报错.
        • 其他的和include一样.也会进行更新.
    • 使用场景

      • 递归

        • 父子之间传递数据.
        • 即让子make预读一些Makefile
      • 特殊

        • 不指定文件的方式,默认搜索也没有搜索到,这个里面的内容可以帮助修改一些变量.
        • 让隐式规则更好的处理传入的target.
      • 环境变量

        • 不太好,虽然上面的特殊情况会得到帮助.
        • 也不建议在顶层make设置。
        • 建议用include
  5. 文件也是target

    • 说明

      • 更新

        • make在读取和内联数据为图后,会对所有的makefile文件进行尝试更新.
      • 文件范围

        • 自身和include的文件等等.
      • 规则更新

        • 从已经构建好的关系依赖图中找,没有就使用隐式规则进行更新。
        • 如果有任意一个需要更新,进行了更新,就需要重新执行make.
      • 警告

        • 如果错误的定义,每次都会更新,这会出现死循环.
    • 特殊变量

      • MAKE_RESTARTS记录了重启了多少次.可以根据这个变量在内部限制深度.
    • 更新还是不更新

      • 无法更新

        • 无法更新的可以通过定义一个空的rule来规避隐式规则搜索.
        • 隐式规则搜索浪费时间.
      • 特殊规则

        • ::双冒号无prerequisite总是执行.
        • 但是在构建的时候如果有任何的makefile是这种规则,将会不纳入更新考虑范围内.
        • 因为这种会造成无限循环.
    • 不指定文件

      • 说明

        • 即默认搜索.
      • 更新

        • 无法读取也会使用隐式规则进行更新生成.
        • 无法生成不会出错,而是使用内置的隐式规则处理target.
      • 比较

        • 有参的make -f如果文件不存在会报错.
    • 更新特殊

      • make -n

        • 进行依赖检查,需要更新的仅仅输出recipe.
        • 使用$(MAKE)而不是make可以传递到子make.
        • 但是会进行文件更新.
      • 规避

        • 将涉及的文件都声明为target
        • make -f file -n files target.
  6. include冲突

    • 说明

      • 前言

        • 在同一个make中,一个target可以进行多次定义rule.
        • 多个rule只能有一个能定义recipe.
      • include

        • 可能带来两个都定义了一个rule,并定义了recipe.
        • 比如都有clean.
    • 规避

      • 策略

        • 采用父子的方式.
      • 案例

        foo:
           frobnicate > foo
        %: force
           @$(MAKE) -f Makefile $@
        force: ;
        
      • 说明

        • 使用%将其他的target都转发出去.
        • force用于强制执行,表示总是转发.
  7. make处理makefile

    • 处理的两个阶段

      • 数据解析

        • 读取所有的makefile文件.
        • 变量名和值内敛,规则之间形成依赖.
        • 最终形成targetprerequisites的有向关系图.
      • target处理

        • 根据有向关系图处理传入的target.
    • 变量扩张

      • 立即扩张

        • 需要马上使用变量或函数值.
      • 延迟扩张

        • 在处理target的时候或其他需要用的时候才扩张.
    • 扩张情况分析

      • include

        • include中使用变量或函数会立即扩张.
      • 变量赋值

        immediate = deferred
        immediate ?= deferred
        immediate := immediate
        immediate ::= immediate
        
        immediate += deferred or immediate
        
        immediate != immediate
        
        define immediate
        deferred
        endef
        
        define immediate =
        deferred
        endef
        
        define immediate ?=
        deferred
        endef
        
        define immediate :=
        immediate
        endef
        
        define immediate ::=
        immediate
        endef
        
        define immediate +=
        deferred or immediate
        endef
        
        define immediate !=
        immediate
        endef
        
        • +=要根据变量本身定义的时候是否为立即扩张,即是否是:=,::=定义的.
        • !=右边是shell指令.立即扩张,结果为变量值.
        • 变量名肯定是马上就要确认,所以都是立即扩张.
      • 条件指令

        • ifeq中的立即扩张.
        • 如果要在recipe中使用,建议使用shell对应的条件,因为recipe只有执行的时候才会处理.
      • 规则定义

        immediate : immediate ; deferred
           deferred
        
        • 所有规则的recipe都是延迟扩张.
        • 获取最终值,因为只有在处理target的时候才真正需要.
        • 后面介绍二次扩张的时候可以加深理解
    • 总结

      • 五大元素之四,除了注释,都已经介绍了.
  8. make解析流程

    • 说明

      • 解析是一行行的解析.
    • 流程

      • 读取一行数据逻辑行.
      • 如果是注释则删除.
      • 如果第一个字符是.RECIPEPREFIX而且当前处于rule上下文中,将这行数据添加到rulerecipe中.跳转到一.重新读取
      • 如果当前行内包含变量或函数引用,需要进行立即扩张,结合前面已经读取构建的图,对变量进行解析并获取结果.
      • 如果数据中第一个出现的是:=,则进行生成规则构建或变量构建.
      • 内联完后跳转到一,继续读取下一行数据.
    • 案例

      • 解析成规则

        myrule = target : ; echo built
        $(myrule)
        
        • 变量定义后扩张.
      • 对比

        define myrule
        target : 
           echo built
        endef
        $(myrule)
        
        • define定义的变量扩张后会去除换行符,也就没有了.RECIPEPREFIX.
        • 所以扩张后也是规则,但echo,builtprerequisite而不是recipe.
      • 合理使用define

        • 建议使用eval,进而调用解析器解析.而是不变量扩张,去除空白字符后的结果.
  9. 二次扩张

    • 说明

      • 针对对象

        • rule中的prerequisite.
        • 更准确的说是在prerequisite使用自动变量.
      • 自动变量

        • 啥样的:$@,$*,$?
        • 看起来和普通的变量没啥区别.
        • 根据上面的make解析makefile的流程,在解析内容前就需要进行变量和函数扩张.
        • 扩张是从环境中找对应值,一般没有定义,得到的是空.
        • 这些变量都是解析后的局部变量.
        • 即从已知的prerequisistes列表中进行查询.
      • 二次扩张

        • 在解析完了所有的之后,给prerequisite再次进行变量解析.
      • 二次扩张补充

        • 执行完了读取构造有向图后,所有的值都已确定.
        • 同样自动变量的值也知道了.
        • 在二次扩张的时候是可以看到这些变量的值的.
        • 也就是说,在二次扩张的时候可以使用这些变量了.
    • 二次扩张

      • 发生阶段

        • 完成有向图构建后,处理任务之前.
      • 使用

        • 在第一个需要使用二次扩张的rule前面定义特殊rule -- .SECONDEXPANSION:;
      • 处理

        • 第一阶段扩张完了之后,在末尾对prerequisite的变量引用进行扩张.即$(var) | $V.
        • 一般第一阶段后,都被解析为了对应的值,第二次不会再进行解析.
        • 如果要解析怎么做呢,先看下面的变量引用介绍和二次扩张案例分析.
      • 获取$字符

        • $$
        • 二次扩张是$$$$,后面分析.
      • 变量引用

        • 两种格式.
        • $(var)括号里面的varkey进行查表解析.
        • $var以第一字符vkey进行查表解析.
      • 案例分析

        .ONESHELL:
        .PHONY:all alla
        .SECONDEXPANSION:
        all:$$@a
           @echo this is $@
        alla:
           @echo this is $@
        
        • 根据make解析规则;
        • 读取数据all:$$@a
        • 不是注释,第一个字符也不是.RECIPEPREFIX,也不在rule上下文中.
        • 检测到需要扩张$$得到$字符,得到all:$@a.
        • 进行数据内联.继续读取.
        • 执行完了读取后,对.SECONDEXPANSION后的rule中的prerequisite再次扩张.
        • 执行二次扩张,处理$@a,查表获取$@的值,得到变量值为all,最终结果是alla.
        • 处理target,默认是all,处理allprerequisite: alla,执行alla的规则.
        • 得到结果this is alla\nthis is all.
      • 变体

        .ONESHELL:
        .PHONY:all alla
        temp=$$
        .SECONDEXPANSION:
        all:$(temp)@a
           @echo this is $@
        alla:
           @echo this is $@
        
        • 得到的结果和上面一样.
      • 为啥是四个$

        • 因为第一次解析后$$变成了$
        • 第二次解析就是$,即美元符号加空格,得到的是空.
        • 因此$$$$第一次解析是$$,第二次解析得到$.
    • 使用

      • 案例一

        .ONESHELL:
        .PHONY:a.out a.cpp
        .SECONDEXPANSION:
        a.out:$$(patsubst %.out,%.cpp,$$@)
           @echo this is $@
        a.cpp:
           @echo this is $@
        
        • 根据a.out获取a.cpp.
    • 变量扩张

      • 说明

        • 有的变量是延迟扩张,有的是立即扩张.
        • 如果延迟扩张类型变量在立即扩张环境中用会立即扩张.
        • 但是如果没有使用,则会在target解析的时候,执行recipe的时候再进行查询.
      • 问题

        • 根据扩张时机不同,得到的结果也就不同.
      • 案例

        .SECONDEXPANSION:
        AVAR = top
        onefile: $(AVAR)
        twofile: $$(AVAR)
        AVAR = bottom
        
        • 解析后的rule分别是onefile:top,twofile:bottom.
        • 因为查询的时候不一样,第一次查询是在已有的进行查询,第二次是最终值.
        • 第一次的值和最终值很可能是不一样的.
    • 自变量

      • 显式规则

        • 对于多ruletarget,他们的prerequisite会依次拼接.
        • 不同时机出现的自动变量查询到的结果不一样,因为他们都是向前查询.
        • 而且因为不会执行target处理,所以更新相关的特殊变量无法知晓值,$$?无法知道值.
        • 而因为显示规则非正则规则,所以也没有匹配的自动变量$$*无法知道值.
      • 显示规则案例

        foo: foo.1 bar.1 $$< $$^ $$+ # line #1
        foo: foo.2 bar.2 $$< $$^ $$+ # line #2
        foo: foo.3 bar.3 $$< $$^ $$+ # line #3
        
        • 第一次的$$<,$$^,$$+是空,因为还没有构建任何的rule.
        • 第二次的$$<,$$^,$$+从已有的rule中查,得到的分别是foo.1,foo.1 bar.1,foo.1 bar.1,然后将现有的prerequisite添加到依赖队列中.
        • 第二次的$$<,$$^,$$+从已有的rule中查,得到的分别是foo.1, foo.1 bar.1 foo.2 bar.2,foo.1 bar.1 foo.2 bar.2 foo.1 foo.1 bar.1 foo.1 bar.1,然后将现有的prerequisite添加到依赖队列中.
      • 正则规则

        • 正则规则知晓$$*的值,即匹配部分.
        • 但是$$?仍然无法知晓.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值