4.2.9 OutputGroupInfo & Output Groups
recommendedOutputGroupInfo gives a rule a second way to publish files: not as the target's default build result, and not as a semantic provider for downstream rules, but as named groups that users and tools can request when they need a specific slice of the target's outputs.1 It is the rule-authoring answer to "this target can produce more files than I want from every normal bazel build."
The ordinary build result
Tools can request the file. A provider gives it semantic meaning.
Useful on demand, noisy by default
A marker for an execution-phase validation action
Default Outputs Are Not Every Output
When a user runs bazel build //pkg:target, Bazel does not run every action the rule declared. It runs the actions needed for the requested output files and their transitive action dependencies.2 DefaultInfo(files = ...) chooses the ordinary output set for that command-line request. If a rule does not return DefaultInfo or omits files, Bazel falls back to all predeclared outputs.3
That boundary is useful. A library rule may produce a compiled archive as its default output, but also produce debug symbols, diagnostics, generated manifests, or machine-readable reports. If all of those files go into DefaultInfo.files, every normal build gets heavier. If none of them go into a default output or output group, their actions may never run when the target is built directly, because no requested artifact reaches them.4
Output groups sit between those two extremes. An output group is a named set of files that can be requested explicitly from a BUILD file through filegroup or from the command line with --output_groups.5 For rule authors, the Starlark surface is the built-in OutputGroupInfo provider.
Declaring Named Groups
OutputGroupInfo is unusual among built-in providers because its constructor accepts arbitrary keyword names. Each keyword becomes one output group, and the value is a depset of File objects.6
def _bundle_impl(ctx):
bundle = ctx.actions.declare_file(ctx.label.name + ".zip")
debug_manifest = ctx.actions.declare_file(ctx.label.name + ".debug.json")
lint_report = ctx.actions.declare_file(ctx.label.name + ".lint.txt")
ctx.actions.run(
executable = ctx.executable._packager,
inputs = ctx.files.srcs,
outputs = [bundle, debug_manifest],
arguments = [bundle.path, debug_manifest.path],
)
ctx.actions.run(
executable = ctx.executable._linter,
inputs = ctx.files.srcs,
outputs = [lint_report],
arguments = [lint_report.path],
)
return [
DefaultInfo(files = depset([bundle])),
OutputGroupInfo(
debug_files = depset([debug_manifest]),
lint_reports = depset([lint_report]),
),
]
Now the default build asks for the bundle:
bazel build //pkg:app_bundle
A developer or tool that wants only the debug manifest can request that group:
bazel build //pkg:app_bundle --output_groups=debug_files
That unprefixed form replaces the normal default output request. To build the ordinary bundle and the debug manifest in one invocation, add the group with +:
bazel build //pkg:app_bundle --output_groups=+debug_files
The BUILD-file request surface is filegroup. This gives the selected group an ordinary target label, which is useful when a package wants a named handle for a generated report without changing the producing rule's default outputs.5
filegroup(
name = "app_bundle_debug_files",
srcs = [":app_bundle"],
output_group = "debug_files",
)
The key design point is that output groups are request surfaces, not dependency contracts. Do not use OutputGroupInfo to convey specific file categories to the actions of consuming rules. Define a rule-specific provider for that instead.7 If another rule needs the debug manifest as an input, publish a custom provider from 4.2.7 Custom Provider Declaration. If a human, CI job, IDE, or aspect result needs to ask Bazel to build and download that file category, use an output group.
Output Groups Fit Tooling
Named groups are especially useful when the consumer is outside the normal dependency graph. cquery --output=files reports only files advertised in the requested output groups, and the Starlark cquery API can inspect target.output_groups. The official cquery docs show reading target.output_groups.compilation_outputs for a cc_library.8 That is the tooling side of the same contract: the rule advertises named files, and a tool chooses which group to request or inspect.
Aspects also participate in this model. A rule target and an aspect applied to that target may both return OutputGroupInfo, and Bazel merges the resulting providers as long as they do not define the same group names.9 That is why aspect-generated lint reports, IDE metadata, or source indexes usually land in output groups rather than in the target's default outputs. The broader aspect design belongs in 4.8.2 Aspect Implementation Basics. Here the rule-authoring lesson is simpler: choose group names as part of the public tooling API.
For debugging, output groups are adjacent to the query tools from 5.2.2 bazel cquery — Configured Graph and 5.2.3 bazel aquery — Action Graph. cquery can show which group files a configured target exposes. aquery shows the actions that produce those files once they become requested outputs. Use both when a report exists in the provider but does not appear in the build result.
Validation Actions Use A Special Group
Validation actions are the most important special case. The validation-layer decision — when a check belongs in attr.*, fail(), provider init, or an execution action — is covered in 4.2.4 Error Handling & Validation. This article only needs the output-group part: artifact-content checks often produce a marker or report that is not an input to the main build artifact.10
Bazel's documented solution is the special _validation output group. A rule puts the validation action's marker output in OutputGroupInfo(_validation = depset([validation_output])). The marker does not go into DefaultInfo and should not be added as an input to unrelated actions.11,12
return [
DefaultInfo(files = depset([main_output])),
OutputGroupInfo(_validation = depset([validation_marker])),
]
The _validation group is special because Bazel requests its outputs by default whenever the target builds, alongside DefaultInfo.files and regardless of --output_groups.13 That means the validation marker does not need to be wired into another action's inputs to run. The rule can keep validation effects out of consumer actions while still guaranteeing the check happens on a normal build. The behavior has documented limits: validation actions are skipped when the target is reached as a tool, through an implicit dependency such as a private _tool attribute, or in the exec configuration, and the whole feature can be disabled with --run_validations=false.14,15 Within those rules, normal caching applies — if the validation action's inputs have not changed and the action previously succeeded, Bazel does not rerun it.13
This makes validation actions a built-in quality gate for a rule. If the check is intrinsic to the rule's correctness, put it in _validation. If an infrastructure team wants to overlay checks across many existing rules without changing those rules, the decision moves toward aspects and the trade-off in 4.8.3 Validation Actions vs Aspects.16
Naming Is API Design
Output group names become strings that users, CI scripts, IDEs, and aspects may depend on. Keep them stable and descriptive: debug_files, lint_reports, source_maps, ide_info. Avoid using an output group as a dumping ground for every non-default file. A group should represent one request someone can explain.
Also avoid over-promising transitive semantics. If a group contains only this target's local report, name it locally. If it intentionally aggregates reports from dependencies, build that aggregation explicitly with depsets and document the behavior. The same collection discipline from 4.1.5 depset vs list applies here because output group values are depsets too.
The mini-ruleset's
OutputGroupInfo
publishes a direct manifest, a transitive manifest rollup, and the special
_validation marker without adding those files to the target's normal default
output set.
Use DefaultInfo.files for the files a normal build of the target should produce. Use custom providers for files and metadata that dependent rules need as a semantic contract. Use OutputGroupInfo for named artifact sets that users and tools can request selectively.
The special _validation group is the exception with scheduling semantics: it makes execution-phase checks run without wiring their marker outputs into unrelated actions.
Check your understanding · 3 questions
1.Match each output-group request surface to what it asks Bazel to build.
Drag each answer onto the matching prompt, or click an answer and then click a prompt
bazel build //pkg:app_bundlebazel build //pkg:app_bundle --output_groups=debug_filesbazel build //pkg:app_bundle --output_groups=+debug_filesfilegroup(srcs = [":app_bundle"], output_group = "debug_files")2.Which are good uses of OutputGroupInfo?
Select all that apply
3.True or false: validation actions and output groups.
Choose True or False for each sentence
_validation outputs are requested alongside normal target outputs in ordinary builds, with documented skip cases such as tool and exec-configuration dependencies.--run_validations controls whether validation actions run, and defaults to true.--output_groups=+debug_files requests debug_files in addition to the target's normal default outputs.Footnotes
-
OutputGroupInfo — provider definition for rule output groups. ↩
-
Rules — requested files determine which reachable actions execute. ↩
-
DefaultInfo —
filesfield and fallback to predeclared outputs. ↩ -
Frequently Asked Questions — unrequested output actions do not execute unless the file is requested directly, via an output group, or through default outputs. ↩
-
Bazel Glossary — output group definition and command-line /
filegrouprequest surfaces. ↩1 ↩2 -
Rules —
OutputGroupInfoaccepts arbitrary group names as keyword arguments. ↩ -
Rules — output groups should not replace rule-specific providers for consumer actions. ↩
-
Configurable Query (cquery) — files output respects
--output_groups. Starlark example readstarget.output_groups.compilation_outputs. ↩ -
Rules — rule and aspect
OutputGroupInfoproviders are merged when group names do not collide. ↩ -
Rules — validation actions run checks that require artifacts during execution. ↩
-
Validation actions: correct builds off the critical path — historical dummy-output workarounds and critical-path cost. ↩
-
Rules —
_validationoutput group example and guidance not to add validation outputs to other action inputs. ↩ -
Rules —
_validationoutputs are always requested while normal caching and incrementality still apply. ↩1 ↩2 -
Rules — documented cases where validation actions are not run. ↩
-
Rules —
--run_validationscontrols validation actions and defaults to true. ↩ -
Validation actions: correct builds off the critical path — Q&A comparing validation actions with aspects and their overhead. ↩