- It’s the same language you’d need to learn to write Macros or Rules
- Macros are like dynamic rule generators, and Gazelle extensions are like static rule generators. Logic implemented in a macro that provides a user experience like
my_abstractioncan be ported to a generator which writes the equivalent targets into the BUILD file (imagine this as “inline macro” refactoring) - and vice versa. - You can share logic between a rule implementation and the BUILD generator for that rule
- Starlark is an interpreted and fast language. It’s also highly parallel, parsing and querying the AST in many threads automatically.
- There’s no problem having many
.axlfiles in your repo. One big user has 29 extensions already. Each extension can be small since there’s little boilerplate. - Don’t have to rely on directive comments, since you’re free to special-case as needed in your extension.
- Rulesets can easily distribute these
.axlfiles in their Bazel module.
Basics
- Write a Starlark source file (anywhere in your repo with a
.axlextension for GitHub code highlighting). - Register rule(s) that you want to manage using
gazelle_rule_kind. We provide three attribute lists for the underlying Gazelle machinery to merge the results we return:
NonEmptyAttrs: a set of attributes that, if present, disqualify a rule from being deleted after merge.MergeableAttrs: a set of attributes that should be merged before dependency resolutionResolveAttrs: a set of attributes that should be merged after dependency resolution
Don’t register a kind that another enabled language already provides (e.g.
js_library, go_library). The runner aborts in that case, since Gazelle’s last-wins behavior would silently clobber the other language’s resolution.- Register an extension to the
configurecommand withorion_extension. The stage arguments are functions, all optional:preparetakes the configuration as an argument and returns aPrepareResultdeclaring which files to process and what queries to runanalyze(optional) inspects query results and declares importable symbols viactx.add_symbol(...)declaretakes a context object as an argument and creates targets as a side-effect
sh_library targets for all your shell code!
The older
register_configure_extension and register_rule_kind names still work but are deprecated — they print a deprecation warning and forward to orion_extension and gazelle_rule_kind respectively.Loading extensions
If you useaspect_gazelle(), pass your extensions to its extensions attribute and the macro wires them up for you (it sets ORION_EXTENSIONS on the binary under the hood):
aspect_gazelle() — point orion at your extensions with environment variables:
ORION_EXTENSIONS_DIR— a directory; every*.axlfile in it is loaded.ORION_EXTENSIONS— a comma-separated list of individual extension file paths.
Stages
EachBUILD file is generated by running the extension’s stages in sequence (extensions run in parallel within a stage). All three are optional:
- Prepare — declares which source files the extension processes and any queries to run on them, by returning a
PrepareResult(sources, queries). - Analyze — inspects per-file query results and calls
ctx.add_symbol(id, provider_type, label)to register symbols that other rules can import. - Declare — calls
ctx.targets.add(...)/ctx.targets.remove(...)to write rules into theBUILDfile. Attribute values of typeaspect.Importare resolved to Bazel labels after this stage.
Beyond the basics
A few capabilities you’ll reach for once the simple case works — see the orion README for the full API:- Symbols & imports —
ctx.add_symbol(...)inanalyzepublishes a symbol;aspect.Import(id, provider)as an attribute value resolves to the target that provides it. Useaspect.Import(multiple = True)for “collect all” patterns, orancestor = Trueto walk up the tree (e.g. finding the nearesttsconfig.json). - Extension properties — declare
propertieswithaspect.Property(type, default), then let users set them per-directory with# gazelle:{name} {value}directives. Read them in any stage viactx.properties, and usectx.properties.is_local(name)to detect where a directive is declared. - Inherited data —
ctx.datais a plugin-private key/value store written duringprepareand inherited by sub-packages (nearest-ancestor-wins), useful for anchoring a scope at a marker directory. - Directory files —
ctx.has_file("tsconfig.json")reports whether the current directory contains a file.

