二、写规则(Rules)

这篇具有很好参考价值的文章主要介绍了二、写规则(Rules)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

规则没有先后顺序

一般来说规则的顺序是没有先后的,除了默认target的规则。make会将第一个makefile里面的第一条规则的第一个target作为默认的target。所以,默认target的规则应该放在最前面,一般使用all作为默认target的名称。

规则的声明

第一种格式,将recipe从新行开始写,如下:

targets : prerequisites
	recipe
	...

第二种格式,从prerequisites列表后面开始写recipes,用[ ; ]隔开,如下:

targets : prerequisites ; recipe
	recipe
	...

target可以有多个,使用空格分开,通常来说一个规则里面只有一个target,也不排除有其它特殊的情况(比如后面要讲的使用了通配符,或者静态模板规则)。

规则里面可以$来引用变量,如果只是想单纯的使用$符号,必须使用$$,因为targets和prerequisites都会在read-in阶段被展开,一个$的话会被直接当作变量引用进行展开。如果使用.SECONDEXPANSION开启了prerequisites的二次展开,而你又想在prerequisites部分使用$符号,则应该使用$$$$(第一次展开去掉第一个,第三个$,第二次展开去掉第二个$,最后剩下一个单纯的$符号)。

在make的第二阶段,make会对target的prerequisites列表里面的项逐个进行检查,看是否需要更新target。如果某个target不存在,或者某些prerequisites有更新的话,则该target会被生成或更新,这样的presiquisites叫做常规presiquisites,它的更新会导致依赖它的target也会被更新。
但是有时候,我们仅仅是希望某些prerequisites存在即可,不希望它们影响到target的更新。比如说像目录这样的prerequisites,我们可能希望运行target的recipe之前存在某个目录以方便存放一些临时文件,但是这个目录是否更新我们并不关心,我们只是希望它存在即可。这样的prerequisites叫做Order-only prerequisites,检查target的时候,其只参与存在与否的检测,不参与是否比target更新的检测。

在prerequisites列表中,使用[ | ]来分割常规prerequisites和Order-only prerequisites。[ | ]左边的为常规类型,右边的为Order-only类型:

targets : normal-prerequisites | order-only-prerequisites

使用通配符

我们可以在规则里面使用通配符,使用通配符得到的结果是经过排序的。make里面的通配符是[ * ],[ ? ]和[ … ]。[ ~ ]也有特殊含义,表示用户的家目录。

targets和prerequisites部分的通配符展开是由make来负责的,recipe部分的通配符展开由shell负责。其它地方应通过wildcard函数来使用通配符,不然通配符会被当作一个普通的字符处理。如果想在Rule里面单纯使用[ * ]符号,则应使用转义[ \ ]进行转义,比如foo\*bar

使用通配符要注意的地方

当通配符没有配到到任何结果时,则返回它本身,比如*.o,如果查找目录下没有.o文件,则*.o的结果就是"*.o",这通常并不是我们想要的结果。我们可以通过一些更成熟的方法来避免这样的问题,比如说使用wildcard函数和字符串替换,这些方法后面再进行学习。

使用wildcard函数

前面讲了Rule里面的通配符可以直接由make或shell展开,但是其它部分的通配符(比如赋值部分出现的通配符,或者函数参数里面的通配符)并不能自动展开,需要借助wildcard函数,其使用形式如下:

$(wildcard pattern...)

匹配到的条目排序后以空格分开,如果没有匹配到任何条目,则函数直接被忽略,不会返回任何东西。

VPATH变量,指定Prerequisites的查找路径

make的查找路径默认为Makefile所在的目录,通过使用VPATH变量,可以设置其它查找目录(目录使用冒号或空格分开),查找顺序是当前目录 > VPATH指定的路径,找到prerequisite后则停止查找。这样,我们可以将prerequisites集中放在几个目录里面,通过VPATH变量来指定这些目录的路径,就不需要在Rule里带上具体的路径了。

实际上,make也会通过VPATH去查找target。

使用vpath指令设置匹配文件的查找路径

vpath directive比VPATH变量更进一步,它可以以文件名匹配的方式设置查找路径,其使用方式如下:

vpath pattern directories

pattern是要匹配的文件名,使用[ % ]来匹配零个或多个任意字符。directories是设置的查找目录,用冒号或空格隔开。查找顺序为当前目录 > 设置的目录,比如有如下的makefile片段:

vpath %.c foo:bar
vpath % blish

对于当前目录下不存在的.c文件,会依次去foo bar blish目录下查找。对于当前目录下不存在的其它文件,则会再去blish目录下查找。

vpath directive还有其它两种使用方式如下:

vpath pattern

表示清除掉使用vpath为pattern设置的查找路径。

