1 名词
名词 | 释义 |
---|---|
aspect | 将自定义行为附加到规则的逻辑包。这与配置相似,但不同的是 aspect 不会更改原始规则。 |
build flag | 构建标志,设置配置的命令行标志,比如 --cpu,它好比 key-value 的 key。根据定义,用户可以直接在任何构建上进行设置。 |
build setting(configuration setting) | 构建设置,是一条配置信息。可以认为配置为 key-value 映射。构建标志产生构建设置,但是可以通过其他方式(例如通过transitions)来设置构建设置。没有附带标志的构建设置对用户不可见。规则设计者可以利用它,例如使规则在其依赖项上设置隐式属性。 |
transition | 表示跨依赖项边缘的配置转换。即可以实现读入一组构建设置,并输出一组构建设置。 |
provider | 简单值对象的构造函数,称为提供 provider 实例 |
这里名词
只做个索引,方便理解,可能现在反而让理解变得更加复杂,不过没关系,我们主要是实现的就是自定义 build flag
。更多参见这里[1]。
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
我们可以自定义构建标志以及规范它的构建设置,即比如我们定义了构建设置 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
设置。
同我们之前文章介绍的自定义规则一样,自定义构建设置规则也需要有实现,即 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")
。
比如我们定义了一个 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
本文对实现自定义命令行构建设置有了个基本的了解,包括规则定义、实例化和实际应用。更多的使用场景可以参考官方文档,包括 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