-
规则
-
前景
-
场景分析
leader
上传了代码,svn,git
下载了代码,然后重新编译生成可执行文件.- 过程简单,但是为什么会更新呢,更新哪些呢?
-
三个要素
- 目标:编译更新或生成的最终产物,
exe,lib,so,dll
都可能. - 依赖:用于生成
exe,lib,so,dll
的文件或文件夹. - 指令:需要执行什么指令来生成目标.
- 目标:编译更新或生成的最终产物,
-
开发
- 变量:尽量少的编辑,一改全改.
- 函数:重复使用的代码抽象成函数,功能简单.
-
make
的编译三要素target
:表示最终要生成更新的.prerequisite
:target
依赖的文件或文件夹,在生成前,需要准备好这些是最新的.recipe
:生成target
需要的指令.
-
make
与开发- 变量:有变量引用和函数引用.
- 函数:
rule
规则.
-
-
默认
target
-
语法解析
-
常见格式
targets : prerequisites recipe targets : prerequisites ; recipe recipe
-
targets
-
recipe
-
prerequisite
-
说明
target
的依赖文件或文件夹.用于判断target
是否需要更新.
-
$
符号- 常规扩张用
$$
表示. - 二次扩张用
$$$$
表示. - 参考
- 常规扩张用
-
逻辑行
- 可以用行尾添加
\
的方式将一个很长的行拆分成多行. - 但是意义不变.
- 可以用行尾添加
-
作用
- 挨个和
target
比较,任何一个比target
新,就说明target
需要重新生成. - 但是在处理
target
之前需要处理每一个prerequisite
,这些也要保证最新. - 也要执行同样的
rule
处理,每一个prerequisite
都会进行rule
处理. - 可能
makefile
中没有定义对应规则,但是还有内置隐式规则.
- 挨个和
-
比较方式
- 时间戳
last modify time
.
- 时间戳
-
-
模型
class Target: def __init__(self,name,prerequisite,recipe): self.name = name self.ltime = ltime(self.name) self.prerequisites = prerequisite; self.recipe = recipe def update(target) newer=False if not exists(target): target.ltime = 0 else: target.ltime = fileinfo(target.name).ltime for file in target.prerequisites: if update(file) < target.ltime: newer = True if newer: target.recipe() return timestamp() else: return 0
- 伪代码
-
-
-
两类
prerequisite
-
前言
-
使用
-
包含
order-only
的处理模型class Target: def __init__(self,name,prerequisite,order_only,recipe): self.name = name self.ltime = ltime(self.name) self.prerequisites = prerequisite; self.order_only = order_only self.recipe = recipe def update(target) newer=False if not exists(target): target.ltime = 0 else: target.ltime = fileinfo(target.name).ltime for file in target.prerequisites: if update(file) < target.ltime: newer = True for file in target.order_only: update(file) if newer: target.recipe() return timestamp() else: return 0
-
order-only
使用- 这类
prerequisite
一般都是用来保证生成前的某些东西是存在的,或者生成前执行某些操作. - 比如生成前
Bin
目录要存在,不存在就创建.
- 这类
-
格式
targets : normal-prerequisites | order-only-prerequisites
-
格式说明
- 左边是普通的,右边是
order-only
的. - 两边都有,则以左边为准,左边的已经包含了更新和存在检测,是
order-only
的超集. - 两个分别在不同的处理集合中.
- 处理顺序是:先处理
normal
再处理order-only
.order-only
与normal
交叉出现也是normal
先处理.
- 左边是普通的,右边是
-
-
-
通配符
-
prerequisite
搜索-
前景
-
VPATH
-
vpath
-
说明
- 和
VPATH
类似,但是这个更加的灵活. - 按照匹配规则进行搜索,文件夹也更加灵活.
- 和
-
格式
vpath pattern directories vpath pattern vpath
-
格式说明
- 第一个是定义,路径格式和
VPATH
相同. - 第二个是取消定义.
- 第三个是取消所有定义.
pattern
用%
当通配符,匹配零或多个字符.pattern
原字符表示\%
.- 定义了多个同样的
pattern
则路径值是拼接在一起的.而不是替换.
- 第一个是定义,路径格式和
-
搜索
- 当前路径不存在,就搜索
vpath
. vpath
按照定义的匹配规则进行匹配,然后用定义的路径进行搜索.- 如果多个规则可以匹配,则按照先后顺序进行依此搜索.
- 当前路径不存在,就搜索
-
案例分析
vpath %.c foo vpath % blish vpath %.c bar
%.c
重复,%.c
的路径有foo bar
%
表示所有.- 搜索
xx.c
则是按照foo,bar,blish
依此搜索.
-
-
搜索处理
-
处理算法
prerequisite
先在当前目录为工作目录下搜索.搜不到再进行目录搜索.- 搜到了,搜索路径暂时存放,作一个为
target
. - 然后保存的路径作为
target
的prerequisite
以同样的方式进行处理. - 执行完了搜索并处理更新,就需要判断是否需要执行
recipe
. - 如果
prerequisite
要执行更新,仅仅保留原样,不保存完整路径. - 如果
prerequisite
不用执行更新,源recipe
中的prerequisite
使用搜索后的路径.
-
特殊
- 使用
GPATH=xxx
声明可以保留完整路径,不管是否是需要更新,但是可能有的make
不支持.
- 使用
-
说明
- 对
prerequisite
的处理步骤是. - 先进行当前文件夹搜索文件.
- 搜不到再搜索显式或隐式规则进行生成.
- 再进行
vpath
进行搜索,尝试其他文件夹. - 再搜索
VPATH
.
- 对
-
-
总结
-
recipe
怎么处理 -
lib,so
库搜索
-
-
PHONY
-
没有
prerequisite,recipe
的rule
-
特殊的
target
-
.PHONY
prerequisite
是特殊的target
,这类型的target
总是执行prerequisite
.- 这类通常可以看作是指令.
-
.DEFAULT
- 任何没有匹配的
rule(隐式和显式规则)
,就总是执行这个的recipe
.
- 任何没有匹配的
-
.PRECIOUS
- 这个的
prerequisite
作为target
编译的时候被打断不会被删除. - 中间文件也不会被删除.
- 隐式规则相关
.INTERMEDIATE
,.SECONDARY
.
- 这个的
-
.SECONDEXPANSION
- 后面的所有
rule
的prerequisite
还会进行第二次解析.
- 后面的所有
-
.DELETE_ON_ERROR
- 声明了,这个
make
执行的时候,如果recipe
的返回值有错误,则进行删除操作. - 就像是被信号中断一样.
- 声明了,这个
-
.EXPORT_ALL_VARIABLES
- 将所有的变量都传递给子
make
。 - 即所有的变量都
export
.
- 将所有的变量都传递给子
-
.NOTPARALLEL
- 当前的
make
是线性. - 不影响子
make
.
- 当前的
-
.ONESHELL
- 指令不再是一行行的传递.
- 而是所有的一起传递.这种有时候更友好,效率更高.
-
.POSIX
- 执行
recipe
时,遇到错误返回值,就立即停止执行.
- 执行
-
-
一个规则多个
target
-
一个
target
多个rule
-
说明
-
效果
- 多次定义
target
,相当于将target
的多个prerequisite
按照顺序拼接在一起. - 有点类似用
\
分成了多行,只是多地方定义更加的灵活.
- 多次定义
-
prerequisite
处理- 拼接的方式有点类似二位数组.为什么说时二维而不是一维呢?
- 因为自变量处理的时候会不一样.
-
模型
def expandAuto(target): ... plen = len(target.prerequisite) for i in range(plen): temp = [] for file in target.prerequisite[i]: if file.name == "$+": temp.append(target.prerequisite[:i]) else: temp.append(file) target.prerequisite[i] = temp ...
-
多
rule
处理- 多个
target
的rule
定义只能有一个recipe
. - 如果有多个以最后一个为准,同时输出错误信息.
- 多个
-
特殊
.
开头的特殊target
可以有多个recipe
target::
格式的则是多个独立的定义处理.可以存在多个recipe
.
-
-
使用
-
总结
- 使得对
target
的prerequisite
更加的灵活. - 和指令
include,ifeq,define
等结合使用很有效果.
- 使得对
-
-
静态匹配规则
-
自动生成
rule
-
说明
-
gcc
选项-
注意
- 不要和其他的混用,比如编译混用.
- 这个指令选项专门用来做依赖查看的.
-
-M
-
-MM
- 和上面一样,但是不包含系统级别的头文件.
- 也就是说全是用户自定义的头文件.
include "self/defined/headers"
-
-MF file
- 和
-MM,-M
一起使用,将结果写入指定位置file
处.没有这个选项,-MM,-M
和与处理结果一起输出. - 和
-MD,-MMD
一起使用,修改默认输出位置. file
如果是-
,则输出到标准输出stdout
.
- 和
-
-MP
- 为除了输入文件以外的输入文件的依赖文件都声明一个
rule
- 比如
test.o: test.c test.h;
和test.h:;
两个rule
. - 这种有时候友好,但是这种会带来问题.
- 为除了输入文件以外的输入文件的依赖文件都声明一个
-
-MT target
- 修改
rule
中的target
. - 默认是只保留文件名并修改后缀为
.o
.这种就可以修改target
. - 一般是添加路径前缀或者是添加自身的
.mk
适用同样的规则.因为依赖的文件变了很可能导致主文件的依赖发生改变. - 即
xx.o xx.mk:prerequisite
,然后在主makefile
中声明隐式规则处理. .mk
的内容和prerequisite
也息息相关.
- 修改
-
-MQ target
- 和
-MT
一样,不过是原样输出,即保留字符串. - 比如
-MQ '$(obj)xx.o'
得到的target
就是$$(obj)xx.o
- 即
Q
就是quotes
的意思.
- 和
-
-MD
- 等价于
-MF -MF file
. - 但是输出文件名需要用
-o
声明输出文件. - 默认是输入文件保留文件名后替换后缀为
.d
.
- 等价于
-
-MMD
-MD
的无系统文件版本.
-
注意
- 输出比如
c,cpp
,rule.target
可能和源文件一样.
- 输出比如
-
-