The bzl Gazelle plugin sets deps correctly when it first generates a bzl_library, but never updates them afterwards. Once a target exists in a BUILD file, its deps are frozen no matter how the load() statements in its srcs change.
Reproduction
- Run Gazelle with the
bzl plugin so that it generates a bzl_library.
- Add a new
load() statement to a .bzl file whose bzl_library already exists.
- Re-run Gazelle.
The new dependency is never added. Deleting a correct dep and re-running does not restore it either.
Cause
Resolve() computes the right value and calls r.SetAttr("deps", deps), so the resolution itself is fine. The problem is the KindInfo in gazelle/bzl/gazelle.go:
var kinds = map[string]rule.KindInfo{
"bzl_library": {
NonEmptyAttrs: map[string]bool{"srcs": true, "deps": true},
MergeableAttrs: map[string]bool{"srcs": true},
},
}
deps appears in neither MergeableAttrs nor ResolveAttrs. Gazelle's merger selects MergeableAttrs in the pre-resolve phase and ResolveAttrs in the post-resolve phase (merger/merger.go, getMergeAttrs). Since deps is resolved in the post-resolve phase and no ResolveAttrs is declared, the computed value is discarded for any rule that already exists. A newly generated rule has nothing to merge against, which is why fresh targets look correct.
Gazelle's own Go language declares ResolveAttrs: map[string]bool{"deps": true} in language/go/kinds.go.
Existing fix
#621 already proposes exactly this change and adds a regression fixture. It has been open since May with no review.
A caveat worth knowing before #621 lands
I applied #621's change locally against a repo with roughly 100 bzl_library targets. It behaves well overall, and notably it also removes stale deps left over from loads that no longer exist, which is a second class of drift.
It does, however, surface #559. Enabling ResolveAttrs means the guessed labels for cross-repository loads now get written onto existing targets, replacing correct hand-written ones. In our case the source has:
load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
The correct dependency is @rules_cc//cc/common (the package's :common target). With #621 applied, Gazelle rewrites it to the guessed labels, neither of which exists:
- "@rules_cc//cc/common",
+ "@rules_cc//cc/common:cc_common",
+ "@rules_cc//cc/common:cc_info",
ERROR: no such target '@@rules_cc+//cc/common:cc_common': target 'cc_common' not declared in package 'cc/common' (did you mean common, or cc_common.bzl?)
Today that mis-guess is mostly harmless, because an existing target's deps are never rewritten. #621 removes that accidental protection, so #559 becomes load-bearing for any repo that loads .bzl files from an external repository whose bzl_library target name does not match the file name.
This is not an argument against #621, which fixes a real bug. It is a note that the two issues interact, and that #559 may deserve attention alongside it. Marking the affected deps with # keep is a workable local mitigation in the meantime.
The
bzlGazelle plugin setsdepscorrectly when it first generates abzl_library, but never updates them afterwards. Once a target exists in a BUILD file, itsdepsare frozen no matter how theload()statements in itssrcschange.Reproduction
bzlplugin so that it generates abzl_library.load()statement to a.bzlfile whosebzl_libraryalready exists.The new dependency is never added. Deleting a correct dep and re-running does not restore it either.
Cause
Resolve()computes the right value and callsr.SetAttr("deps", deps), so the resolution itself is fine. The problem is theKindInfoingazelle/bzl/gazelle.go:depsappears in neitherMergeableAttrsnorResolveAttrs. Gazelle's merger selectsMergeableAttrsin the pre-resolve phase andResolveAttrsin the post-resolve phase (merger/merger.go,getMergeAttrs). Sincedepsis resolved in the post-resolve phase and noResolveAttrsis declared, the computed value is discarded for any rule that already exists. A newly generated rule has nothing to merge against, which is why fresh targets look correct.Gazelle's own Go language declares
ResolveAttrs: map[string]bool{"deps": true}inlanguage/go/kinds.go.Existing fix
#621 already proposes exactly this change and adds a regression fixture. It has been open since May with no review.
A caveat worth knowing before #621 lands
I applied #621's change locally against a repo with roughly 100
bzl_librarytargets. It behaves well overall, and notably it also removes stale deps left over from loads that no longer exist, which is a second class of drift.It does, however, surface #559. Enabling
ResolveAttrsmeans the guessed labels for cross-repository loads now get written onto existing targets, replacing correct hand-written ones. In our case the source has:The correct dependency is
@rules_cc//cc/common(the package's:commontarget). With #621 applied, Gazelle rewrites it to the guessed labels, neither of which exists:Today that mis-guess is mostly harmless, because an existing target's
depsare never rewritten. #621 removes that accidental protection, so #559 becomes load-bearing for any repo that loads.bzlfiles from an external repository whosebzl_librarytarget name does not match the file name.This is not an argument against #621, which fixes a real bug. It is a note that the two issues interact, and that #559 may deserve attention alongside it. Marking the affected
depswith# keepis a workable local mitigation in the meantime.