Aspects Last updated Mar 24, 2025
An aspect may be thought of as an extension of the normal build program. The core of an aspect is one or more build actions that are executed in addition to or instead of the normal build actions. Typical examples are aspects that run formatters or linters.
There are two ways to implement an aspect in this sense: external and inline.
External aspects
External aspects use Bazel’s aspect mechanism, which is designed to support the use of external tools (tools not included in the toolchain) to extend build actions throughout a dependency graph defined by the aspect. External aspects specify a set of rule attributes that determine a “shadow” dependency graph, and Bazel will apply the aspect to all the targets in the dependency graph.
Inline aspects
The other method, which is not generally recognized, is to use Bazel’s
--output_groups
facility to enable inline aspects. An inline
aspect is a build action that is directly supported by a build rule
but only executed when the user requests a specific output group.
Inline aspects are local; they do not propagate to additional build
targets in the way that
For example, the OCaml SDK includes a tool, ocamlobjinfo
, that dumps
textual (human-readable) metadata about compiled artifacts to
stdout
. To use ocamlobjinfo
, the developer must first compile the
object of interest - say, //foo/bar:A
, producing foo/bar/a.cmo
-
and then run ocamlobjinfo
against the output.
Without an inline aspect, this would be impractical for Bazel users.
Building a target puts the outputs in a filesystem area controlled by
Bazel, on a path constructed by Bazel. So to apply ocamlobjinfo
to
the output of $ bazel build foo/bar:A
, one would have to obtain from
Bazel the path of the output object. This is possible, but very cumbersome.
But an inline aspect makes this almost trivial. The ocaml_module
rule includes in its implementation function a build action to compile
its input. It also includes an inline aspect that runs ocamlobjinfo
against output of the compile action. The inline aspect is just a
build action that takes the output of the normal compile action as its
input, and uses ctx.actions.run_shell
to run ocamlobjinfo
. (Use of
run_shell
is required in this case because ocamlobjinfo
writes to
stdout
and does not have a parameter like -o
to redirect output to
a file.)
What makes this work as an aspect is that the output of the
ocamlobinfo
action is listed only in the OutputGroupInfo
provider,
as field modinfo
. That means that the aspect action will only be
executed if the user passes --output_groups=modinfo
to the build
command. So a normal $ bazel build foo/bar:A
will ignore the inline
aspect and proceed as usual. But $ bazel build foo/bar:A
--output_groups=modinfo
tells bazel that only the output specified as
the modinfo
field of the OutputGroupInfo
provider need be built.
Since that field contains the output of the ocamlobjinfo
build
action, which in turn depends on the output of the compile action, the
result is that the target will be compile as normal, and then the
ocamlobjinfo
action will be run against the compilation output,
producing the modinfo
requested by the user.
Inline aspects only make sense for actions that use tools provided by the toolchain. An inline action that uses an external tool would make the ruleset dependent on that tool, whether the user wants to use it or not.