如何将参数从命令行传递到 bazel 文件

问题描述

我在 .bzl 文件示例中声明了一个变量:VERSION_NUMBER = "00"。 当我构建不同版本的项目时,我想从命令行覆盖这个变量。 示例:bazel 构建目标 --sunbversion_number= "99" 我想更改此变量,因为它在某个函数中被调用以创建输出路径的名称。 示例:对于版本“00”,输出文件将是:name_00.extension 对于版本“99”,输出文件将是:name_99.extension 这是我的例子: 在 .bzl 文件中我声明:
SUBVERSION_NUMBER = "99" 以及返回与 SUBVERSION_NUMBER
相关的文件名的函数 def get_name(SUBVERSION_NUMBER):
return "test-"+SUBVERSION_NUMBER

OUTPUT_NAME = get_name("99")

然后是我的 genrule():
genrule(name = "test",
srcs = [srcs],
outs = [OUTPUT_NAME+".tek"],
cmd = "cmd to generate the file" )
当我建立这个规则时,我得到了输出文件 test-99.tek
我想要的是当我运行 bazel build test --//version=01 或任何其他建议的解决方案时,我想获得输出 test-01.tek 谢谢

解决方法

没有办法从命令行获取值到这样的 bzl 文件中,但有几个选项,具体取决于您想要做什么。

一种方法是使用“戳记”来设置版本,然后将版本化文件放在一个已知名称的 zip 文件中,例如使用一般规则。构建标记通常用于将版本号和其他信息嵌入到输出文件本身中,但也可以在这里使用。一个 zip 文件是必要的,因为必须在加载时知道输出文件名(即在分析时处理来自命令行的任何配置数据之前)。

像这样:

# out.txt is the original unversioned file
genrule(
  name = "gen_out",outs = ["out.txt"],cmd = "echo foo > $@",)

genrule(
  name = "gen_versioned_out",outs = ["out_versioned.zip"],srcs = [":out.txt"],tools = ["@bazel_tools//tools/zip:zipper"],stamp = True,cmd = """
# bazel-out/stable-status.txt is created when stamp = True
# Value of BUILD_EMBED_LABEL key comes from --embed_label on the command line
version="$$(grep BUILD_EMBED_LABEL bazel-out/stable-status.txt | cut -d ' ' -f 2)"
# Set a reasonable default if --embed_label was not specified
if [ -z "$$version" ]; then version="0"; fi

file="$$(basename $<)"
name="$${file%%.*}"
ext="$${file#*.}"
versioned_file="$${name}_$${version}.$${ext}"

# zipper allows specifying the name of the file in the zip directly,unlike the
# regular zip tool.
# c = create
# $@ = output file from "outs"
# $< = input file from "srcs"
# $$versioned_file=$< = "put this file in to the archive with this name"
$(location @bazel_tools//tools/zip:zipper) c "$@" "$$versioned_file=$<"
""",)

那么:

$ bazel build out_versioned.zip --embed_label=99
INFO: Analyzed target //:out_versioned.zip (7 packages loaded,19 targets configured).
INFO: Found 1 target...
Target //:out_versioned.zip up-to-date:
  bazel-bin/out_versioned.zip
INFO: Elapsed time: 0.340s,Critical Path: 0.10s
INFO: 3 processes: 1 internal,2 linux-sandbox.
INFO: Build completed successfully,3 total actions

$ unzip -l bazel-bin/out_versioned.zip
Archive:  bazel-bin/out_versioned.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        4  2010-01-01 00:00   out_99.txt
---------                     -------
        4                     1 file

这样会生成一个包含版本化文件的已知名称的 zip 文件。

另一种方法是使用“用户定义的构建设置”: https://docs.bazel.build/versions/master/skylark/config.html#user-defined-build-settings

像这样:

defs.bzl

def _version_file_impl(ctx):
  version = ctx.attr._version[VersionProvider].version
  
  name,extension = ctx.file.file.basename.rsplit(".",1)
  
  versioned_file = ctx.actions.declare_file(
      "%s_%s.%s" % (name,version,extension))
      
  copy_args = ctx.actions.args()
  copy_args.add_all([ctx.file.file,versioned_file])
  
  ctx.actions.run_shell(
    inputs = [ctx.file.file],outputs = [versioned_file],command = 'cp "$1" "$2"',arguments = [copy_args])

  return DefaultInfo(files = depset([versioned_file]))

version_file = rule(
  implementation = _version_file_impl,attrs = {
    "file": attr.label(mandatory = True,allow_single_file = True),"_version": attr.label(default = "//:version"),}
)

VersionProvider = provider(fields = ["version"])

def _version_flag_impl(ctx):
  return VersionProvider(version = ctx.build_setting_value)

version_flag = rule(
  implementation = _version_flag_impl,build_setting = config.int(flag = True),)

BUILD

load(":defs.bzl","version_flag","version_file")

version_flag(
  name = "version",build_setting_default = 0,)

genrule(
  name = "gen_out",)

version_file(
  name = "versioned_out",file = ":out.txt",)

那么:

$ bazel build :versioned_out --//:version=99
INFO: Analyzed target //:versioned_out (5 packages loaded,10 targets configured).
INFO: Found 1 target...
Target //:versioned_out up-to-date:
  bazel-bin/out_99.txt
INFO: Elapsed time: 0.322s,Critical Path: 0.06s
INFO: 3 processes: 1 internal,3 total actions

这样就会生成一个名称中带有版本的文件。但是没有标签来引用版本化文件本身,因此 bazel build :out_99.txtsrcs = [":out_99.txt"] 都不起作用,您必须通过 versioned_out 目标。


更新:

这是一个可以对多个输出进行版本控制的版本:

defs.bzl

def _versioned_files_impl(ctx):
  version = ctx.attr._version[VersionProvider].version

  versioned_files = []
  for f in ctx.attr.src.files.to_list():
    name,extension = f.basename.rsplit(".",1)

    versioned_file = ctx.actions.declare_file(
        "%s_%s.%s" % (name,extension))
    versioned_files.append(versioned_file)

    copy_args = ctx.actions.args()
    copy_args.add_all([f,versioned_file])

    ctx.actions.run_shell(
        inputs = [f],arguments = [copy_args])

  return DefaultInfo(files = depset(versioned_files))

versioned_files = rule(
  implementation = _versioned_files_impl,attrs = {
    "src": attr.label(mandatory = True),"versioned_files")

version_flag(
  name = "version",outs = ["foo.txt","bar.txt","baz.txt"],cmd = """
echo foo > $(location foo.txt)
echo bar > $(location bar.txt)
echo baz > $(location baz.txt)
""",)

versioned_files(
  name = "versioned_files",src = ":gen_out",)

用法:

$ bazel build versioned_files --//:version=123
INFO: Analyzed target //:versioned_files (5 packages loaded,9 targets configured).
INFO: Found 1 target...
Target //:versioned_files up-to-date:
  bazel-bin/foo_123.txt
  bazel-bin/bar_123.txt
  bazel-bin/baz_123.txt
INFO: Elapsed time: 0.491s,Critical Path: 0.06s
INFO: 5 processes: 1 internal,4 linux-sandbox.
INFO: Build completed successfully,5 total actions

更新:

将版本放在 cc 目标的定义中的示例:

BUILD

cc_binary(
  name = "main",srcs = ["main.cc"],defines = ["VERSION=\\\"$(VERSION)\\\""],)

main.cc

#include <iostream>

#ifndef VERSION
#define VERSION "0.0.0"
#endif

int main() {
    std::cout << "version: " << VERSION << std::endl;
    return 0;
}

构建并运行:

$ bazel run main --define=VERSION=1.2.3
INFO: Analyzed target //:main (15 packages loaded,52 targets configured).
INFO: Found 1 target...
Target //:main up-to-date:
  bazel-bin/main
INFO: Elapsed time: 0.524s,Critical Path: 0.26s
INFO: 6 processes: 4 internal,6 total actions
INFO: Build completed successfully,6 total actions
version: 1.2.3

结合上述方法,您必须在命令行中同时指定 --//:version=1.2.3--define=VERSION=1.2.3。有一种方法只有 --//:version,但它需要另一个 Starlark 规则,例如 versioned_files,其中任一

  1. 生成一个文件,其中包含 data 属性中的版本并且程序在运行时读取,或者

  2. Starlark 规则,它生成一个包含版本的 C++ 文件,然后将其放入 cc_library 的源代码中,程序的其余部分可以依赖并在编译时使用。

这些方法可能需要重构您的程序。