| --- |
| layout: documentation |
| title: Build Event Protocol Examples |
| --- |
| |
| # Build Event Protocol Examples |
| |
| |
| The full specification of the Build Event Protocol can be found in its protocol |
| buffer definition. However, it might be helpful to build up some intuition |
| before looking at the specification. |
| |
| Consider a simple Bazel workspace that consists of two empty shell scripts |
| `foo.sh` and `foo_test.sh` and the following `BUILD` file: |
| |
| ```bash |
| sh_library( |
| name = "foo_lib", |
| srcs = ["foo.sh"], |
| ) |
| |
| sh_test( |
| name = "foo_test", |
| srcs = ["foo_test.sh"], |
| deps = [":foo_lib"], |
| ) |
| ``` |
| |
| When running `bazel test ...` on this project the build graph of the generated |
| build events will resemble the graph below. The arrows indicate the |
| aforementioned parent and child relationship. Note that some build events and |
| most fields have been omitted for brevity. |
| |
|  |
| |
| Initially, a `BuildStarted` event is published. The event informs us that the |
| build was invoked through the `bazel test` command and announces child events: |
| |
| * `OptionsParsed` |
| * `WorkspaceStatus` |
| * `CommandLine` |
| * `UnstructuredCommandLine` |
| * `BuildMetadata` |
| * `BuildFinished` |
| * `PatternExpanded` |
| * `Progress` |
| |
| The first three events provide information about how Bazel was invoked. |
| |
| The `PatternExpanded` build event provides insight |
| into which specific targets the `...` pattern expanded to: |
| `//foo:foo_lib` and `//foo:foo_test`. It does so by declaring two |
| `TargetConfigured` events as children. Note that the `TargetConfigured` event |
| declares the `Configuration` event as a child event, even though `Configuration` |
| has been posted before the `TargetConfigured` event. |
| |
| Besides the parent and child relationship, events may also refer to each other |
| using their build event identifiers. For example, in the above graph the |
| `TargetComplete` event refers to the `NamedSetOfFiles` event in its `fileSets` |
| field. |
| |
| Build events that refer to files (i.e. outputs) usually don’t embed the file |
| names and paths in the event. Instead, they contain the build event identifier |
| of a `NamedSetOfFiles` event, which will then contain the actual file names and |
| paths. The `NamedSetOfFiles` event allows a set of files to be reported once and |
| referred to by many targets. This structure is necessary because otherwise in |
| some cases the Build Event Protocol output size would grow quadratically with |
| the number of files. A `NamedSetOfFiles` event may also not have all its files |
| embedded, but instead refer to other `NamedSetOfFiles` events through their |
| build event identifiers. |
| |
| Below is an instance of the `TargetComplete` event for the `//foo:foo_lib` |
| target from the above graph, printed in protocol buffer’s JSON representation. |
| The build event identifier contains the target as an opaque string and refers to |
| the `Configuration` event using its build event identifier. The event does not |
| announce any child events. The payload contains information about whether the |
| target was built successfully, the set of output files, and the kind of target |
| built. |
| |
| ```json |
| { |
| "id": { |
| "targetCompleted": { |
| "label": "//foo:foo_lib", |
| "configuration": { |
| "id": "544e39a7f0abdb3efdd29d675a48bc6a" |
| } |
| } |
| }, |
| "completed": { |
| "success": true, |
| "outputGroup": [{ |
| "name": "default", |
| "fileSets": [{ |
| "id": "0" |
| }] |
| }], |
| "targetKind": "sh_library rule" |
| } |
| } |
| ``` |
| |
| ## Aspect Results in BEP |
| |
| Ordinary builds evaluate actions associated with `(target, configuration)` |
| pairs. When building with [aspects](skylark/aspects.html) enabled, Bazel |
| additionally evaluates targets associated with `(target, configuration, |
| aspect)` triples, for each target affected by a given enabled aspect. |
| |
| Evaluation results for aspects are available in BEP despite the absence of |
| aspect-specific event types. For each `(target, configuration)` pair with an |
| applicable aspect, Bazel publishes an additional `TargetConfigured` and |
| `TargetComplete` event bearing the result from applying the aspect to the |
| target. For example, if `//:foo_lib` is built with |
| `--aspects=aspects/myaspect.bzl%custom_aspect`, this event would also appear in |
| the BEP: |
| |
| ```json |
| { |
| "id": { |
| "targetCompleted": { |
| "label": "//foo:foo_lib", |
| "configuration": { |
| "id": "544e39a7f0abdb3efdd29d675a48bc6a" |
| }, |
| "aspect": "aspects/myaspect.bzl%custom_aspect" |
| } |
| }, |
| "completed": { |
| "success": true, |
| "outputGroup": [{ |
| "name": "default", |
| "fileSets": [{ |
| "id": "1" |
| }] |
| }] |
| } |
| } |
| ``` |
| |
| Note that the only difference between the IDs is the presence of the `aspect` |
| field. A tool that does not check the `aspect` ID field and accumulates output |
| files by target may conflate target outputs with aspect outputs. |
| |
| ## Consuming `NamedSetOfFiles` |
| |
| Determining the artifacts produced by a given target (or aspect) is a common |
| BEP use-case that can be done efficiently with some preparation. This section |
| discusses the recursive, shared structure offered by the `NamedSetOfFiles` |
| event, which matches the structure of a Starlark [Depset](skylark/depsets.html). |
| Consumers must take care to avoid quadratic algorithms when processing |
| `NamedSetOfFiles` events because large builds can contain tens of thousands of |
| such events, requiring hundreds of millions operations in a traversal with |
| quadratic complexity. |
| |
|  |
| |
| A `NamedSetOfFiles` event always appears in the BEP stream *before* a |
| `TargetComplete` or `NamedSetOfFiles` event that references it. This is the |
| inverse of the "parent-child" event relationship, where all but the first event |
| appears after at least one event announcing it. A `NamedSetOfFiles` event is |
| announced by a `Progress` event with no semantics. |
| |
| Given these ordering and sharing constraints, a typical consumer must buffer all |
| `NamedSetOfFiles` events until the BEP stream is exhausted. The following JSON |
| event stream and Python code demonstrate how to populate a map from |
| target/aspect to built artifacts in the "default" output group, and how to |
| process the outputs for a subset of built targets/aspects: |
| |
| <p> |
| <button class="btn btn-primary" type="button" data-toggle="collapse" |
| data-target="#collapseNamedSetJson" aria-expanded="false" |
| aria-controls="collapseNamedSetJson"> |
| Show/Hide BEP JSON |
| </button> |
| </p> |
| {: .collapse #collapseNamedSetJson} |
| |
| ```python |
| named_sets = {} # type: dict[str, NamedSetOfFiles] |
| outputs = {} # type: dict[str, dict[str, set[str]]] |
| |
| for event in stream: |
| kind = event.id.WhichOneof("id") |
| if kind == "named_set": |
| named_sets[event.id.named_set.id] = event.named_set_of_files |
| elif kind == "target_completed": |
| tc = event.id.target_completed |
| target_id = (tc.label, tc.configuration.id, tc.aspect) |
| outputs[target_id] = {} |
| for group in event.completed.output_group: |
| outputs[target_id][group.name] = {fs.id for fs in group.file_sets} |
| |
| for result_id in relevant_subset(outputs.keys()): |
| visit = outputs[result_id].get("default", []) |
| seen_sets = set(visit) |
| while visit: |
| set_name = visit.pop() |
| s = named_sets[set_name] |
| for f in s.files: |
| process_file(result_id, f) |
| for fs in s.file_sets: |
| if fs.id not in seen_sets: |
| visit.add(fs.id) |
| seen_sets.add(fs.id) |
| ``` |