I am following a bazel tutorial that ends up with a "zip" command. When I execute it - I end up with an error.
I am running on a mac laptop. I have tried multiple variations, so I am sharing just the latest one.
Here is the rule definition
def _archive(ctx):
out_file = ctx.actions.declare_file(ctx.attr.out)
args = ctx.actions.args()
args.add(ctx.attr.out)
args.add_all(ctx.files.files)
ctx.actions.run(
executable = "zip",
arguments=[args],
inputs=ctx.files.files,
outputs=[out_file],
use_default_shell_env = True,
)
return [DefaultInfo(files=depset([out_file]))]
archive = rule(
implementation = _archive,
attrs = {
"files": attr.label_list(allow_files=True),
"out": attr.string(mandatory=True),
},
)
Here is the usage
load(":archive.bzl", "archive")
filegroup(
name = "text_files",
srcs = [
"file1.txt",
"file2.txt",
],
)
archive(
name = "archive",
files = [
":text_files",
],
out = "all.zip",
)
Here is the command I am using
bazel build archive
And here is the error I am getting
$ bazel build archive
INFO: Analyzed target //:archive (0 packages loaded, 4 targets configured).
INFO: From Action all.zip:
adding: file1.txt (stored 0%)
adding: file2.txt (stored 0%)
ERROR: <redacted>/bazel-playground-2/zip_custom_rule/BUILD:12:8: output 'all.zip' was not created
ERROR: <redacted>/bazel-playground-2/zip_custom_rule/BUILD:12:8: Action all.zip failed: not all outputs were created or valid
Target //:archive failed to build
Use --verbose_failures to see the command lines of failed build steps.
INFO: Elapsed time: 0.151s, Critical Path: 0.03s
INFO: 2 processes: 1 internal, 1 darwin-sandbox.
ERROR: Build did NOT complete successfully
Debugging and log prints all show everything is at it should be.
When I implement a macro using genrule it works fine
def archive(name, files, out):
native.genrule(
name = name,
srcs = files,
outs = [out],
cmd = "zip -j $@ $(SRCS)",
)
How can I change my rule implementation to make it work?
The problem is that the out
attribute should be an output (file) rather than a string:
def _archive(ctx):
args = ctx.actions.args()
args.add(ctx.outputs.out)
args.add_all(ctx.files.files)
ctx.actions.run(
executable = "zip",
arguments = [args],
inputs = ctx.files.files,
outputs = [ctx.outputs.out],
use_default_shell_env = True,
)
return [DefaultInfo(files = depset([ctx.outputs.out]))]
archive = rule(
implementation = _archive,
attrs = {
"files": attr.label_list(allow_files=True),
"out": attr.output(mandatory=True),
},
)
You can see the command line that bazel uses with --subcommands
:
out
as a string:
zip all.zip file1.txt file2.txt
vs
out
as an output (file):
zip bazel-out/k8-fastbuild/bin/all.zip file1.txt file2.txt
Also note that zip
embeds timestamps in the archive, making the output non-deterministic. Also, relying on the system zip is not hermetic. Consider using rules_pkg at https://github.com/bazelbuild/rules_pkg which has the pkg_zip
rule.