如何让 bazel shell 命令示例工作?

how to get bazel shell command example to work?

我正在尝试获取 shell 命令以使用 bazel (v4.2.1)。我正在使用来自 bazelbuild github 的 page 作为参考。该示例使用了 BUILD 文件和 rules.bzl 文件的组合。 rules.bzl 文件包含在 BUILD 文件中。为简单起见,我使用单个文件 foo/shell_command/BUILD.

这是我的 BUILD 文件的内容:

"""Rules that execute shell commands to do simple transformations.
While shell commands are convenient, they should be used carefully. Shell
commands are subject to escaping and injection issues, as well as portability
problems. It is often better to declare a binary target in a BUILD file and
execute it.
For very simple commands that are only used for a small number of targets, it
may be simpler to use genrule()s in a BUILD file instead of a custom rule that
invokes shell commands.
"""


def _emit_size_impl(ctx):
    # The input file is given to us from the BUILD file via an attribute.
    in_file = ctx.file.file

    # The output file is declared with a name based on the target's name.
    out_file = ctx.actions.declare_file("%s.size" % ctx.attr.name)

    ctx.actions.run_shell(
        # Input files visible to the action.
        inputs = [in_file],
        # Output files that must be created by the action.
        outputs = [out_file],
        # The progress message uses `short_path` (the workspace-relative path)
        # since that's most meaningful to the user. It omits details from the
        # full path that would help distinguish whether the file is a source
        # file or generated, and (if generated) what configuration it is built
        # for.
        progress_message = "Getting size of %s" % in_file.short_path,
        # The command to run. Alternatively we could use '', '', etc., and
        # pass the values for their expansion to `run_shell`'s `arguments`
        # param (see convert_to_uppercase below). This would be more robust
        # against escaping issues. Note that actions require the full `path`,
        # not the ambiguous truncated `short_path`.
        command = "wc -c '%s' | awk '{print }' > '%s'" %
                  (in_file.path, out_file.path),
    )

    # Tell Bazel that the files to build for this target includes
    # `out_file`.
    return [DefaultInfo(files = depset([out_file]))]



# this loaded
emit_size = rule(
    implementation = _emit_size_impl,
    attrs = {
        "file": attr.label(
            mandatory = True,
            allow_single_file = True,
            doc = "The file whose size is computed",
        ),
    },
    doc = """
Given an input file, creates an output file with the extension `.size`
containing the file's size in bytes.
""",
)

def _convert_to_uppercase_impl(ctx):
    # Both the input and output files are specified by the BUILD file.
    in_file = ctx.file.input
    out_file = ctx.outputs.output
    ctx.actions.run_shell(
        outputs = [out_file],
        inputs = [in_file],
        arguments = [in_file.path, out_file.path],
        command = "tr '[:lower:]' '[:upper:]' < \"\" > \"\"",
    )
    # No need to return anything telling Bazel to build `out_file` when
    # building this target -- It's implied because the output is declared
    # as an attribute rather than with `declare_file()`.


# this loaded
convert_to_uppercase = rule(
    implementation = _convert_to_uppercase_impl,
    attrs = {
        "input": attr.label(
            allow_single_file = True,
            mandatory = True,
            doc = "The file to transform",
        ),
        "output": attr.output(doc = "The generated file"),
    },
    doc = "Transforms a text file by changing its characters to uppercase.",
)



emit_size(
    name = "foo",
    file = "foo.txt",
)


convert_to_uppercase(
    name = "make_uppercase",
    input = "foo.txt",
    output = "upper_foo.txt",
)

我的文件系统是这样设置的:

~/xxx/mysrc 
$ tree .
.
├── shell_command
│   ├── BUILD
│   ├── BUILD_ORIG
│   └── foo.txt
└── WORKSPACE

当我尝试使用 Bazel 构建示例时,我得到以下结果:

$ bazel build //shell_command
Starting local Bazel server and connecting to it...
ERROR: /home/xxx/xxx/xxx/mysrc/shell_command/BUILD:42:13: name 'DefaultInfo' is not defined
ERROR: /home/xxx/xxx/xxx/mysrc/shell_command/BUILD:47:13: name 'rule' is not defined
ERROR: /home/xxx/xxx/xxx/mysrc/shell_command/BUILD:50:17: name 'attr' is not defined
ERROR: /home/xxx/xxx/xxx/mysrc/shell_command/BUILD:78:24: name 'rule' is not defined
ERROR: /home/xxx/xxx/xxx/mysrc/shell_command/BUILD:81:18: name 'attr' is not defined
ERROR: /home/xxx/xxx/xxx/mysrc/shell_command/BUILD:86:19: name 'attr' is not defined
ERROR: Skipping '//shell_command': no such target '//shell_command:shell_command': target 'shell_command' not declared in package 'shell_command' defined by /home/xxx/xxx/xxx/mysrc/shell_command/BUILD
WARNING: Target pattern parsing failed.
ERROR: no such target '//shell_command:shell_command': target 'shell_command' not declared in package 'shell_command' defined by /home/xxx/xxx/xxx/mysrc/shell_command/BUILD
INFO: Elapsed time: 1.771s
INFO: 0 processes.
FAILED: Build did NOT complete successfully (1 packages loaded)

FWIW,我已经尝试了各种目录和命令变体来使构建工作,但它们都给出了相同的结果:

WORKSPACE 文件的目录中。

~/xxx/xxx/mysrc 
$ ls -FC
shell_command/  WORKSPACE
~/xxx/xxx/mysrc 
$ bazel build //shell_command

同样,指定目录(?)和目标(?):

$ bazel build //shell_command:shell_command

不要合并 rules.bzl 和您的 BUILD 文件。 Bazel 不支持在 BUILD 文件中调用 rule(以及其他各种方法)。您必须使用这些定义加载一个 bzl 文件,就像 bazelbuild github 示例所做的那样。