vpath

表示清除掉所有通过vpath设置的查找路径。

make是如何使用搜索目录的?

前面两部分说了可以通过VPATH变量或vpath directive来指定target和prerequisites的额外查找路径。对于target文件来说,还存在一个问题就是是否确实要使用查找到的路径。当我们在额外路径下面找到了target文件时,如果发现其是最新的,不需要重构,那么会直接将它作为其它target文件的prerequisite。如果它需要进行重构,那么make会抛弃找到它的路径,优先使用Rule里面指定的路径(也就是会在Rule里指定的路径下生成新的target)。
可以通过GPATH变量来为指定目录定制这种行为,通过GPATH变量指定的目录,作为被查找到的路径,会被保留使用(新的target会生成在查找到的目录里面)。

当我们在makefile里面使用了目录搜索的功能时,需要小心编写Rule里面的Recipe,以便shell能够通过正确的路径找到文件。这可以通过使用自动变量来实现,自动变量会包含文件所在的具体路径。比如,有如下的makefile片段:

VPATH = src:../headers
foo.o : foo.c defs.h hack.h
cc -c $(CFLAGS) $< -o $@

如果在src目录下找到了foo.c,则$<src/foo.c;如果在../headers目录下找到了foo.c,则$<../headers/foo.c。相对路径都是以makefile所在目录为起点。

隐式规则里面的目录查找

目录查找也适用于隐式规则,比如对于foo.o目标的隐式规则,会使用foo.c来生成foo.o。当前目录下没有foo.c的时候,就会去vpath和VPATH指定的目录下去查找foo.c。隐式规则的Recipe里面使用的通常都是自动变量,所以能正确引用foo.c的路径。

链接库的目录查找

当prerequisites列表里面使用-lname的形式指定了依赖库的时候,也会对其进行目录查找,优先使用动态库libname.so,然后是静态库libname.a。查找顺序是当前目录 > vpath指定的目录 > VPATH指定的目录 > 系统库目录(/lib, /usr/lib, and /usr/local/lib)。

伪目标(Phony Targets)

伪目标并不对应一个真正的文件,其只是用于明确告诉make去执行一个规则,而不是创建伪目标这个文件。比如有如下的伪目标定义:

clean:
	rm *.o temp

一般来说,目录下不会存在与伪目标同名的clean文件,所以每次去make伪目标的时候,对应的Recipies都会被运行。但是当目录下确实存在一个同名文件的时候,这个规则就不能得到预期的效果,因为规则里没有presiquisites,所以target总是最新的,因此recipe总是得不到运行。为了避免这样的问题,我们可以明确指定clean为一个Phony Target,方法是将clean作为.PHONY这个特殊目标的依赖,如下:

.PHONY: clean
clean:
rm *.o temp

这样,每次make clean的时候,不管是否存在clean这个文件,规则里面的recpies都会运行。

.PHONY目标的依赖都是直接被当作文件名,不进行通配展开。
make不会对伪目标进行隐式规则匹配。
伪目标不应该作为其它真实目标的prerequisite,不然目标总是会被更新。
伪目标可以包含prerequisites列表,比如我们常用的all:

all : prog1 prog2 prog3
.PHONY : all
prog1 : prog1.o utils.o
cc -o prog1 prog1.o utils.o
prog2 : prog2.o
cc -o prog2 prog2.o
prog3 : prog3.o sort.o utils.o
cc -o prog3 prog3.o sort.o utils.o

这样,我们可以使用一个makefile编译出3个程序的可执行文件。

没有Prerequisites或Recipes的规则

如果这种规则的target不是一个存在的文件,则make将Rule的运行等同于target的更新。这样的话,任何将这个target当作prerequisite的target的Recipies总是会被运行。比如,有如下的makefile片段:

clean: FORCE
	rm $(objects)
	
FORCE:

对于目标FORCE的Rule,没有prerequisites和recipes,并且FORCE也并不是一个文件(可以近似看作一个伪目标,只是没有用.PHONY声明)。当运行make clean的时候,去检查其依赖FORCE。因为FORCE不存在,并且规则里面也没有对应的Recipes去生成它,所以make直接将其视为更新状态。这样,clean也会被视为需要进行更新,其Recipes会被执行。通常用这种方法对某个目标进行强制更新;或者强制运行某个Rule里面的Recipes(这种情况跟使用.PHONY的方法效果相同,对于一些不支持.PHONY的make程序,就使用FORCE来实现相同的效果)。

使用空目标文件来记录事件

有时候一个规则不是为了生成一个具体的target文件,而是为了执行一组命令,比如打印改变了的文件。这样,我们需要记录上次打印的时间,以免重复打印,可以专门将target作为记录打印事件的用途。假设有如下的makefile片段:

