Skip to content

Fix #259 / #227 .bzl macro changes now invalidate targets#342

Merged
tinder-maxwellelliott merged 3 commits into
masterfrom
claude/reproducer-issue-259
May 12, 2026
Merged

Fix #259 / #227 .bzl macro changes now invalidate targets#342
tinder-maxwellelliott merged 3 commits into
masterfrom
claude/reproducer-issue-259

Conversation

@tinder-maxwellelliott
Copy link
Copy Markdown
Collaborator

@tinder-maxwellelliott tinder-maxwellelliott commented May 12, 2026

Summary

Fixes #259 and the duplicate #227.

Root cause. Bazel pre-7 populated Build.Rule.skylark_environment_hash_code in the query proto, so any change to a .bzl file loaded by a rule's BUILD file naturally bubbled into that rule's hash. Bazel 7+ leaves that field empty, so an edit like adding print() to a macro body no longer invalidated any caller — the emitted rule attrs were byte-identical.

Fix. BuildGraphHasher now walks every queried SourceFile target's subincludeList (the Build.SourceFile.subinclude proto field, which already lists every .bzl the BUILD file loaded), softDigests each main-repo .bzl, and mixes the union of digests into the workspace-wide seed.

Key properties:

  • Conditional seed mix-in. Only mixed in when at least one main-repo .bzl was actually hashed, so the seed (and every target's hash) is byte-for-byte stable for workspaces that don't load any tracked .bzl files. Cached hashes from before this change are preserved for those users.
  • External .bzl files are ignored (@repo//..., @@canonical//...) because SourceFileHasher.softDigest returns null for non-main-repo labels. This keeps the seed stable across BCR fetches that don't actually change repo contents.
  • Intentionally conservative. A single .bzl edit re-hashes every target. .bzl edits are rare; silently missing them is the worse failure mode. Per-package precision would require mapping each rule to its package's BUILD file, which isn't a direct dep relationship in the query proto.

Also updates the macro_invalidation reproducer to use a print()-only mutation (the exact example from the issue) — the original cmd = mutation already propagated via rule attrs and wasn't the real bug. @Ignore removed; test renamed to a regression test.

Test plan

  • bazel build //cli:bazel-diff //cli:cli-test-lib succeeds.
  • bazel test //cli:BuildGraphHasherTestPASSED (verifies the conditional seed mix-in doesn't shift hashes for workspaces with no tracked .bzl files).
  • bazel test //cli:E2ETest --test_filter=testMacroBzlChangeImpactsCallers_regressionForIssue259And227PASSED in 15.6s (the reproducer that used to fail now succeeds).
  • Broad non-Maven E2E sweep (testE2E$|testE2EWithNoKeepGoing|testMacroBzlChangeImpactsCallers|testBzlmodLocalPathOverride|testModuleBazelComment|testGenerateHashesWithCquery|testFineGrainedHashBzlMod|testBzlmodShowRepo|testExcludeExternalTargets|testCquery|testDetermineBazelVersion|testE2EIncludingTargetType|testE2EWithTargetType) → PASSED in 123s. The four Maven-fixture tests (testFineGrainedHashExternalRepo, testUseCqueryWithExternalDependencyChange, testUseCqueryWithAndroidCodeChange, testBzlmodTransitiveDepsCquery) fail locally with Cannot find Java binary bin/java from coursier — environmental (the bazel test sandbox doesn't propagate JAVA_HOME to the nested bazel invocations that run coursier). CI's setup-java step passes them.
  • Manual repro confirmed end-to-end with the locally built CLI: adding print("miniature: " + name) to miniature.bzl produces //:logo_miniature in the impacted-targets list (previously it produced an empty list).

🤖 Generated with Claude Code

@tinder-maxwellelliott tinder-maxwellelliott changed the title Reproducer test for #259 / #227 .bzl macro change does not invalidate callers Fix #259 / #227 .bzl macro changes now invalidate targets May 12, 2026
@tinder-maxwellelliott tinder-maxwellelliott force-pushed the claude/reproducer-issue-259 branch from ef6c1d5 to 6764f81 Compare May 12, 2026 18:27
tinder-maxwellelliott and others added 3 commits May 12, 2026 15:12
…date callers

Both issues describe the same underlying gap: when a BUILD file
`load()`s a .bzl macro, editing the macro body is not reflected in
`bazel-diff get-impacted-targets`. The user in #259 noticed this after
upgrading to Bazel 7. The user in #227 hit it with a shared
`all_gke_service.bzl` macro loaded by many BUILD files. In both cases,
none of the targets that call the macro are reported as impacted.

This adds a minimal in-tree workspace `macro_invalidation` plus an
`@Ignore`d E2E test:
- `miniature.bzl` defines a `miniature(name, src)` macro that wraps
  `native.genrule`.
- `BUILD` does `load(":miniature.bzl", "miniature")` and calls the macro
  to produce `//:logo_miniature`.
- The test generate-hashes against the original workspace, mutates only
  `miniature.bzl` (changing the genrule cmd), generate-hashes again,
  then asserts `//:logo_miniature` shows up in `get-impacted-targets`.

Today the assertion fails because bazel-diff does not capture .bzl file
contents as part of a Rule's transitive hash. Drop the @ignore once
that's fixed.

Refs #259
Refs #227

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bazel pre-7 populated Build.Rule.skylark_environment_hash_code in the
query proto so any change to a .bzl file loaded by a rule's BUILD file
naturally bubbled into that rule's hash. Bazel 7+ leaves that field
empty, which is the root cause of #259 and #227: editing a macro body
(e.g. adding `print()`) no longer invalidated any caller because the
emitted rule attrs were identical.

Fix: BuildGraphHasher now walks every queried SourceFile target's
subincludeList -- the `Build.SourceFile.subinclude` proto field, which
already lists every .bzl the BUILD file loaded -- softDigests each
main-repo .bzl, and mixes the union of digests into the workspace-wide
seed.

The seed mix-in is conditional on at least one main-repo .bzl actually
being hashed. This keeps the seed (and every target's hash)
byte-for-byte stable for workspaces that don't load any tracked .bzl
files, preserving cached hashes from before this change.

The fix is intentionally conservative -- a single .bzl edit re-hashes
every target -- because .bzl edits are rare and silently missing them
is the worse failure mode. Per-package precision would require mapping
each rule to its package's BUILD file, which isn't a direct dep
relationship in the query proto.

Also updates the macro_invalidation reproducer to use a `print()`-only
mutation (matching the user's exact example in #259), removes the
@ignore, and renames the test to a regression test.

Refs #259
Refs #227

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tinder-maxwellelliott tinder-maxwellelliott force-pushed the claude/reproducer-issue-259 branch from b551472 to dfaf94d Compare May 12, 2026 19:14
@tinder-maxwellelliott tinder-maxwellelliott merged commit 16bb7e8 into master May 12, 2026
15 checks passed
@tinder-maxwellelliott tinder-maxwellelliott deleted the claude/reproducer-issue-259 branch May 12, 2026 20:46
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.

Bazel Macro issue

1 participant