[Bazel]自定义命令行编译标志
1 名词
名词 |
释义 |
---|---|
aspect |
将自定义行为附加到规则的逻辑包。这与配置相似,但不同的是 aspect 不会更改原始规则。 |
build flag |
构建标志,设置配置的命令行标志,比如 --cpu,它好比 key-value 的 key。根据定义,用户可以直接在任何构建上进行设置。 |
build setting(configuration setting) |
构建设置,是一条配置信息。可以认为配置为 key-value 映射。构建标志产生构建设置,但是可以通过其他方式(例如通过transitions)来设置构建设置。没有附带标志的构建设置对用户不可见。规则设计者可以利用它,例如使规则在其依赖项上设置隐式属性。 |
transition |
表示跨依赖项边缘的配置转换。即可以实现读入一组构建设置,并输出一组构建设置。 |
provider |
简单值对象的构造函数,称为提供 provider 实例 |
这里名词
只做个索引,方便理解,可能现在反而让理解变得更加复杂,不过没关系,我们主要是实现的就是自定义 build flag
。更多参见这里[1]。
2 背景
Starlark Configurations
是 Bazel 的 API,用于自定义项目的构建方式。使用 Starlark Configurations
可以让你:
- 定义项目自己的编译标志,而不再需要
--define
- 对于规则,可以实现默认的编译配置
- 不像传统的
--cpu
、--copt
、--compilation_mode=
(-c
) 等方式,是 Bazel 版本内置,而用户自定义的编译设置可以在.bzl
文件中实现,不需要重新编译 Bazel 源码就可以实现
我们最终实现:
$ bazel build //my:binary --some_feature_specific_to_my_app=fancy_mode
或者 some_feature_specific_to_my_app
内部默认配置:
$ bazel build //my:binary
3 自定义构建设置的定义
我们可以自定义构建标志以及规范它的构建设置,即比如我们定义了构建设置 week
规则,它可能的值只能是星期{1,2,3,4,5,6,7}
。当然,简单的,我们可以定义构建设置,只限制值类型,而不限制值内容。
构建设置相关的规则跟其他规则定义差不多,区别就是看有没有 build_setting
属性。示例:
# 定义构建设置规则 string_flag
string_flag = rule(
implementation = _string_impl,
build_setting = config.string(flag = True)
)
config
规定了该构建设置规则的值类型为 string
类型,还可以设置 int
、bool
、string_list
类型[2]。flag = True
表示该构建设置能够允许用户在命令行上设置,否则的话只能由规则编写者在内部默认设置或者通过 transitions
设置。
4 自定义构建设置的实现和实例化
同我们之前文章介绍的自定义规则一样,自定义构建设置规则也需要有实现,即 implementation = _string_impl
中 _string_impl
函数。我们可以在 _string_impl
函数中通过 ctx.build_setting_value
获取构建标志的值:
def _string_impl(ctx):
# do something...
value = ctx.build_setting_value
# do something...
比如前面说的利用 string_flag
实现一个构建设置目标 week
,需要对 week
的值做约束,那么需要在 _string_impl
里做检测,如果不匹配,则提示错误:
BuildSettingInfo = provider(
doc = "A singleton provider that contains the raw value of a build setting",
fields = {
"value": "The value of the build setting in the current configuration. " +
"This value may come from the command line or an upstream transition, " +
"or else it will be the build setting's default.",
},
)
def _string_impl(ctx):
allowed_values = ctx.attr.values
value = ctx.build_setting_value
if len(allowed_values) == 0 or value in ctx.attr.values:
return BuildSettingInfo(value = value)
else:
fail("Error setting " + str(ctx.label) + ": invalid value '" + value + "'. Allowed values are " + str(allowed_values))
string_flag = rule(
implementation = _string_impl,
build_setting = config.string(flag = True),
attrs = {
"values": attr.string_list(
doc = "The list of allowed values for this setting. An error is raised if any other value is given.",
),
},
doc = "A string-typed build setting that can be set on the command line",
)
实例化 week 构建设置:
# BUILD file
string_flag(
name = "week",
build_setting_default = "1",
values = [
"1",
"2",
"3",
"4",
"5",
"6",
"7"
]
)
build_setting_default
是内置保留属性,表示对构建设置的默认值设置。
完成 week
构建设置的定义(实例化),可以通过命令行传入值设置:
$ bazel build :week --//:week=2
DEBUG: /home/biedamingming/workspace/bazel/build_setting_test/deps.bzl:13:10: allowed_values = ["1", "2", "3", "4", "5", "6", "7"]
DEBUG: /home/biedamingming/workspace/bazel/build_setting_test/deps.bzl:14:10: value = 2
如果我们设定的值不是 {1, 2, 3, 4, 5, 6, 7}
,则会提示错误。当然,如果你定义 week
目标(构建设置)的时候,不设置 values
属性,则对命令行传入的值没有限制。
注意:传递自定义命令行参数时 --
是紧跟构建设置目标的。string_flag
构建设置规则在实际工程中我们也不需要自己去实现,可以通过 `bazel-skylib`[3] 加载:load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
。
5 自定义规则绑定自定义构建设置
比如我们定义了一个 date
规则,我们在构建 date
的目标时,希望能够在命令行获取 week
参数,则我们需要在 date
的规则实现中能够获取 week
的配置值。
def _date_impl(ctx):
print("星期", ctx.attr._today[BuildSettingInfo].value)
return []
date = rule(
implementation = _date_impl,
attrs = {
"_today" : attr.label(default = ":week")
}
)
通过在 date
规则中声明私有属性 _today
,即 date
规则使用用户无法直接更改 _today
属性值,保证了只能在命令行上设置值或者设置默认值。同时 _today
属性绑定 :week
目标,从而在 date
规则实现中可以获得 _today
属性值。
完成规则实现后,就可以在 BUILD
文件中定义 date
目标:
load("//:deps.bzl", "string_flag", "date")
date(
name = "today"
)
构建 today 目标:
$ bazel build :today --//:week=3
DEBUG: /home/biedamingming/workspace/bazel/build_setting_test/deps.bzl:13:10: allowed_values = ["1", "2", "3", "4", "5", "6", "7"]
DEBUG: /home/biedamingming/workspace/bazel/build_setting_test/deps.bzl:14:10: value = 3
DEBUG: /home/biedamingming/workspace/bazel/build_setting_test/deps.bzl:33:10: 星期 3
$ bazel build :today
DEBUG: /home/biedamingming/workspace/bazel/build_setting_test/deps.bzl:13:10: allowed_values = ["1", "2", "3", "4", "5", "6", "7"]
DEBUG: /home/biedamingming/workspace/bazel/build_setting_test/deps.bzl:14:10: value = 1
DEBUG: /home/biedamingming/workspace/bazel/build_setting_test/deps.bzl:33:10: 星期 1
6 小结
本文对实现自定义命令行构建设置有了个基本的了解,包括规则定义、实例化和实际应用。更多的使用场景可以参考官方文档,包括 Bazel 内置的 label_flag
和 label_setting
;结合 select()
使用;
参考资料
[1]
这里: https://docs.google.com/document/d/1vc8v-kXjvgZOdQdnxPTaV0rrLxtP2XwnD2tAZlYJOqw
[2]
类型: https://docs.bazel.build/versions/master/skylark/lib/config.html
[3]
bazel-skylib
: https://github.com/bazelbuild/bazel-skylib/releases
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【我在拉勾训练营学技术】mysql 索引面试再也不怕啦
- 智能合约中常见的漏洞总结复现#技术创作101训练营#
- JS根据列表排列对象数组
- git提取两次提交或者版本的差异文件并打包成zip压缩包
- 博客通用版Live2d伊斯特瓦尔发布
- 一个小需求,自动重启k8s集群中日志不刷新的POD
- 多图,一文了解 8 种常见的数据结构
- Jenkins--pipline 流水线部署Java后端项目
- 微信小程序修炼之路LV1—工具介绍篇
- CentOS 7 部署OpenLDAP+FreeRadius
- 手把手教你使用yolo进行对象检测
- K8s之Helm工具详解
- 技术创作101训练营——上古神器Gvim--从入门到精通
- 关于linux7下编写crontab任务执行mysqldump备份无效
- 黑暗中的YOLO:解决黑夜里的目标检测 | ECCV 2020