jaksky
jaksky

Reputation: 3405

How do I zip files in Bazel?

I have a set of files as part of the my repository. How do I produce a zip file out of those files in Bazel? I found a rules for tar.gz etc. but cannot find a way how to achieve a zip archive.

Found references mentioning zipper but couldn't figure out how to load it and use it. Can someone more experienced with Bazel help?

Upvotes: 5

Views: 8663

Answers (2)

ahumesky
ahumesky

Reputation: 5006

The zipper utility is at @bazel_tools//tools/zip:zipper, this is its usage:

Usage: zipper [vxc[fC]] x.zip [-d exdir] [[zip_path1=]file1 ... [zip_pathn=]filen]
  v verbose - list all file in x.zip
  x extract - extract files in x.zip to current directory, or
       an optional directory relative to the current directory
       specified through -d option
  c create  - add files to x.zip
  f flatten - flatten files to use with create or extract operation
  C compress - compress files when using the create operation
x and c cannot be used in the same command-line.

For every file, a path in the zip can be specified. Examples:
  zipper c x.zip a/b/__init__.py= # Add an empty file at a/b/__init__.py
  zipper c x.zip a/b/main.py=foo/bar/bin.py # Add file foo/bar/bin.py at a/b/main.py

If the zip path is not specified, it is assumed to be the file path.

So it can be used in a genrule like this:

$ tree
.
├── BUILD
├── dir
│   ├── a
│   ├── b
│   └── c
└── WORKSPACE

1 directory, 5 files


$ cat BUILD
genrule(
  name = "gen_zip",
  srcs = glob(["dir/*"]),
  tools = ["@bazel_tools//tools/zip:zipper"],
  outs = ["files.zip"],
  cmd = "$(location @bazel_tools//tools/zip:zipper) c $@ $(SRCS)",
)


$ bazel build :files.zip
INFO: Analyzed target //:files.zip (7 packages loaded, 41 targets configured).
INFO: Found 1 target...
Target //:files.zip up-to-date:
  bazel-bin/files.zip
INFO: Elapsed time: 0.653s, Critical Path: 0.08s
INFO: 1 process: 1 linux-sandbox.
INFO: Build completed successfully, 2 total actions


$ unzip -l bazel-bin/files.zip
Archive:  bazel-bin/files.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  2010-01-01 00:00   dir/a
        0  2010-01-01 00:00   dir/b
        0  2010-01-01 00:00   dir/c
---------                     -------
        0                     3 files

It can similarly be used in Starlark:

def _some_rule_impl(ctx):

  zipper_inputs = []
  zipper_args = ctx.actions.args()
  zipper_args.add("c", ctx.outputs.zip.path)
  ....
  ctx.actions.run(
    inputs = zipper_inputs,
    outputs = [ctx.outputs.zip],
    executable = ctx.executable._zipper,
    arguments = [zipper_args],
    progress_message = "Creating zip...",
    mnemonic = "zipper",
  )


some_rule = rule(
  implementation = _some_rule_impl,
  attrs = {
    "deps": attr.label_list(),
    "$zipper": attr.label(default = Label("@bazel_tools//tools/zip:zipper"), cfg = "host", executable=True),
  },
  outputs = {"zip": "%{name}.zip"},
)

Upvotes: 7

Rohan Singh
Rohan Singh

Reputation: 21465

A basic pkg_zip rule was added to rules_pkg recently. Here is a basic usage example from the unit tests:

load("@rules_pkg//:pkg.bzl", "pkg_zip")

pkg_zip(
    name = "test_zip_basic",
    srcs = [
        "testdata/hello.txt",
        "testdata/loremipsum.txt",
    ],
)

You can specify the paths using the extra rules in mappings.bzl. Here is the example given by the Bazel team:

load("@rules_pkg//:mappings.bzl", "pkg_attributes", "pkg_filegroup", "pkg_files", "pkg_mkdirs", "strip_prefix")
load("@rules_pkg//:pkg.bzl", "pkg_tar", "pkg_zip")

# This is the top level BUILD for a hypothetical project Foo.  It has a client,
# a server, docs, and runtime directories needed by the server.
# We want to ship it for Linux, macOS, and Windows.
#
# This example shows various techniques for specifying how your source tree
# transforms into the installation tree. As such, it favors using a lot of
# distict features, at the expense of uniformity.

pkg_files(
    name = "share_doc",
    srcs = [
        "//docs",
    ],
    # Required, but why?: see #354
    strip_prefix = strip_prefix.from_pkg(),
    # Where it should be in the final package
    prefix = "usr/share/doc/foo",
)

pkg_filegroup(
    name = "manpages",
    srcs = [
        "//src/client:manpages",
        "//src/server:manpages",
    ],
    prefix = "/usr/share",
)


pkg_tar(
    name = "foo_tar",
    srcs = [
        "README.txt",
        ":manpages",
        ":share_doc",
        "//resources/l10n:all",
        "//src/client:arch",
        "//src/server:arch",
    ],
)

pkg_zip(
    name = "foo_zip",
    srcs = [
        "README.txt",
        ":manpages",
        ":share_doc",
        "//resources/l10n:all",
        "//src/client:arch",
        "//src/server:arch",
    ],
)

Upvotes: 10

Related Questions