前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[Bazel]自定义命令行编译标志

[Bazel]自定义命令行编译标志

作者头像
别打名名
发布2020-08-27 16:19:36
2.6K0
发布2020-08-27 16:19:36
举报
文章被收录于专栏:小白AI.易名

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 源码就可以实现

我们最终实现:

代码语言:javascript
复制
$ bazel build //my:binary --some_feature_specific_to_my_app=fancy_mode

或者 some_feature_specific_to_my_app 内部默认配置:

代码语言:javascript
复制
$ bazel build //my:binary

3 自定义构建设置的定义

我们可以自定义构建标志以及规范它的构建设置,即比如我们定义了构建设置 week 规则,它可能的值只能是星期{1,2,3,4,5,6,7}。当然,简单的,我们可以定义构建设置,只限制值类型,而不限制值内容。

构建设置相关的规则跟其他规则定义差不多,区别就是看有没有 build_setting 属性。示例:

代码语言:javascript
复制
# 定义构建设置规则 string_flag
string_flag = rule(
    implementation = _string_impl,
    build_setting = config.string(flag = True)
)

config 规定了该构建设置规则的值类型为 string 类型,还可以设置 intboolstring_list 类型[2]flag = True 表示该构建设置能够允许用户在命令行上设置,否则的话只能由规则编写者在内部默认设置或者通过 transitions 设置。

4 自定义构建设置的实现和实例化

同我们之前文章介绍的自定义规则一样,自定义构建设置规则也需要有实现,即 implementation = _string_impl_string_impl 函数。我们可以在 _string_impl 函数中通过 ctx.build_setting_value 获取构建标志的值:

代码语言:javascript
复制
def _string_impl(ctx):
    # do something...
    value = ctx.build_setting_value
    # do something...

比如前面说的利用 string_flag 实现一个构建设置目标 week,需要对 week 的值做约束,那么需要在 _string_impl 里做检测,如果不匹配,则提示错误:

代码语言:javascript
复制
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 构建设置:

代码语言:javascript
复制
# BUILD file

string_flag(
    name = "week",
    build_setting_default = "1",
    values = [
        "1",
        "2",
        "3",
        "4",
        "5",
        "6",
        "7"
    ]
)

build_setting_default 是内置保留属性,表示对构建设置的默认值设置。

完成 week 构建设置的定义(实例化),可以通过命令行传入值设置:

代码语言:javascript
复制
$ 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 的配置值。

代码语言:javascript
复制
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 目标:

代码语言:javascript
复制
load("//:deps.bzl", "string_flag", "date")

date(
    name = "today"
)

构建 today 目标:

代码语言:javascript
复制
$ 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_flaglabel_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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-08-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 别打名名 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 2 背景
  • 3 自定义构建设置的定义
  • 4 自定义构建设置的实现和实例化
  • 5 自定义规则绑定自定义构建设置
  • 6 小结
    • 参考资料
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档