3.7.3 Aspect Extension Language (AXL)

extra

AXL (Aspect Extension Language) is a Starlark task layer for workflows Bazel intentionally leaves outside the build graph: chaining multiple Bazel invocations, reacting to Build Event Protocol output as it streams, or driving repo-wide tools that do not fit cleanly into a BUILD target.1,2

AXL wraps Bazel from outside the build graph
A task layer above Bazel can orchestrate builds while repo-side effects stay outside graph semantics
Outside Bazel's build graph
CLI COMMAND
Aspect exposes a command
User-facing task entry
aspect lint //...
aspect mycmd --help
OUTER EFFECTS
Optional repo-side effects
Outside graph, task-owned
print reports buildozer via WASM multi-run repo updates
Reports, edits, and reruns stay outside declared outputs.
AXL TASK
AXL task drives Bazel
Outside-graph task logic
task(...)
ctx.bazel.build(...)
events = True
build.wait()
Optional event-aware output while Bazel runs.
Inside Bazel's build graph
BAZEL CORE
Bazel analyzes, builds, and tests
Declared graph execution
analyze build test
declared targets -> actions -> outputs
CUSTOM RULES
Rules create actions and providers
Graph-internal extension point
attrs actions providers
Need new graph semantics? Use a rule, not AXL.
AXL wraps Bazel from outside the graph. Custom rules extend Bazel from inside the graph.

Where AXL Fits

3.7.1 Bazel's Output Boundary drew the boundary first: Bazel inspects graphs and populates output trees, but it does not want to own every repo workflow. 3.7.2 Workflows Outside Bazel then showed the jobs that fall outside that boundary: source-tree updates, query-driven collectors, or multi-phase reporting. AXL is one answer to that gap. It gives the outer workflow a real language instead of a pile of shell wrappers.1,2

That does not make AXL the default answer for every side effect. If all you need is "build this thing, then let me run a checked-in publish or accept script", the simpler bazel run custom-verb pattern is still a better fit.3 The workflow-orchestration example shows that simpler pattern in shell-script form — a .publish launcher plus a query-driven release workflow — so it is the kind of repo-around-Bazel sketch AXL would re-express in Starlark once the orchestration grows beyond a few scripts. AXL becomes interesting when the workflow has to coordinate multiple Bazel calls, consume build events, or combine Bazel with tools that act on the repo rather than on declared outputs.

A Task Is Not a Target