print: foo.c bar.c
	lpr -p $?
	touch print

这条规则用于在foo.c或bar.c被更新的时候打印对应的文件。print并不是一个规则需要生成的target文件,其只是在打印后被更新,用于表示更新的文件已经被打印过了,下次再运行make print便不会重复打印。

一条规则里面出现多个target

一条规则里面出现多个target的时候,make以两种不同的方式对待这些target:把它们当作相互独立的target或将它们当作成组的target。

当作独立的target是指相当于将每个target都拆分成相同的规则,有同样的prerequisites列表和recipes,自动变量$@用于表示当前的target。

当作成组的target是指这些target都是在规则的recipes里同时生成的,此时自动变量$@表示的是触发Recipies被执行的那个target。成组的target总是会同时更新,哪怕只有一个target过期,其它的target也会被一起更新。

targets : prerequisites表示targets是相互独立的。
targets &: prerequisites表示targets是成组的。
[ &:: ]用于在多个组里面包含同一个target文件,这种情况就不讨论了。

多条规则对应同一个target文件

一个文件可以作为多条规则的目标文件,这个目标对应的所有规则里面的所有prerequisites文件会合并成一个prerequisites列表来作为这个target的prerequisites,但是只能运行一个recipe来更新这个target。当有一个以上的规则里面都有recipe的时候,make会使用最后一条规则的recipe并打印一个错误消息(对于使用[ . ]开头的target,不会打印这个错误)。如果这些规则里面都没有recipe,则尝试使用合适的隐式规则来更新target。

通常来说,将多条规则对应同一个target文件是为了方便分多次为target添加prerequisites文件,recipe可以写在任意一条规则里面,比如下面的例子:

objects = foo.o bar.o
foo.o : defs.h
bar.o : defs.h test.h
$(objects) : config.h

config.h同时作为foo.o和bar.o的依赖,使用一条规则同时把它添加到foo.o和bar.o的prerequisites列表里面。

静态模板规则(Static Pattern Rules)

前面讲的规则都是普通规则(包括显式规则和隐式规则),在一条普通规则里面,可以有多个target文件,这些target文件共享规则的prerequisites列表。因为这个缘故,当某些targets有部分共享的prerequisites时,可以通过这种方式将这些targets写在一个规则里面。但是对于其它不是共享的prerequisites,则需要为target再单独增加规则进行指定,这就出现一个target会有多条规则的情况。

出现这个情况的根本原因在于将多个target写在一个规则里面时,它们会共享规则的prerequisites列表(哪怕这些target其实并不依赖于某些prerequisites)。静态模板规则就是为了解决这个问题而提出来的,我们可以将多个target文件写在一条静态模板规则里面,但是prerequisites列表不是写死的,而是使用静态模板匹配的方式分别生成这些targets的prerequisites列表。静态模板规则的书写格式如下:

targets ...: target-pattern: prereq-patterns ...
	recipe
	...

targets列表中的每个target文件都会与target-pattern模板进行匹配(对于没有进行匹配的target,会给出一个警告信息),匹配符是[ % ],target文件名中被[ % ]匹配到的部分叫做主干(stem)。通常来说,pattern中只有一个匹配符。主干替换掉prereq-patterns模板里面的[ % ]就得到这个target文件的prerequisites列表。

从静态模板的匹配方式可以看出,虽然可以在一个静态模板规则里面为不同的targets生成不同的prerequisites列表,但是这些targets文件的名称必须相似,否则无法使用target模板来做主干提取;这些prerequisites文件的名称也必须相似,否则无法使用prereq模板来做主干替换。

在prereq-patterns里面直接写死prerequisite文件名也是合法的,这个文件会成为所有target的prerequisite。

假设有如下的makefile片段:

objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
	$(CC) -c $(CFLAGS) $< -o $@

对于foo.o,[ % ]匹配到的主干是"foo",因此其prerequisites列表是foo.c;对于bar.o,[ % ]匹配到的主干是"bar",因此其prerequisites列表是bar.c;

假设有如下的makefile片段:

bigoutput littleoutput : %output : text.g
	generate text.g -$* > $@

这里text.g会同时作为bigoutput和littleoutputd的prerequisite文件,recipe中的自动变量$*表示匹配到的主干,对于bigoutput目标,$* = “big”;对于littleoutput目标,$* = “little”。

隐式规则和静态模板规则的比较

这两种规则有点类似,都是通过使用模板匹配的方式,通过target文件名来生成对应的prerequisites文件列表的,它们的区别在于make怎么决定什么时候应用规则。

