更新脚本怎么写

  1. recipe语法

    • 说明

      • 执行

        • 默认是一行行的执行.
        • .ONESHELL:,recipe一次性执行.
      • recipe脚本类型

        • 默认/bin/sh.
        • 修改SHELL为其他.
      • 解析案例

        .ONESHELL:
        .PHONY:all
        all:
               @for i in {1..9}
               do
                   echo $$i
               done
        
        • 定义.ONESHELL:和不定义是两个执行结果.
      • 脚本解析器

        .PHONY:all
        .ONESHELL:
        SHELL:= $(shell which python)
        all:
               @for i in range(9):
                   print(i)
        
        • 定义和不定义SHELL是两个结果.
      • 执行模型

        def exeRecipe(target):
           if not target.oneshell:
               return os.system("\n".join(target.recipes))
        
           for i in target.recipes:
               ret = os.system(i).returnValue
               if ret:
                   return ret
           return 0
        
    • 语义

      • 两类

        • makerecipe.
      • recipe

        • 正则
        recipe = "(?<=^%s)(.*)"%(value(".RECIPEPREFIX"))
        
        • 特殊情况target:preq;recipe.
      • rule上下文打断

        • 遇到变量定义或rule.
        • 行首是#,整行被忽略.
      • 条件定义

        .PHONY:all
        .ONESHELL:
        SHELL:= $(shell which python)
        all:
                  @  for i in range(9):
                   print(i);
        ifdef SHELL
                   print(i*i)
        endif
        
        • include,ifeq指令不影响上下文.
    • makerecipe的处理

      • 处理逻辑行

        • 任务
          • 处理recipe行尾的\.
        • 处理
          • \将多行算作一行处理,\\n原封不动传递过去.
          • \\n后是.RECIPEPREFIX,这个字符会被删除.但是空格不删除.
        • \shell
          • bash中,\会按照对应的情况处理.
          • ''保持原字符,""进行拼接转化.
        • recipe多行问题
          • 末尾\会保留.
          • 不想要可定义变量,变量不会留.
          • 根本原因是两个语义对\处理方式不一样.
      • 处理变量

        • 任务
          • 处理变量引用和函数引用.
        • 处理时机
          • 处理target的时候执行.
          • 不需要处理的可能永不扩张.
        • 注意
          • recipe中的变量引用还是shell中的变量引用.
          • shell中的变量引用用$$转义得到源字符.
  2. recipe输出

    • 说明

      • 默认

        • 执行前输出.
      • 规避

        • recipe行首定义@.
        • 传递给shell之前,行首的@会被make删除.
    • @使用

      • @使用场景

        • 本身就是输出,即echo xxxx.
      • @-n

        • @开头的也会输出.
      • @-s

        • 就像所有的recipe行首都添加了@.
  3. recipe执行

    • 说明

      • 执行

        • $(SHELL) $(SHELLFALGS) recipe
        • 执行一行调用一次.每行指令之间独立.
      • 问题

        • 频繁调用效率低.
        • 编写时错误预测指令效果.
      • 案例

        • cd xx在两行执行,工作目录不变.
        • 可使用&&拼接.
    • 一次性执行

      • 好处

        • 减少调用次数,提高效率.
        • 真正友好的多行.
      • 使用方式

        • makefile内任意位置定义.ONESHELL:.
      • 区别

        • 特殊符号@,-,+处理只处理第一行.
        • 注意makefile的特殊符号在SHELL中有特殊含义.
        • 错误处理不一样,之前是一行指令错误就判断,.ONESHELL只判断最后一行指令.使用.SHELLFLAGS+=e规避.
      • 特性

        • 已经定义了多个@,再定义.ONESHELL:也可以友好处理.
        • 只针对部分脚本.
        • 行首的特殊符号会被删掉.这样是避免尽量少的修改.
    • 修改脚本类型

      • recipe处理

        • $(SHELL) $(.SHELLFLAGS) recipe
        • 修改SHELL类型为其他就可以修改recipe的值.
        • 不同rule不同SHELL,可以通过局部变量的方式设置.
      • 案例

        .PHONY:all ok
        .ONESHELL:
        
        all:SHELL:=$(shell which bash)
        all:ok
               @echo ok
        
        ok:SHELL:=$(shell which python)
        ok:
               @import re
               print(re)
        
        
      • SHELL来源

        • SHELL不会从环境变量中继承,其他的都可以.
        • SHELL的值默认是/bin/sh.
        • 用户自定义了SHELL,如果不export是不会继承下去的.shell环境变量依然向下继承.
  4. 并行编译

    • 说明

      • 概念

        • 同时处理多个rule,同时执行多个recipe.
        • 充分的利用多核性能.
      • 声明

        • make -j[N] --jobs[=N]
        • 不声明默认是1线性处理.
        • N是数字表示最多同时处理N个.
        • N不是数字表示不限制.(大型项目编译,可能出现内存不够错误).
    • 递归调用与并行编译

      • 具体见后面章节.
    • 并行编译错误处理

      • 错误返回值

        • 执行某一条指令,返回值不为0,当前target剩下的recipe不执行.
        • 等待其他的正在执行的执行完后退出.
      • -k,--keep-going

        • 仅仅是停止发生错误的target.
        • 依赖的target也会停止最终生成.
        • 但是其他不相关的prerequisite都会继续执行编译.
        • 这种就是尽可能的编译所有.
    • 负载上限

      • 默认

        • 无上限
      • -l float

        • 设置负载值为对应的.
        • 表示最多处理CPU * float个.
    • 并行输出

      • 默认

        • 交叉输出.
        • 有时候难以阅读.
      • 自定义

        • 可以设置输出打包.即输出内容保存,然后一次性输出.
        • 有的可能不支持.版本太老了.
      • 格式

        • -Oxxxx,--output-sync=xxxx
      • 等级

        • none
          • 默认.
          • 交叉立即输出.
        • line
          • 执行完一条指令后输出.
          • recipe有多行,且没有声明.ONESHELL就可能交叉输出.
        • target
          • -O,--output-sync默认值.
          • 处理完target再输出.
        • recurse
          • 处理完一个make再输出.
          • 如果是target有子make,也会保存为一组再进行输出.
          • 而不是子make执行完后就输出.
          • 问题就是可能长时间没有输出.甚至内存消耗过大.
  5. 错误处理

    • 处理

      • 细节

        • 之前下一条recipe之前查看前一条是否正常退出.
        • 有错误就停下当前,并等待其他正在执行的退出.
      • 非错误

        • 得到非0返回值可能并不是错误.
        • 比如mkdir,但是已经存在.
    • 规避

      • 规避错误就退出

        • 行首添加-,针对某一行.
        • -i,--ignore-errors所有的recipe.
        • .IGNORE:没有任何的prerequisite就忽略所有.
        • 虽然能规避,但是可能不可避免的会输出对应的提示信息.
      • -k,--keep-going

        • 有直接或间接依赖关联的targetrecipe不再执行.
        • 没有关联的prerequisite还是会执行.
    • 问题

      • 分析

        • 中途失败可能导致部分编译,而且文件时间戳已经更新.
        • 下次再编译就会出错.
      • 规避

        • .DELETE_ON_ERROR:可以规避.
        • 这个target的依赖会比较前后时间戳,如果时间戳发生了变化,就会删除.
  6. 中断

    • 默认

      • 别中断就删除.
      • 删除判断依据:开始的时间戳和现在的时间戳是否一致.
    • 原因

      • 时间戳前后不一致,表示更新只进行了部分.
      • 数据不完整,就没有意义,可以删除.
    • 规避

      • 声明target.PRECIOUSprerequisite.
      • make处理前就会在这个里面查.
  7. 递归调用

    • 说明

      • 特征

        • rulerecipe中有make指令.
      • 分析

        • 编译子目录下的项目时使用.
      • 等价案例

        subsystem:
           cd subdir && $(MAKE)
        
        subsystem:
           $(MAKE) -C subdir
        
        • 一般来说,递归调用maketarget都是.PHONYprerequisite
    • -C

      • 说明

        • 最常见的递归调用方式.
        • $(make) -C subdir
      • 作用

        • 在启动的时候就生成一个CURDIR并赋值为当前工作目录.
        • 这个变量之后就没有什么用了,只是用于告知开发者当前所在位置.
        • 修改这个值对make没啥影响.
      • CURDIR优先级

        • makefile中定义的变量一样.
    • MAKE变量

        • 最上层shell中调用的可执行文件路径.
        • 在解析recipe的时候会特殊对待.
      • 注意

        • 递归调用建议使用$(MAKE)而不是make.
        • 简介的引用$(MAKE)是无效的,比如DMAKE=$(MAKE);$(DMAKE)是无用的.
      • 区别

        • 在使用-t,-n,-q的时候,包含$(MAKE)recipe会执行,make就不会执行.
        • 执行的好处就是可以递归向下传递.
        • 虽然make加行首+也可以得到同样的效果.
    • make之间参数传递

      • 指定上定义变量

        • make name=value,父子之间会继承.
      • 顶层make

        • 顶层make继承父进程shell的环境变量值.
        • 优先级:env < makefile.-e选项:env > makefile.
      • make

        • 继承父make的环境变量.执行recipe也会继承.
        • make可以通过export添加环境变量.
        • 优先级同上.
      • 变量传递

        • 环境变量,指令中声明.
        • 变量名只能是数字字母下划线.
        • export name[op value|names].
        • export传递所有.定义.EXPORT_ALL_VARIABLES:也传递所有,前者新版本,现在基本都支持;后者老版本,兼容性更好
        • 可以禁止传递部分变量.
        • 禁止传递unexport names
      • SHELL

        • SHELL变量值不会传递.
        • 最顶层的shell的值会传递为默认.
        • 可以使用export强制向下传递.
      • MAKEFLAGS

        • 始终传递.
      • MAKELEVEL

        • 0开始,输出递归深度.
        • 全局共享.一般用作条件判断.
        • 案例:
        .PHONY:all
        ifeq ($(MAKELEVEL),0)
        all:
               @echo $(MAKELEVEL)
               @$(MAKE) -f makefile
        else
        all:
               @echo ok
        endif
        
      • MAKEFILES

        • 让子Make预读一些makefile文件.
    • 选项参数部分传递

      • 传递方式

        • 借助变量MAKEFLAGS.
        • make -ks这个值就是ks.
      • 说明

        • 基本每一个子make都有这个环境变量.
      • 变量定义传递

        • 也是通过MAKEFLAGS,也是=格式.
        • 不想传递可以$(MAKE) -C subdir MAKEFLAGS=
      • 不传递的选项

        • -C,-f,o,W.
      • 并行编译-j

        • 系统支持,那么使用这个的父make可以和所有子make交互.
        • make确保最多N个同时处理,信号量.
        • 任何一个任务被标记为recursive都不会算在总的job里面.
        • 递归算在内那就没有真正执行的了.有可能递归很深就导致一个都不执行.
      • 值定义的值

        .PHONY:all
        ifeq ($(MAKELEVEL),0)
        all:
               @echo $(MAKELEVEL)
               @$(MAKE) -f makefile -b aa=bb
        else
        all:
               @echo ok
               @echo $(MAKEOVERRIDES): $(MAKEFLAGS)
        endif
        
        
        • 实际值定义位置在MAKEOVERRIDES.MAKEFLAGS实际是个值引用.
        • 不想传递值定义,仅仅传递选项就可以将这个的值设置有空.
        • 一般不会用,不过有的时候因为变量定义导致参数太长需要忽略参数定义.
    • --print-directory

      • 说明

        • 每次子make就会输出调用深度和位置.
      • 输出格式

        • 开始make[$(MAKELEVEL)]: enter$(CURDIR)
        • 结束make[$(MAKELEVEL)]: leave$(CURDIR)
      • 输出

        • -w,--print-directory
      • 禁止输出

        • -s,--no-print-directory
  8. 打包recipe

    • 说明

      • 分析

        • 某些指令在多个target中频繁使用.
        • 就可以用define打包指令,多行.
      • 格式

        • define开头,endef结尾.
        • 所有的都原样保留,变量不扩张.
        • 在真正扩张的位置再进行扩张.
    • 处理

      • recipe

        • 为每一条的行首都添加.RECIPEPREFIX
      • 特殊字符

        • @$(VAR)所有的指令都加@.
        • $(VAR)内部加了的才算.
        • 同理-+是一样.
      • 变量扩张

        • 递归扩张,即扩张变量.
        • 扩张内部.相当于扩张了至少两次.
  9. 空的rule

    • 特征

      • 没有prerequisite,没有recipe
    • 好处

      • 避免隐式搜索.
    • 坏处

      • 不存在就执行recipe,因为是空的.
      • 所以执行完了之后就认为target更新了.
    • 格式

      • target:;
    • 使用

      • .PHONY效果.
      • 可以让不可生成但是存在的target避免隐式搜索.一般是主make.cpp之类的.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值