Current Aspect CLI loads shared configuration from .aspect/config.axl and custom tasks from .aspect/*.axl files. After installing and pinning aspect as shown in 3.5.4 Aspect CLI Extensibility, run aspect help to see the tasks already available in the repository. Add a task file only when the workflow needs logic beyond an existing built-in.

An AXL task may look rule-like, but it has a different contract. A rule declares attributes and becomes a node in Bazel's analysis graph. An AXL task declares command-line arguments and becomes a CLI entry point. From that entry point, it can invoke Bazel with ctx.bazel.build(), wait for the result, and optionally subscribe to build events while the build is still running.1

A minimal task has this shape:

def impl(ctx: TaskContext) -> int:
    build = ctx.bazel.build(
        events = True,
        inherit_stderr = False,
        *ctx.args.target_pattern,
    )

    build_status = build.wait()
    return build_status.code

mycmd = task(
    implementation = impl,
    args = {
        "target_pattern": args.positional(default = ["..."]),
    },
)

That boundary matters. If you need new actions, providers, or output groups inside Bazel's graph, that is <a class="cross-ref" href="/book/4~2" title="4.2 Custom Rules, Providers &amp; Actions"><span class="cross-ref-id">4.2</span> Custom Rules, Providers &amp; Actions</a> territory. AXL lives one layer higher: it orchestrates Bazel, rather than extending Bazel's rule semantics.1,3

What AXL Buys You

  • Interactive output instead of post-processing. Linting can be implemented as a task that asks Bazel for build events, watches for named sets of files in the Build Event Protocol, and prints lint findings as they arrive. Bazel still does the analysis and actions. The task owns the user experience around them.1

  • Portable orchestration around repo tools. WASM bindings let a task call Go or Rust helpers from Starlark. One example wraps <a class="cross-ref" href="/book/3~4~3" title="3.4.3 Buildozer"><span class="cross-ref-id">3.4.3</span> Buildozer</a>, so a migration or fix-up command can edit BUILD or MODULE.bazel files without growing another platform-specific shell script.1

  • Workflows that never fit a collector target. Multi-target run, coverage post-processing, or query-driven "find targets, then build only those" flows are exactly the kind of outer workflow that tends to become brittle when modeled as fake graph edges. AXL keeps those steps outside the graph while still letting them speak Bazel fluently.1,2

How It Shows Up in a Real Repo

The clearest adoption example in the sources is rules_lint 2.0. Its aspect lint and aspect format commands were backed by AXL and wired through a MODULE.aspect file beside MODULE.bazel.4 Current Aspect CLI makes that relationship direct: .aspect/config.axl configures built-ins and .aspect/*.axl defines custom commands.5 AXL does not replace Bazel's build graph or <a class="cross-ref" href="/book/3~5~4" title="3.5.4 Aspect CLI Extensibility"><span class="cross-ref-id">3.5.4</span> Aspect CLI Extensibility</a>. The CLI provides the task surface and AXL provides its program and configuration.

That also keeps the layering honest. The linting logic itself still depends on outputs produced by Bazel and, in the rules_lint design, on aspect-driven analysis over existing targets.1,6 AXL sits above that machinery and decides how to sequence the build, how to render the results, and when to offer an outer-graph side effect such as applying a fix.

key takeaway

Reach for AXL when the workflow is around Bazel rather than inside Bazel: multiple invocations, repo-wide coordination, BEP-aware terminal UX, or structured side effects. If the job is just "produce outputs in the build graph", use <a class="cross-ref" href="/book/4~2" title="4.2 Custom Rules, Providers &amp; Actions"><span class="cross-ref-id">4.2</span> Custom Rules, Providers &amp; Actions</a>. If it is just a thin extra verb over one target, the bazel run macro pattern is usually simpler. AXL earns its keep when the outer workflow has become real software and you want that software in Starlark instead of shell glue.

Check your understanding · 3 questions

1.An AXL task calls ctx.bazel.build() and subscribes to build events. How does this differ from a regular Bazel rule or a genrule?

Select one answer

2.True or false about AXL's role and adoption:

Choose True or False for each sentence

rules_lint 2.0 implements 'aspect lint' as an AXL task wired via a MODULE.aspect file next to MODULE.bazel.
Current Aspect CLI uses AXL for configuration and custom tasks. The gRPC plugin architecture belongs to versions 2025.41 and earlier.
The simpler 'bazel run' custom-verb pattern (e.g., :target.publish) is often sufficient and should be preferred over AXL when only a single side-effect command is needed.

3.When does AXL become the right choice over shell scripts for outer-workflow automation?

Select one answer

0 of 3 answered

Footnotes

  1. Sponsored Lightning Talk: Beyond Make Serve: Starlarkification for Tasks - Alex Eagle, Aspect Build — AXL as a Starlark task layer, ctx.bazel.build(), BEP streaming, WASM helpers, PTY multi-run, and coverage/lint examples 1 2 3 4 5 6 7 8

  2. The 'outside of Bazel' pattern — why repo-wide archives, query-driven collectors, Gazelle, and coverage often belong in a task runner layer outside the build graph 1 2 3

  3. Using Macros to Create Custom Verbs — the simpler bazel run + macro pattern for .publish / .accept style side-effect verbs 1 2

  4. Aspect's rules_lint Reaches 2.0 — concrete AXL adoption via aspect lint / aspect format and MODULE.aspect next to MODULE.bazel

  5. Aspect CLI upstream README — current .aspect/config.axl and .aspect/*.axl loading model, custom task example, and aspect help discovery command

  6. Rules_lint: Formatting and Linting All Languages - Alex Eagle, Aspect Build Systems — linting architecture where Bazel/aspects do the graph work and the outer workflow decides how results are consumed