当没有为target指定recipe,并且target的prerequisites文件可以找到时,才会应用隐式规则。如果有多条匹配的隐式规则可用时,只会按顺序选择一条规则来应用。
而静态模板规则会直接应用到指定的targets,如果有两条规则冲突,并且都指定了recipe,会报错。

一般来说,静态模板规则更优于隐式规则,因为我们可以在规则里面指定匹配方式并且可以直接给出recipe。而隐式规则对我们来说其实并不具体(特别是我们不太清楚目录下有些什么东西时),我们不知道它具体匹配到了什么prerequisites,使用了什么recipe,这些不确定性因素的引入可能会埋下一些潜在的隐患。文章来源地址https://www.toymoban.com/news/detail-775596.html

到了这里,关于二、写规则(Rules)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ElementUI 表单 rules 规则

    ElementUI组件库中表单校验默认使用的是async-validator,所以要了解ElementUI表单验证的rules规则,先了解async-validator type :验证数据类型 支持的类型如下,默认类型为string string 值必须是 String 类型,这是默认值 number 值必须是 String 类型,包含整数和小数 integer 值必须是 Number 和整

    2024年02月10日
    浏览(34)
  • 二、写规则(Rules)

    规则没有先后顺序 一般来说规则的顺序是没有先后的,除了默认target的规则。make会将第一个makefile里面的第一条规则的第一个target作为默认的target。所以,默认target的规则应该放在最前面,一般使用all作为默认target的名称。 规则的声明 第一种格式,将recipe从新行开始写,如

    2024年02月03日
    浏览(28)
  • Vue rules校验规则详解

    当我们在开发Vue应用时,经常需要对表单进行校验,以确保用户输入的数据符合预期。Vue提供了一个强大的校验规则机制,通过定义rules规则,可以方便地对表单进行验证,并给出相应的错误提示。 在Vue的rules中,我们可以使用预定义的校验规则,如 required 、 type 、 min 、

    2024年02月04日
    浏览(40)
  • Prometheus-Rules(规则)

    Prometheus规则是一种逻辑表达式,可用于定义有关监控数据的逻辑关系和约束条件。这些规则可以用于告警条件、聚合和转换等。 普罗米修斯支持两种类型的规则,可以对其进行配置,然后定期进行评估: recording rules alerting rules。 要在 Prometheus 中使用规则,请创建一个包含所

    2024年02月10日
    浏览(35)
  • MISRA 2012学习笔记(5)-Rules 8.10

    8.10 基本类型模型(The essential type model) 8.10.1 原理 本节中的规则共同定义了基本类型模型,并对C类型系统进行了约束,以便: 支持更强大的类型检查系统; 为定义控制隐式和显式类型转换使用的规则提供合理的基础; 推广可移植编码做法; 解决一些在ISO C中发现的类型转换异常

    2024年02月02日
    浏览(36)
  • MISRA 2012学习笔记(2)-Rules 8.1-3

    8.1标准的C环境 Rule 1.1 程序不得违反标准 C 语法和约束,并且不得超出具体实现的编译限制 等级:必要 分析:可判定,单一编译单元 适用:C90,C99 展开:程序只能使用选定的标准版本(参见第3.1节)中指定的C语言及其库的那些特性。 该标准允许实现提供语言扩展,并且该规则允

    2024年02月13日
    浏览(35)
  • Easy Rules规则引擎(2-细节篇)

    在 Easy Rules规则引擎(1-基础篇) 中我们已经简单介绍了 Easy Rules 规则引擎的使用示例,这节我们详解介绍一下规则引擎的相关参数配置实例还有组合规则。 Easy Rules 规则引擎支持下面参数配置: 参数名称 参数类型 必选 默认值 rulePriorityThreshold int 否 Integer.MAX_VALUE skipOnFirst

    2024年02月11日
    浏览(41)
  • Prometheus-Rules(规则)-基础语法

    Prometheus规则是一种逻辑表达式,可用于定义有关监控数据的逻辑关系和约束条件。这些规则可以用于告警条件、聚合和转换等。 普罗米修斯支持两种类型的规则,可以对其进行配置,然后定期进行评估: recording rules alerting rules。 要在 Prometheus 中使用规则,请创建一个包含所

    2024年02月08日
    浏览(37)
  • Vue中常用的rules校验规则

    2024年02月03日
    浏览(37)
  • Easy Rules规则引擎(1-基础篇)

    最近团队在做一些 Visa 、 Master 卡的交易风控,运营团队提供了一些交易风控的规则,比如针对卡号MCC设置单笔交易限额,24小时交易限额,72小时交易限额等等,还有触发风控规则是否拦截交易还是只发告警邮件等等等。 虽然写各种条件判断也能实现,但是随着后面规则增加

    2024年02月12日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包