4.2.9 OutputGroupInfo & Output Groups

recommended

OutputGroupInfo 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."

Publish each rule output on the surface that matches its consumer
Ask who needs each artifact: normal builds, tools, dependent rules, or automatic validation.
Rule target
//pkg:app_bundle produces several artifacts
analysis chooses how each is published
bundle.zip main

The ordinary build result

debug.json metadata

Tools can request the file. A provider gives it semantic meaning.

lint.txt report

Useful on demand, noisy by default

validation.marker check

A marker for an execution-phase validation action

DefaultInfo.files
Normal build result
requested by bazel build
files = depset([bundle.zip])
OutputGroupInfo
Named request surfaces
for users, CI, IDEs, aspects
debug_files lint_reports
--output_groups=+debug_files
Custom provider
Rule-to-rule meaning
dependency contract, not a download knob
BundleDebugInfo schema_version
_validation group
Default validation request
runs without becoming an action input
_validation = depset([validation.marker])
Output groups let humans and tools request files. --output_groups=name replaces defaults while --output_groups=+name adds to them.

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.

key takeaway

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

Answers
bazel build //pkg:app_bundle
bazel build //pkg:app_bundle --output_groups=debug_files
bazel build //pkg:app_bundle --output_groups=+debug_files
filegroup(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.
Validation marker outputs should be added as inputs to the main compile action so Bazel knows to run them.
--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.
0 of 3 answered

Footnotes

  1. OutputGroupInfo — provider definition for rule output groups.

  2. Rules — requested files determine which reachable actions execute.

  3. DefaultInfofiles field and fallback to predeclared outputs.

  4. Frequently Asked Questions — unrequested output actions do not execute unless the file is requested directly, via an output group, or through default outputs.

  5. Bazel Glossary — output group definition and command-line / filegroup request surfaces. 1 2

  6. RulesOutputGroupInfo accepts arbitrary group names as keyword arguments.

  7. Rules — output groups should not replace rule-specific providers for consumer actions.

  8. Configurable Query (cquery) — files output respects --output_groups. Starlark example reads target.output_groups.compilation_outputs.

  9. Rules — rule and aspect OutputGroupInfo providers are merged when group names do not collide.

  10. Rules — validation actions run checks that require artifacts during execution.

  11. Validation actions: correct builds off the critical path — historical dummy-output workarounds and critical-path cost.

  12. Rules_validation output group example and guidance not to add validation outputs to other action inputs.

  13. Rules_validation outputs are always requested while normal caching and incrementality still apply. 1 2

  14. Rules — documented cases where validation actions are not run.

  15. Rules--run_validations controls validation actions and defaults to true.

  16. Validation actions: correct builds off the critical path — Q&A comparing validation actions with aspects and their overhead.