rules_pkg — Claude Code guide

rules_pkg is a set of Bazel rules for building distribution packages (tar, zip, deb, rpm, …). The core abstraction is a set of package-format-agnostic mapping rules (pkg_files, pkg_filegroup, pkg_mkdirs, pkg_mklink) that describe what goes where in a package; format-specific rules (pkg_tar, pkg_zip, pkg_deb, pkg_rpm) consume those descriptions.

Repository layout

pkg/            Runtime rules and providers (shipped in the distribution)
  mappings.bzl  pkg_files, pkg_filegroup, pkg_mkdirs, pkg_mklink
  providers.bzl PackageVariablesInfo and other providers
  private/      Internal implementation helpers (not public API)
    util.bzl    substitute_package_variables, setup_output_files, …
    deb/        pkg_deb implementation
    tar/        pkg_tar implementation
    zip/        pkg_zip implementation
tests/          All tests (not shipped)
  mappings/     Analysis tests for pkg_files / pkg_filegroup
  tar/          Tests for pkg_tar
  deb/          Tests for pkg_deb
  rpm/          Tests for pkg_rpm
  zip/          Tests for pkg_zip
examples/       Runnable examples (tested in CI)
docs/           Generated reference docs (do not edit by hand)
distro/         Rules to build the distribution tarball

Top-level .bzl shims (mappings.bzl, pkg.bzl, etc.) are backward-compatibility re-exports of the files inside pkg/.

Code style

Starlark / BUILD files — always run buildifier after editing

After editing any .bzl or BUILD file, run:

buildifier --lint=fix <FILE>

buildifier enforces load ordering, argument sorting, and other canonical style. It will reorder loads alphabetically; let it.

Starlark conventions

  • All public rule attributes must have a doc = string.
  • Use substitute_package_variables(ctx, value) (from //pkg/private:util.bzl) to expand $(VAR) make-variable syntax in string attributes. The rule must also declare a package_variables attribute typed attr.label(providers = [PackageVariablesInfo]).
  • Prefer solutions that work for all package formats (via pkg_files/pkg_filegroup) over format-specific additions.
  • Actions must not write quoted strings directly to command lines — write paths to an intermediate file instead.

Python

  • Python 3 only; no Python 2 support.
  • Always import with full paths from the workspace root.
  • No new third-party package dependencies — standard library only.

General

  • No files should have trailing whitespace.
  • Try to keep lines under 100 characters long.

Testing

All features and bug fixes must have tests.

Mappings (pkg_files / pkg_filegroup)

Tests live in tests/mappings/mappings_test.bzl and are registered via the mappings_analysis_tests() macro called from tests/mappings/BUILD.

  • Use pkg_files_contents_test (defined in mappings_test.bzl) to assert expected destination paths from a pkg_files target.
  • Use pkg_filegroup_contents_test to compare a pkg_filegroup output against reference pkg_files / pkg_mkdirs / pkg_mklink targets.
  • Use generic_negative_test for targets that are expected to fail analysis.
  • Add new test names to the pkg_files_analysis_tests test suite list at the bottom of mappings_analysis_tests().

Run them with:

bazel test //tests/mappings/...

package_variables / make-variable substitution

The sample naming rule used across tests is my_package_naming in tests/my_package_name.bzl. Load it as:

load("//tests:my_package_name.bzl", "my_package_naming")

Create an instance, then wire it to the package_variables attribute of the rule under test. Example:

my_package_naming(name = "my_vars", label = "linux_x86_64", tags = ["manual"])

pkg_files(
    name = "my_files",
    srcs = [...],
    prefix = "usr/lib/$(label)",
    package_variables = ":my_vars",
    tags = ["manual"],
)

Format-specific tests

bazel test //tests/tar/...
bazel test //tests/deb/...
bazel test //tests/zip/...
bazel test //tests/rpm/...   # may require rpm toolchain

Running everything

bazel test //tests/...

Regenerating docs

After any feature change, regenerate the reference docs before committing:

bazel build //doc_build:reference
cp bazel-bin/doc_build/reference.md docs/latest.md

Do not git commit yet — that is a separate step the user will handle.

Common patterns

Adding package_variables support to an attribute

  1. Import PackageVariablesInfo from //pkg:providers.bzl and substitute_package_variables from //pkg/private:util.bzl.
  2. Add to the rule attrs:
    "package_variables": attr.label(
        doc = """See [Common Attributes](#package_variables)""",
        providers = [PackageVariablesInfo],
    ),
    
  3. In the implementation, call substitution at the top before using the value:
    prefix = substitute_package_variables(ctx, ctx.attr.prefix)
    
  4. Use the substituted local variable everywhere instead of ctx.attr.prefix.
  5. Run buildifier --lint=fix on the modified file.
  6. Add analysis tests in the appropriate test file.