前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[Bazel]repository_rule() vs rule()

[Bazel]repository_rule() vs rule()

作者头像
别打名名
发布2020-09-03 16:39:03
2.3K0
发布2020-09-03 16:39:03
举报
文章被收录于专栏:小白AI.易名小白AI.易名
  • 1 区别
  • 2 repository rule
  • 3 使用 repository_rule 实现文件读取/生成
  • 4 使用 repository_rule 实现下载工具链
  • 5 小结

1 区别

我们之前的文章里经常使用常规规则(regular rules)函数 rule() 来创建自定义规则,但是这些规则都有一个问题:他们依赖于主机系统上安装的各种工具。这样就会出现一个问题,即构建是不可复制的,如果同一项目上的两个开发人员安装了不同版本的 Go SDK,则他们将构建不同的二进制文件。它还会中断远程执行,即主机的工具链可能在执行平台上不可用。而 repository_rule() 就可以解决这个问题。

首先整体比较下 repository_rule()rule 的区别:

repository_rule

rule

仅可在 WORKSPACE 中使用

只能在 BUILD 中使用

在构建的最开始(获取阶段)运行

在分析阶段

会新建一个工作区(WORKSPACE)

在本 WORKSPACE 中

注意:

  • 构建分 fetch(获取), load(加载), analysis(分析) 和 execute(执行) 四个阶段。从构建阶段来看,rule() 规则可以依赖 repository_rule() 生成的 BUILD 文件中的目标或者 bzl 文件等。
  • 通过 $(bazel info output_base)/external/{工作区名称} 可以看到新建的工作区。
  • 如果需要在自定义的 repository rules 中使用第三方规则库,则需要在 WORKSPACE 调用自定义规则加载第三方规则库。
  • repository_ctx APIs 提供的规则可直接访问主机系统而无需沙箱,因此为了构建在不同环境下的可复制性,需要注意不要引入系统相关的信息,比如时间戳或者特定目录名或者环境变量等。

因此从构建的阶段来看,repository_rule 可以做的事情很多,比如包括:

  • 创建/删除文件
  • 执行本地可执行文件,并获取执行结果
  • 创建软链接
  • 下载解压文件
  • 读取本地文件内容
    • 实现自动化的 BUILD 文件
    • 下载第三方仓库,并实现校验
  • 根据模板文件生成 bzl 文件
  • ......

2 repository rule

Bazle 内置工具中 repository rules 相关规则分为两类:

  • git 相关的规则:@bazel_tools//tools/build_defs/repo:git.bzl
    • git_repository:克隆一个外部 git 仓库
    • new_git_repository:克隆一个外部 git 仓库
  • http 相关的规则:@bazel_tools//tools/build_defs/repo:http.bzl
    • http_archive:将 Bazel 相关的压缩的存档文件远程仓库下载下来,对其进行解压缩,然后可以使用其中相关规则
    • http_file:从 URL 下载文件,并使其可用作文件组(file group)
    • http_jar:从 URL 下载一个 .jar 扩展名包,并以 java_import 的形式提供

和内置的 repository rules 一样,可以使用 `repository_rule`[1] 函数自定义 repository rules

