aboutsummaryrefslogtreecommitdiffhomepage
path: root/site/docs/skylark/aspects.md
diff options
context:
space:
mode:
Diffstat (limited to 'site/docs/skylark/aspects.md')
-rw-r--r--site/docs/skylark/aspects.md187
1 files changed, 187 insertions, 0 deletions
diff --git a/site/docs/skylark/aspects.md b/site/docs/skylark/aspects.md
new file mode 100644
index 0000000000..36d9d7b660
--- /dev/null
+++ b/site/docs/skylark/aspects.md
@@ -0,0 +1,187 @@
+---
+layout: documentation
+title: Skylark Aspects
+---
+# Aspects
+
+**Status: Experimental**. We may make breaking changes to the API, but we will
+ help you update your code.
+
+Aspects allow augmenting build dependency graphs with additional information
+and actions. Some typical scenarios when aspects can be useful:
+* IDEs that integrate Bazel can use aspects to collect information about the
+ project
+* Code generation tools can leverage aspects to execute on their inputs in
+ "target-agnostic" manner. As an example, BUILD files can specify a hierarchy
+ of [protobuf](https://developers.google.com/protocol-buffers/) library
+ definitions, and language-specific rules can use aspects to attach
+ actions generating protobuf support code for a particular language
+
+## Aspect basics
+
+Bazel BUILD files provide a description of a project’s source code: what source
+files are part of the project, what artifacts (_targets_) should be built from
+those files, what the dependencies between those files are, etc. Bazel uses
+this information to perform a build, that is, it figures out the set of actions
+needed to produce the artifacts (such as running compiler or linker) and
+executes those actions. Bazel accomplishes this by constructing a _dependency
+graph_ between targets and visiting this graph to collect those actions.
+
+Consider the following BUILD file:
+
+```python
+java_library(name = 'W', ...)
+java_library(name = 'Y', deps = [':W'], ...)
+java_library(name = 'Z', deps = [':W'], ...)
+java_library(name = 'Q', ...)
+java_library(name = 'T', deps = [':Q'], ...)
+java_library(name = 'X', deps = [':Y',':Z'], runtime_deps = [':T'], ...)
+```
+
+This BUILD file defines a dependency graph shown in Fig 1.
+
+![Build Graph](build-graph.svg)
+
+Bazel analyzes this dependency graph by calling implementations of
+[rules](rules.md) (in this case "java_library" starting from leaves of
+the dependency graph). These implementations generate actions that build
+artifacts (such as Jar files), and provide information (such as locations
+and names of those artifacts) to their dependencies in providers that
+they return. Their dependencies can access those providers through the
+[Target object](lib/Target.html). In other words, every target
+defined in the BUILD file generates a node in the dependency graph, and
+the appropriate rule implementation function is called for every node.
+
+Aspects are similar to rules in that they have an implementation function that
+generates actions and returns providers. However, their power comes from
+the way the dependency graph is built for them. An aspect has an implementation
+and a list of all attributes it propagates along. Consider an aspect A that
+propagates along attributes named "deps". This aspect can be applied to
+a target X, yielding an aspect application node A(X). During its application,
+aspect A is applied recursively to all targets that X refers to in its "deps"
+attribute (all attributes in A's propagation list). Thus a single act of
+applying aspect A to a target X yields a "shadow graph" of the original
+dependency graph of targets (see Fig.2).
+
+![Build Graph with Aspect](build-graph-aspect.svg)
+
+The only edges that are shadowed are the edges along the attributes in
+the propagation set, thus the `runtime_deps` edge is not shadowed in this
+example. An aspect implementation function is then invoked on all nodes in
+the shadow graph similar to how rule implementations are invoked on the nodes
+of the original graph.
+
+## Defining aspects
+
+Aspect defintions are similiar to rule definitions. Let's take a look at
+the example:
+
+```python
+metal_proto_aspect = aspect(implementation = _metal_proto_aspect_impl,
+ attr_aspects = ["deps"],
+ attrs = {
+ "_protoc" : attr.label(
+ default=Label("//tools:metal_protoc"),
+ executable = True
+ )
+ }
+)
+```
+
+Just like a rule, an aspect has an implementation function. ``attr_aspects``
+specify the aspect's propagation set: a list of attributes of rules along which
+the aspect propagates.
+
+``attrs`` defines a set of attributes for aspects. Aspects are only allowed
+to have private attributes of types ``label`` or ``label_list``. Attributes
+can be used to specify dependencies on tools or libraries that are needed
+for actions generated by aspects.
+
+### Implementation functions
+
+Aspect implementation functions are similiar to the rule implementation
+functions. They return [providers](rule.md#providers), can generate
+[actions](rule.md#actions) and take two arguments:
+* `target`: the target the aspect is being applied to.
+* [`ctx`](lib/ctx.html): `ctx` object that can be used to access attributes and
+ generate outputs and actions.
+
+Example:
+```python
+def _metal_proto_aspect_impl(target, ctx):
+ # For every `src` in proto_library, generate an output file
+ proto_sources = [f for src in ctx.rule.attrs.src
+ for f in src.files]
+ outputs = [ctx.new_file(f.short_path + ".metal")
+ for f in proto_sources]
+ ctx.action(
+ executable = ctx.executable._protoc,
+ argument = ...
+ inputs = proto_sources
+ outputs = outputs)
+ transitive_outputs = set(outputs)
+ for dep in ctx.attrs.deps:
+ transitive_outputs = transitive_outputs | dep.metal_proto.transitive_outputs
+ return struct(
+ metal_proto = struct(direct_outputs = outputs,
+ transitive_outputs = transitive_outputs))
+```
+
+The implementation function can access the attributes of the target rule via
+[`ctx.rule.attrs`](lib/ctx.html#rule). It can examine providers that are
+provided by the target to which it is applied (via the `target` argument).
+
+Just like a rule implementation function, an aspect implementation function
+returns a struct of providers that are accessible to its dependencies.
+* The set of providers for an aspect application A(X) is the union of providers
+ that come from the implementation of a rule for target X and from
+ the implementation of aspect A. It is an error if a target and an aspect that
+ is applied to it each provide a provider with the same name.
+* For the aspect implementation, the values of attributes along which
+ the aspect is propagated (from the 'attr_aspect' list) are replaced with
+ the results of an application of the aspect to them. For example, if target
+ X has Y and Z in its deps, `ctx.rule.attrs.deps` for A(X) will be [A(Y), A(Z)].
+ In the `_metal_proto_aspect_impl` function above, ctx.rule.attrs.deps will be
+ Target objects that are the results of applying the aspect to the 'deps'
+ of the original target to which the aspect has been applied.
+ That allows the aspect to examine `metal_proto` provider on them.
+
+
+## Applying aspects
+
+Aspect propagation can be initiated either from a rule or from the command line.
+
+### Applying aspects to rule attributes
+
+Rules can specify that they want to apply aspects to their depenedencies.
+The aspects to be applied to a particular attribute can be specified
+using the `aspects` parameter to `attr.label` or `attr.label_list` function:
+
+```python
+metal_proto_library = rule(implementation = _impl,
+ attrs = {
+ 'proto_deps' : attr.label_list(aspects = [metal_proto_aspect]),
+ },
+)
+```
+
+If a rule specifies an aspect on its attributes, the values of that attribute
+will be replaced by the result of aspect application to them (similiar to
+what happens during aspect propagation). Thus implementaion of
+`metal_proto_library` will have access to `metal_proto` providers
+on the target objects representing its `proto_deps` attribute values.
+
+### Applying aspects from command line.
+
+Aspects can also be applied on the command line, using the `--aspect` flag:
+
+
+```
+bazel build //java/com/company/example:main \
+ --aspect path/to/extension.bzl%metal_proto_aspect
+```
+
+`--aspect` flag takes one argument, which is a specification of the aspect in
+the format `<extension file path>%<aspect top-level name>`.
+
+