Aspect CLI includes a special Gazelle extension (nicknamed “orion”) which wraps a Starlark interpreter, and provides an API for you to write your own extensions.
Writing extensions in Starlark fixes a bunch of the problems we’ve seen.
- 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_abstraction
can 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
.star
files 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
.star
files in their Bazel module.
Basics
- Write a starlark source file (anywhere in your repo with a
.star
extension for GitHub code highlighting). - Register rule(s) that you want to manage using
register_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
aspect.register_rule_kind("sh_library", {
"From": "@rules_shell//shell:sh_library.bzl",
"NonEmptyAttrs": ["srcs"],
"MergeableAttrs": ["srcs"],
"ResolveAttrs": ["deps"],
})
- Register an extension to the
configure
command withregister_configure_extension
. Two arguments are functions: prepare
takes the configuration as an argument and returns aPrepareResult
declare
takes a context object as an argument and creates targets as a side-effect
Here’s a simple example:
aspect.register_configure_extension(
id = "rules_sh",
prepare = lambda cfg: aspect.PrepareResult(
sources = aspect.SourceExtensions(".bash", ".sh"),
),
declare = lambda ctx: ctx.targets.add(
kind = "sh_library",
name = "shell",
attrs = {
"srcs": [s.path for s in ctx.sources],
},
),
)
That’s all you need to generate sh_library
targets for all your shell code!
A full-featured example: https://github.com/aspect-build/aspect-workflows-template/blob/main/{{ .ProjectSnake }}/.aspect/cli/shell.star