简体   繁体   中英

How to pass argument from command line to bazel file

I have a variable declared in.bzl file example: VERSION_NUMBER = "00" . I want to override this variable from command line when i build different version of the project. example: bazel build target --sunbversion_number= "99" I want to change this variable because it is invoked in some function to create the name of the output paths. exammple: for version "00" the outputfile will be: name_00.extension and for version "99" the outputfile will be: name_99.extension This is my example: in.bzl file i declared:
SUBVERSION_NUMBER = "99" and a fucntion that return a name of the file regarding to the SUBVERSION_NUMBER
def get_name(SUBVERSION_NUMBER):
return "test-"+SUBVERSION_NUMBER

OUTPUT_NAME = get_name("99")

Then my genrule():
genrule(name = "test",
srcs = [srcs] ,
outs = [OUTPUT_NAME+".tek"],
cmd = "cmd to generate the file" )
when i build this rule i get the output file test-99.tek
what i want is when i run bazel build test --//version=01 or any other suggested solution, i want to get output test-01.tek Thanks

There is no way to get values from the command line into a bzl file like that, but there are a few options, depending on exactly what you want to do.

One way is to use "stamping" to set the version, and then put the versioned file in a zip file with a known name, eg using a genrule. Build stamping is usually used for embedding a version number and other information into the output files themselves, but can be used here too. A zip file is necessary because the output file name has to be known at load time (ie before any configuration data from the command line is processed at analysis time).

Something like this:

# 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=$<"
""",
)

Then:

$ 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

So that results in a zip file with a known name containing the versioned file.

Another approach is to use "User-defined build settings": https://docs.bazel.build/versions/master/skylark/config.html#user-defined-build-settings

Something like this:

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",
  outs = ["out.txt"],
  cmd = "echo foo > $@",
)

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

Then:

$ 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, 2 linux-sandbox.
INFO: Build completed successfully, 3 total actions

So that results in a file with the version in its name. There's no label to refer to the versioned file itself though, so bazel build:out_99.txt nor srcs = [":out_99.txt"] will work, you have to go through the versioned_out target.


Update:

Here's a version that can version multiple outputs:

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, version, extension))
    versioned_files.append(versioned_file)

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

    ctx.actions.run_shell(
        inputs = [f],
        outputs = [versioned_file],
        command = 'cp "$1" "$2"',
        arguments = [copy_args])

  return DefaultInfo(files = depset(versioned_files))

versioned_files = rule(
  implementation = _versioned_files_impl,
  attrs = {
    "src": attr.label(mandatory = 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", "versioned_files")

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

genrule(
  name = "gen_out",
  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",
)

usage:

$ 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

Update:

An example of putting the version in a cc target's define:

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;
}

build and run:

$ 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, 2 linux-sandbox.
INFO: Build completed successfully, 6 total actions
INFO: Build completed successfully, 6 total actions
version: 1.2.3

Combining the above methods, you would have to specify both --//:version=1.2.3 and --define=VERSION=1.2.3 on the command line. There's a way to have only --//:version , but it would require another Starlark rule like versioned_files which either

  1. generates a file with the version in it that goes in the data attribute and the program reads at runtime, or

  2. a Starlark rule which generates a C++ file with the version in it, which then gets put in the sources of a cc_library , which the rest of your program can depend on and use at compile time.

These approaches will probably require refactoring your program.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM