You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于workspace_status_command输出构建Bazel自定义规则?

解决Bazel自定义规则中访问workspace_status_command输出的问题

我刚好做过类似的需求,结合rules_go和rules_docker的思路,给你两个核心解决方案:

一、如何将状态命令输出转为字典?

首先明确:在Starlark规则的实现函数中,无法直接读取ctx.info_file/ctx.version_file的内容并转为字典——因为这些是构建阶段的输入文件,Starlark的规则逻辑是声明式的,不能直接读取文件内容。

但你可以通过一个辅助action来解析状态文件为字典格式(比如JSON),步骤如下:

  1. 写一个简单的Python脚本parse_status.py,用来将Bazel的status文件转为JSON:

    import sys
    import json
    
    status_dict = {}
    with open(sys.argv[1], 'r') as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            key, *value_parts = line.split()
            status_dict[key] = ' '.join(value_parts)
    
    print(json.dumps(status_dict))
    
  2. 在自定义规则中,调用这个脚本生成JSON格式的状态字典文件:

    def _parse_status_to_json(ctx):
        json_output = ctx.actions.declare_file("status.json")
        ctx.actions.run(
            inputs = [ctx.version_file],
            outputs = [json_output],
            executable = ctx.executable._parser,
            arguments = [ctx.version_file.path, json_output.path],
        )
        return json_output
    
    # 把脚本声明为工具
    _status_parser = rule(
        implementation = _parse_status_to_json,
        attrs = {
            "_parser": attr.label(
                executable = True,
                cfg = "exec",
                allow_single_file = True,
                default = Label("//path/to:parse_status.py"),
            ),
        },
    )
    

不过说实话,除非你需要把字典传给其他复杂逻辑,否则这个方法有点冗余——下面的方式更直接。

二、自定义规则访问workspace_status输出的最简方式

直接利用ctx.actions.run_shell或envsubst,在构建阶段解析status文件并替换模板生成version.go,这也是rules_go的核心思路:

步骤1:准备模板文件

创建version.go.tpl,用{{KEY_NAME}}作为占位符(对应你status命令输出的键):

package version

const (
    GitCommit = "{{STABLE_GIT_COMMIT}}"
    GitTag    = "{{STABLE_GIT_TAG}}"
    BuildTime = "{{BUILD_TIMESTAMP}}"
)

步骤2:实现自定义规则

def _generate_version_file_impl(ctx):
    # 声明输出文件
    output = ctx.actions.declare_file("version.go")
    # 依赖的模板和status文件(用ctx.version_file获取稳定的STABLE_*键)
    inputs = [ctx.file.template, ctx.version_file]

    # 用shell命令解析status文件为环境变量,再替换模板
    ctx.actions.run_shell(
        inputs = inputs,
        outputs = [output],
        command = """
            # 读取status文件,把每行的KEY VALUE转为环境变量
            while IFS=' ' read -r key value; do
                export "$key"="$value"
            done < "$1"
            # 用envsubst替换模板中的占位符
            envsubst < "$2" > "$3"
        """,
        arguments = [
            ctx.version_file.path,
            ctx.file.template.path,
            output.path,
        ],
    )

    # 返回输出文件作为规则产物
    return [DefaultInfo(files = depset([output]))]

# 定义规则
generate_version_file = rule(
    implementation = _generate_version_file_impl,
    attrs = {
        "template": attr.label(allow_single_file = True),
    },
)

步骤3:在BUILD中使用规则

generate_version_file(
    name = "version",
    template = "version.go.tpl",
)

# 然后在go_library/go_binary中依赖这个规则
go_library(
    name = "my_lib",
    srcs = [":version", "other.go"],
    # ...其他属性
)

关键说明:

  • ctx.version_file包含所有以STABLE_开头的状态键(这些键不会随每次构建变化,适合作为版本信息);ctx.info_file包含所有状态键(包括临时的、每次构建可能变化的键,比如BUILD_TIMESTAMP)。
  • 你的workspace_status_command脚本需要输出KEY VALUE格式的行,比如:
    # status.sh
    echo "STABLE_GIT_COMMIT $(git rev-parse HEAD)"
    echo "STABLE_GIT_TAG $(git describe --tags --abbrev=0 --exact-match 2>/dev/null || echo 'unknown')"
    echo "BUILD_TIMESTAMP $(date +%Y-%m-%dT%H:%M:%S)"
    
  • 记得在.bazelrc或构建命令中启用:build --workspace_status_command=./status.sh

这种方式不需要额外的依赖,直接用系统自带的envsubst和shell命令,是最简洁的实现方式。

内容的提问来源于stack exchange,提问作者Arne Hormann

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 09:08:20