Skip to content

feat: scope .bzl edits to macro-produced rules via instantiation stack#436

Merged
tinder-maxwellelliott merged 2 commits into
Tinder:masterfrom
corypaik:bzl-seed-instantiation-stack
Jul 17, 2026
Merged

feat: scope .bzl edits to macro-produced rules via instantiation stack#436
tinder-maxwellelliott merged 2 commits into
Tinder:masterfrom
corypaik:bzl-seed-instantiation-stack

Conversation

@corypaik

Copy link
Copy Markdown
Contributor

Problem

packageBzlSeeds (added in #365) attributes a loaded .bzl's content digest to every
target in every package that load()s it
. That fixed the #259/#227 under-invalidation
(a macro edit that changed no rule attribute invalidated nothing), but it over-corrected:
editing one widely-load()ed rule .bzl now re-hashes every unrelated target in each
loading package — native rules, filegroups, source files — not just the targets that use
the rule.

In a repo where a single rule .bzl is loaded by hundreds of packages (each for one target),
a one-line edit to that .bzl reported ~130k impacted targets. In our case this was a
copybara target in each vendored directory, but would also have a similar effect for
any other lightweight config macro whose target nothing else in the package depends on
(e.g., a license target). With this fix our impacted set for that scenario is down to ~1k.

Change

Attribute each .bzl to the specific rules a macro produced, not the whole package.

RuleHasher.ruleBzlSeed builds a per-rule seed from the .bzl files in that rule's macro
instantiation stack (--proto:instantiation_stack), so a macro edit re-hashes only the
rules that macro created. Two signals combine to cover every edit:

Source-file target hashes drop the seed entirely (a source file's hash is its content).

When instantiation stacks are unavailable (older Bazel / the flag off), BuildGraphHasher
falls back to the existing per-package seed, so the #365 behavior is preserved there.

Coverage

edit invalidates the consumer via
macro (same file as rule) instantiation stack + impl hash
macro in a different file than the rule instantiation stack
rule definition file (any file) $rule_implementation_hash
macro wrapping a native rule (#259/#227) instantiation stack
unrelated macro added elsewhere (#365) — (correctly not impacted)
external rule (e.g. cc_library from @rules_cc) impl-hash attribute + external dep-edge (unchanged)

Unrelated targets that merely share a package with a macro-produced target are no longer
swept in.

Cost

--proto:instantiation_stack adds ~2–7% to query-proto size on a macro-heavy graph. The
stack is already computed by Bazel (it backs macro-aware error messages), so there is no
meaningful query-time cost. It is on by default; the package-seed fallback keeps older
Bazel working.

Tests

New e2e fixtures + tests:

  • rule_bzl_overtrigger — editing an existing widely-loaded rule .bzl impacts the target
    that uses it but not unrelated native targets in the same package.
  • cross_file_macro_rule — a macro in one file loading a rule from another; editing
    either file impacts the consumer, and a sibling native target stays out.

The existing regression tests still pass: macro_invalidation (#259/#227) and
bzl_seed_overtrigger (#365).

packageBzlSeeds (Tinder#365) attributes a loaded .bzl's digest to every target in every
package that load()s it. So editing one widely-loaded rule .bzl re-hashes every
unrelated target in each loading package -- native rules, filegroups, source files --
not just the targets that use the rule.

Attribute each .bzl to the specific rules a macro produced instead. RuleHasher.ruleBzlSeed
builds a per-rule seed from the .bzl files in the rule's macro instantiation stack
(--proto:instantiation_stack). $rule_implementation_hash (already hashed via
BazelRule.digest) covers a rule class's own definition .bzl and its transitive loads, so
editing a rule file still invalidates its consumers even across files. Source files drop
the seed. The package-wide seed remains as a fallback when instantiation stacks are
unavailable (older Bazel), preserving the Tinder#365 fix there.

--proto:instantiation_stack adds ~2-7% to query-proto size; the stack is already computed
by Bazel, so there is no meaningful time cost.

Tests: editing a rule .bzl (co-located, or a cross-file macro/rule pair) impacts the
targets that use it but not unrelated same-package targets; editing either the macro or
the rule invalidates the consumer. The existing Tinder#259/Tinder#227 (macro_invalidation) and Tinder#365
(bzl_seed_overtrigger) tests still pass.
@tinder-maxwellelliott
tinder-maxwellelliott self-requested a review July 16, 2026 21:45

@tinder-maxwellelliott tinder-maxwellelliott left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great fix! Thanks

@tinder-maxwellelliott
tinder-maxwellelliott merged commit 700780a into Tinder:master Jul 17, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants