使用宏创建自定义动词

报告问题 查看来源 每晚 · 7.2。 · 7.1敬上 · 7.0。 · 6.5 · 6.4

与 Bazel 的日常互动主要通过以下几个命令进行: buildtestrun。但有时,您可能会感觉这些事情有局限性: 希望将软件包推送到代码库、为最终用户发布文档,或 来部署应用但是,Bazel 没有 publishdeploy 命令 - 这些操作在什么位置适用?

bazel run 命令

而 Bazel 注重封闭性、可再现性和增量性, buildtest 命令对上述任务没有帮助。这些操作 可以在沙盒中运行,具有有限的网络访问权限,并且不能保证 每 bazel build重新运行一次。

请改为使用 bazel run,即您希望 Google 处理的任务的工作负载。 副作用。Bazel 用户已习惯创建可执行文件的规则, 规则作者可以遵循一组常见的模式,将这一过程扩展到 "custom verbs"。

实际应用:rules_k8s

rules_k8s 为例, 针对 Bazel 的 Kubernetes 规则假设您有以下目标:

# BUILD file in //application/k8s
k8s_object(
    name = "staging",
    kind = "deployment",
    cluster = "testing",
    template = "deployment.yaml",
)

k8s_object 规则构建 在 staging 上使用 bazel build 时,您需要创建标准 Kubernetes YAML 文件 目标。不过,其他目标也由 k8s_object 创建。 宏的名称,如 staging.apply:staging.delete。这些 build 脚本来执行这些操作;使用 bazel run staging.apply 执行时,这些行为类似于我们自己的 bazel k8s-applybazel k8s-delete 命令。

另一个示例:ts_api_guardian_test

在 Angular 项目中也可以看到此模式。通过 ts_api_guardian_test 会生成两个目标。第一个是标准的nodejs_test目标 根据“黄金”模型文件(即包含 预期输出)。这可以通过常规的 bazel test 调用进行构建和运行。在 angular-cli 中,您可以运行这样的一个 目标值bazel test //etc/api:angular_devkit_core_api共享。

随着时间的推移,该黄金文件可能会因合理原因而需要更新。 手动更新这个宏既繁琐又容易出错, 一个 nodejs_binary 目标,用于更新 Golden 文件,而不是在 反对它。实际上,可以编写相同的测试脚本,以便在“verify”(验证) 或“接受”调用模式。这遵循相同的模式 因为没有原生 bazel test-accept 命令, 使用 bazel run //etc/api:angular_devkit_core_api.accept

这种模式可能非常强大,在你上手后, 学习识别它。

调整您自己的规则

是此模式的核心。宏的用途 但可以创建多个目标通常,他们会创建一个 具有执行主要构建操作的指定名称的目标:可能是 它可以构建普通的二进制文件、Docker 映像或源代码归档。在 其他目标会被创建为生成 取决于主要目标的输出结果,例如,将 生成的二进制文件或更新预期的测试输出。

为了说明这一点,我们用一条虚构的规则来生成一个网站, Sphinx 和 目标,让用户可以在准备就绪时发布广告素材。请考虑以下事项 使用 Sphinx 生成网站的现有规则

_sphinx_site = rule(
     implementation = _sphinx_impl,
     attrs = {"srcs": attr.label_list(allow_files = [".rst"])},
)

接着,请考虑如下规则,该规则会构建一个脚本,在运行时 发布生成的网页:

_sphinx_publisher = rule(
    implementation = _publish_impl,
    attrs = {
        "site": attr.label(),
        "_publisher": attr.label(
            default = "//internal/sphinx:publisher",
            executable = True,
        ),
    },
    executable = True,
)

最后,定义以下宏,为上述两种情况创建目标 规则集:

def sphinx_site(name, srcs = [], **kwargs):
    # This creates the primary target, producing the Sphinx-generated HTML.
    _sphinx_site(name = name, srcs = srcs, **kwargs)
    # This creates the secondary target, which produces a script for publishing
    # the site generated above.
    _sphinx_publisher(name = "%s.publish" % name, site = name, **kwargs)

BUILD 文件中,使用该宏,就像它只创建主宏一样 目标:

sphinx_site(
    name = "docs",
    srcs = ["index.md", "providers.md"],
)

在此示例中,“docs”目标,就像宏是 标准的单个 Bazel 规则构建完毕后,该规则会生成一些配置 并运行 Sphinx 来生成 HTML 网站,以供手动检查。不过, 一个额外的“docs.publish”target 会构建目标,用于为 发布网站检查主要目标的输出结果后,您可以 使用 bazel run :docs.publish 将其发布以供公众使用,就像 一个虚构的 bazel publish 命令。

_sphinx_publisher 的实现 可能是什么样子通常,此类操作会编写一个启动器 Shell 脚本。 此方法通常涉及 ctx.actions.expand_template 编写一个非常简单的 Shell 脚本,在本示例中为调用发布商二进制文件 替换为主要目标的输出路径。这样,发布商就可以 _sphinx_site规则只能生成 只需这个小脚本就能将两者结合 。

rules_k8s 中,.apply 的确会这样做: expand_template 编写了一个非常简单的 Bash 脚本, apply.sh.tpl, 该命令会运行 kubectl 命令,其中包含主要目标的输出。此脚本可以 然后使用 bazel run :staging.apply 进行构建和运行,从而有效地提供 针对 k8s_object 目标的 k8s-apply 命令。