Skip to content

Build Rules

This guide explains how to use build rules in Bazel to define your build targets.

Understanding Build Rules

Build rules define how Bazel should build different types of targets. Each rule specifies:

  • Input files and dependencies
  • Commands and tools to execute
  • Output files and artifacts
  • Configuration and platform settings

Rule Types

Core Rules

These are built into Bazel:

python
# Basic file operations
filegroup(name = "assets", srcs = ["data.txt"])
genrule(name = "gen", outs = ["out.txt"], cmd = "...")

# Shell scripts
sh_binary(name = "script", srcs = ["script.sh"])
sh_test(name = "test", srcs = ["test.sh"])

Language-Specific Rules

Rules for different programming languages:

python
# C/C++
cc_binary(
    name = "app",
    srcs = ["main.cc"],
    deps = [":lib"],
)

# Java
java_library(
    name = "lib",
    srcs = glob(["src/main/java/**/*.java"]),
    deps = ["@maven//:com_google_guava_guava"],
)

# Python
py_binary(
    name = "app",
    srcs = ["main.py"],
    deps = [":lib"],
    python_version = "PY3",
)

# Protocol Buffers
proto_library(
    name = "proto",
    srcs = ["data.proto"],
)

Rule Attributes

Common Attributes

Most rules support these attributes:

python
cc_library(
    name = "lib",                    # Required: Target name
    srcs = ["lib.cc"],              # Source files
    deps = ["//common:utils"],       # Build dependencies
    data = ["//data:config"],       # Runtime dependencies
    visibility = ["//visibility:public"],  # Access control
    tags = ["no-remote"],           # Special properties
)

Platform-Specific Attributes

Configure for different platforms:

python
cc_binary(
    name = "app",
    srcs = ["app.cc"],
    deps = select({
        "//config:linux": [":linux_lib"],
        "//config:darwin": [":darwin_lib"],
        "//conditions:default": [":default_lib"],
    }),
    target_compatible_with = [
        "@platforms//os:linux",
    ],
)

Starlark Rules

Writing Custom Rules

Define in .bzl files:

python
# my_rules.bzl
def _impl(ctx):
    output = ctx.actions.declare_file(ctx.label.name + ".out")
    ctx.actions.run(
        outputs = [output],
        inputs = ctx.files.srcs,
        executable = ctx.executable._tool,
        arguments = [output.path] + [f.path for f in ctx.files.srcs],
    )
    return [DefaultInfo(files = depset([output]))]

my_rule = rule(
    implementation = _impl,
    attrs = {
        "srcs": attr.label_list(allow_files = True),
        "_tool": attr.label(
            default = "//tools:processor",
            executable = True,
            cfg = "exec",
        ),
    },
)

Using Custom Rules

python
load("//rules:my_rules.bzl", "my_rule")

my_rule(
    name = "custom",
    srcs = ["input.txt"],
)

Best Practices

  1. Rule Design

    • Use built-in rules when possible
    • Keep custom rules focused and reusable
    • Document rule behavior and requirements
    • Follow the single responsibility principle
  2. Attribute Configuration

    • Make attributes clear and well-documented
    • Use appropriate attribute types
    • Provide sensible defaults
    • Validate attribute values
  3. Dependencies

    • Keep dependency graphs shallow
    • Use fine-grained targets
    • Avoid circular dependencies
    • Properly declare indirect dependencies
  4. Testing

    • Write rule unit tests
    • Test with different platforms
    • Verify output correctness
    • Test error conditions

Common Patterns

Test Rules

python
cc_test(
    name = "lib_test",
    srcs = ["lib_test.cc"],
    deps = [
        ":lib",
        "@com_google_googletest//:gtest_main",
    ],
    size = "small",
)

Multi-output Rules

python
genrule(
    name = "generate",
    srcs = ["input.txt"],
    outs = [
        "output1.txt",
        "output2.txt",
    ],
    cmd = "$(location //tools:generator) $(SRCS) $(OUTS)",
    tools = ["//tools:generator"],
)

Macro Rules

python
def cc_test_suite(name, srcs, deps = None):
    """Create a test suite from multiple source files."""
    tests = []
    for src in srcs:
        test_name = "%s_%s" % (name, src.replace(".cc", ""))
        cc_test(
            name = test_name,
            srcs = [src],
            deps = deps,
        )
        tests.append(test_name)
    
    native.test_suite(
        name = name,
        tests = tests,
    )

Released under the MIT License.