Starlark in Bazel
Starlark in Bazel
Section titled “Starlark in Bazel”Bazel is Google’s open-source build system, designed for fast, reproducible builds at scale. Starlark is the extension language that makes Bazel customizable.
Key Concepts
Section titled “Key Concepts”File Types
Section titled “File Types”| File | Purpose | Example |
|---|---|---|
BUILD / BUILD.bazel | Defines build targets in a package | cc_binary(name = "app", srcs = ["main.cc"]) |
*.bzl | Starlark extension files (macros, rules) | def my_macro(name): ... |
WORKSPACE / WORKSPACE.bazel | Declares external dependencies | http_archive(name = "rules_go", ...) |
MODULE.bazel | Bzlmod module definition (Bazel 6+) | bazel_dep(name = "rules_go", version = "0.41.0") |
.bazelrc | Build configuration flags | build --compilation_mode=opt |
Targets and Labels
Section titled “Targets and Labels”A label uniquely identifies a target:
@repo_name//package/path:target_name│ │ ││ │ └── Target name│ └── Package path (directory with BUILD file)└── Repository name (@ for external, omit for current)Examples:
# Same package":my_lib"
# Different package, same repo"//src/lib:utils"
# External repository"@rules_go//go:def.bzl"Evaluation Model
Section titled “Evaluation Model”Bazel builds happen in three distinct phases:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐│ Loading Phase │────▶│ Analysis Phase │────▶│ Execution Phase ││ │ │ │ │ ││ • Parse BUILD │ │ • Run rule impl │ │ • Run actions ││ • Evaluate .bzl │ │ • Create actions│ │ • Produce files ││ • Expand macros │ │ • Return provs │ │ • Cache results │└─────────────────┘ └─────────────────┘ └─────────────────┘ Starlark Starlark SandboxedLoading Phase
Section titled “Loading Phase”During loading, Bazel:
- Parses
BUILDand.bzlfiles - Evaluates Starlark code
- Expands macros into rule instantiations
- Builds the target graph
# BUILD file - evaluated during loadingload("//build_defs:macros.bzl", "my_macro")
my_macro(name = "foo") # Macro expands here
cc_library( name = "bar", srcs = ["bar.cc"], deps = [":foo"],)Analysis Phase
Section titled “Analysis Phase”During analysis, Bazel:
- Runs each rule’s
implementationfunction - Creates actions (commands to run later)
- Returns providers (information for dependents)
def _my_rule_impl(ctx): # Create an action output = ctx.actions.declare_file(ctx.label.name + ".out") ctx.actions.run( outputs = [output], inputs = ctx.files.srcs, executable = ctx.executable._compiler, arguments = [f.path for f in ctx.files.srcs] + [output.path], )
# Return providers return [ DefaultInfo(files = depset([output])), MyInfo(data = output), ]Execution Phase
Section titled “Execution Phase”During execution, Bazel:
- Determines which actions need to run
- Executes actions in parallel (respecting dependencies)
- Caches action results for incremental builds
Macros vs Rules
Section titled “Macros vs Rules”Macros
Section titled “Macros”A macro is a Starlark function that instantiates rules. Macros are expanded during the loading phase.
def cc_test_suite(name, srcs, deps = []): """Creates a test target for each source file.""" for src in srcs: test_name = src.replace(".cc", "_test") native.cc_test( name = test_name, srcs = [src], deps = deps, )# Recommended for Bazel 8+my_macro = macro( implementation = _my_macro_impl, attrs = { "srcs": attr.label_list(allow_files = True), },)
def _my_macro_impl(name, srcs): native.genrule( name = name, srcs = srcs, outs = [name + ".out"], cmd = "cat $(SRCS) > $@", )# Works in all Bazel versionsdef my_macro(name, srcs): native.genrule( name = name, srcs = srcs, outs = [name + ".out"], cmd = "cat $(SRCS) > $@", )A rule has full control over the analysis phase. Rules can create actions, define providers, and access Bazel internals.
def _example_rule_impl(ctx): output = ctx.actions.declare_file(ctx.label.name + ".txt") ctx.actions.write(output, "Hello from " + ctx.label.name) return [DefaultInfo(files = depset([output]))]
example_rule = rule( implementation = _example_rule_impl, attrs = { "message": attr.string(default = "Hello"), },)Core APIs
Section titled “Core APIs”Providers
Section titled “Providers”Providers pass information between rules. They’re the primary way rules communicate.
# Define a providerMyInfo = provider( doc = "Information from my rule", fields = { "output": "The main output file", "data": "Additional data files", },)
def _my_rule_impl(ctx): # Collect from dependencies all_data = [] for dep in ctx.attr.deps: if MyInfo in dep: all_data.extend(dep[MyInfo].data)
# Return for dependents return [ DefaultInfo(files = depset([output])), MyInfo(output = output, data = all_data), ]Depsets
Section titled “Depsets”A depset is an efficient collection for accumulating transitive dependencies:
def _my_rule_impl(ctx): # Collect transitive files efficiently transitive_srcs = depset( direct = ctx.files.srcs, transitive = [dep[MyInfo].srcs for dep in ctx.attr.deps], ) return [MyInfo(srcs = transitive_srcs)]Actions
Section titled “Actions”Actions are the commands Bazel executes:
# Run a commandctx.actions.run( outputs = [output], inputs = depset([input]), executable = ctx.executable._tool, arguments = ["--input", input.path, "--output", output.path], mnemonic = "MyAction", progress_message = "Processing %s" % input.short_path,)
# Run a shell commandctx.actions.run_shell( outputs = [output], inputs = [input], command = "cat $1 | tr a-z A-Z > $2", arguments = [input.path, output.path],)
# Write a filectx.actions.write( output = output, content = "Generated content",)Aspects
Section titled “Aspects”Aspects traverse the dependency graph, augmenting targets with additional actions:
def _my_aspect_impl(target, ctx): # Run on every target in the graph if hasattr(ctx.rule.attr, "srcs"): # Do something with srcs pass return []
my_aspect = aspect( implementation = _my_aspect_impl, attr_aspects = ["deps"], # Propagate along deps)Starlark Dialect
Section titled “Starlark Dialect”Bazel’s Starlark has some restrictions compared to standard Starlark:
| Feature | Bazel Starlark |
|---|---|
| Recursion | Disabled by default |
Top-level for | Disabled by default |
print() | Available (outputs to console) |
load() | Only at top level |
| Mutable globals | Frozen after load |
Further Reading
Section titled “Further Reading”Source References
Section titled “Source References”This documentation is based on Bazel source code at commit a147aac:
- starlark-java implementation
- StarlarkThread.java - Thread execution context
- Starlark.java - Core evaluation
- Extension concepts