创建通用规则时,我们得到的 ctx 对象作为实现函数的参数。同样,创建 reposiroty 规则时,将得到一个 repository_ctx 对象作为实现函数的参数。

  • repository_ctx.attr :可以获取用户在规则中定义的相关属性的属性值
  • bool repository_ctx.delete(path) :删除一个文件或者目录
  • repository_ctx.download :下载并可以通过 sha256 校验一个 url 文件到输出目录(output path)
  • repository_ctx.download_and_extract :同上,但包含了解压功能,支持 "zip", "jar", "war", "tar.gz", "tgz", "tar.bz2", 或 "tar.xz" 包类型。
  • exec_result repository_ctx.execute(arguments, ...:执行参数列表给出的命令,执行命令具有超时限制,默认为600秒。
  • repository_ctx.extract :解压压缩包到指定目录
  • repository_ctx.file:创建一个可指定可执行属性的文件,并可写入内容
  • string repository_ctx.read(path) :读取一个文件内容
  • repository_ctx.symlink(from, to):创建符号链接
  • repository_ctx.template :使用模板生成一个文件,没有代入值的话,则功能等同于拷贝文件,类似的还有 ctx.actions.expand_template 函数
  • path repository_ctx.which(program) :返回一个程序的路径

更多参见:https://docs.bazel.build/versions/master/skylark/lib/repository_ctx.html

3 使用 repository_rule 实现文件读取/生成

conf/test.tpl:

代码语言:javascript
复制
NAME = "%{name}"
PASSWORD = "%{passwd}"

bazels/my_test_repo.bzl:

代码语言:javascript
复制
def _test_repo_impl(repository_ctx):
    # 读取文件内容
    content = repository_ctx.read(repository_ctx.path(repository_ctx.attr.conf))
    # 根据模板文件创建文件
    repository_ctx.template(
        "hello.bzl",
        repository_ctx.attr._test_tpl_path,
        substitutions = {
            "%{name}": "biedamingming",
            "%{passwd}" : "null",
        },
        executable = False,  # not executable
    )
    # 创建一个空的 BUILD 文件
    # Tip: 如果需要引用新工作空间内 bzl 文件,需要创建 BUILD 文件,即创建包
    repository_ctx.file("BUILD.bazel", "")
    
my_test_repo = repository_rule(
    implementation = _test_repo_impl,
    attrs = {
        "conf" : attr.label(allow_single_file = True),
        "_test_tpl_path": attr.label(default = "//:conf/test.tpl"),
    },
)

然后到 WORKSPACE 中加载规则并创建规则实例。新工作区名称即规则实例名称。

代码语言:javascript
复制
load("//bazels:my_test_repo.bzl", "my_test_repo")              
my_test_repo(                                                         
    name = "my_test_repo",                                            
    conf = "@autosdk//:conf/example.txt",
)   

调试:bazel query @{工作区名称}//:*

4 使用 repository_rule 实现下载工具链

我们可以将 Bazel 配置为使用本地工具链,但是为了实现构建环境的可复制性,我们可以将工具链统一远端管理,当然不只是工具链可以,我们的依赖也可以。使用 repository_rule 实现工具链的下载,可以整个依赖环境统一到沙箱中,从而保证了可复制性。

这里我们用 `rules_go_simple`[2] 来举例,首先声明定义一个规则:

代码语言:javascript
复制
go_download = repository_rule(
    implementation = _go_download_impl,
    attrs = {
        "urls": attr.string_list(
            mandatory = True,
            doc = "List of mirror URLs where a Go distribution archive can be downloaded",
        ),
        "sha256": attr.string(
            mandatory = True,
            doc = "Expected SHA-256 sum of the downloaded archive",
        ),
        "goos": attr.string(
            mandatory = True,
            values = ["darwin", "linux", "windows"],
            doc = "Host operating system for the Go distribution",
        ),
        "goarch": attr.string(
            mandatory = True,
            values = ["amd64"],
            doc = "Host architecture for the Go distribution",
        ),
        "_build_tpl": attr.label(
            default = "@rules_go_simple//internal:BUILD.dist.bazel.tpl",
        ),
    },
    doc = "Downloads a standard Go distribution and installs a build file",
)
  • urlssha256 用于下载归档文件。提供 SHA-256 校验和,以确保下载文件不会被破坏或篡改。这也使得下载缓存能够跨本地工作区进行。
  • 这里的 osarch 用于生成 BUILD 文件时使用
  • _build_tpl 是用于生成构建文件的模板的标签。这是一个隐藏属性(它的名字以_开头),这意味着它必须有一个默认值。具体参见 rules_go_simple[3]

_go_download_impl 的实现如下:

代码语言:javascript
复制
def _go_download_impl(ctx):
    # 开始下载 Go 发行版
    ctx.report_progress("downloading")
    ctx.download_and_extract(
        ctx.attr.urls,
        sha256 = ctx.attr.sha256,
        stripPrefix = "go",
    )

    # Add a build file to the repository root directory.
    # We need to fill in some template parameters, based on the platform.
    ctx.report_progress("generating build file")
    if ctx.attr.goos == "darwin":
        os_constraint = "@platforms//os:osx"
    elif ctx.attr.goos == "linux":
        os_constraint = "@platforms//os:linux"
    elif ctx.attr.goos == "windows":
        os_constraint = "@platforms//os:windows"
    else:
        fail("unsupported goos: " + ctx.attr.goos)
    if ctx.attr.goarch == "amd64":
        arch_constraint = "@platforms//cpu:x86_64"
    else:
        fail("unsupported arch: " + ctx.attr.goarch)
    constraints = [os_constraint, arch_constraint]
    constraint_str = ",\n        ".join(['"%s"' % c for c in constraints])

    substitutions = {
        "{goos}": ctx.attr.goos,
        "{goarch}": ctx.attr.goarch,
        "{exe}": ".exe" if ctx.attr.goos == "windows" else "",
        "{exec_constraints}": constraint_str,
        "{target_constraints}": constraint_str,
    }
    ctx.template(
        "BUILD.bazel",
        ctx.attr._build_tpl,
        substitutions = substitutions,
    )

这里的 ctx 实为 repository_ctx 上下文。通过 repository_ctx.report_progress(status) 可以更新正下载包的进度状态。repository_ctx.download_and_extract(),下载一个文件,并校验其文件,解压到其工作空间的指定文件夹中。最后 ctx.template() 同上一节所述,实现将参数传入模板文件,然后复制生成 BUILD.bazel 文件。

这里简单实现了文件下载、校验和解压,进一步的我们还可以实现对私有服务器进行身份验证或者通过自定义协议进行通信,当然这个实现就更复杂了。

下载并解压工具链后,如果去使用这些工具链呢?其实就相当于我们要实现一套语言相关的规则了,比如 go_binary,怎么去实现下载指定 Go 发行版 SDK,并编译出该 SDK 对应的可执行文件呢?我们则需要去定义工具链以及定义工具链的动作,比如编译动作(Action)。最后实现 go_binary,将输入(源文件)传入规则,并调用具体的动作实现最后的可执行文件生成。

5 小结

repository rules 作用强大是显而易见的,在构建 fetch(获取), load(加载), analysis(分析) 和 execute(执行) 四个阶段中作为第一阶段(fetch),能够做到文件下载并校验、动态生成文件、可以提前执行命令等,可操作的空间很大。利用好它,我们可以实现一个可复制的构建。

参考资料

[1]

repository_rule: https://docs.bazel.build/versions/master/skylark/lib/globals.html#repository_rule

[2]

rules_go_simple: https://github.com/jayconrod/rules_go_simple

[3]

rules_go_simple: https://github.com/jayconrod/rules_go_simple/blob/v5/internal/BUILD.dist.bazel.tpl

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 区别
  • 2 repository rule
  • 3 使用 repository_rule 实现文件读取/生成
  • 4 使用 repository_rule 实现下载工具链
  • 5 小结
    • 参考资料
    相关产品与服务
    对象存储
    对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档