@@ -25,14 +25,19 @@ import (
2525)
2626
2727// NewTargetHashCache creates a TargetHashCache which uses context for metadata lookups.
28- func NewTargetHashCache (context map [gazelle_label.Label ]map [Configuration ]* analysis.ConfiguredTarget , bazelRelease string ) * TargetHashCache {
28+ func NewTargetHashCache (
29+ context map [gazelle_label.Label ]map [Configuration ]* analysis.ConfiguredTarget ,
30+ normalizer * Normalizer ,
31+ bazelRelease string ,
32+ ) * TargetHashCache {
2933 bazelVersionSupportsConfiguredRuleInputs := isConfiguredRuleInputsSupported (bazelRelease )
3034
3135 return & TargetHashCache {
3236 context : context ,
3337 fileHashCache : & fileHashCache {
3438 cache : make (map [string ]* cacheEntry ),
3539 },
40+ normalizer : normalizer ,
3641 bazelRelease : bazelRelease ,
3742 bazelVersionSupportsConfiguredRuleInputs : bazelVersionSupportsConfiguredRuleInputs ,
3843 cache : make (map [gazelle_label.Label ]map [Configuration ]* cacheEntry ),
@@ -63,6 +68,8 @@ type TargetHashCache struct {
6368 bazelRelease string
6469 bazelVersionSupportsConfiguredRuleInputs bool
6570
71+ normalizer * Normalizer
72+
6673 frozen bool
6774
6875 cacheLock sync.Mutex
@@ -138,6 +145,10 @@ func (thc *TargetHashCache) Freeze() {
138145 thc .frozen = true
139146}
140147
148+ func (thc * TargetHashCache ) ParseCanonicalLabel (label string ) (gazelle_label.Label , error ) {
149+ return thc .normalizer .ParseCanonicalLabel (label )
150+ }
151+
141152// Difference represents a difference of a target between two commits.
142153// All fields except Category are optional.
143154type Difference struct {
@@ -264,14 +275,16 @@ func WalkDiffs(before *TargetHashCache, after *TargetHashCache, labelAndConfigur
264275 Before : string (attributeBeforeJson ),
265276 })
266277 } else {
267- if ! equivalentAttributes (attributeBefore , attributeAfter ) {
278+ normalizedBeforeAttribute := before .AttributeForSerialization (attributeBefore )
279+ normalizedAfterAttribute := after .AttributeForSerialization (attributeAfter )
280+ if ! equivalentAttributes (normalizedBeforeAttribute , normalizedAfterAttribute ) {
268281 if attributeName == "$rule_implementation_hash" {
269282 differences = append (differences , Difference {
270283 Category : "RuleImplementedChanged" ,
271284 })
272285 } else {
273- attributeBeforeJson , _ := protojson .Marshal (AttributeForSerialization ( attributeBefore ) )
274- attributeAfterJson , _ := protojson .Marshal (AttributeForSerialization ( attributeAfter ) )
286+ attributeBeforeJson , _ := protojson .Marshal (normalizedBeforeAttribute )
287+ attributeAfterJson , _ := protojson .Marshal (normalizedAfterAttribute )
275288 differences = append (differences , Difference {
276289 Category : "AttributeChanged" ,
277290 Key : attributeName ,
@@ -285,7 +298,7 @@ func WalkDiffs(before *TargetHashCache, after *TargetHashCache, labelAndConfigur
285298 sortedAttributeNamesAfter := sortKeys (attributesAfter )
286299 for _ , attributeName := range sortedAttributeNamesAfter {
287300 if _ , ok := attributesBefore [attributeName ]; ! ok {
288- attributeAfterJson , _ := protojson .Marshal (AttributeForSerialization (attributesAfter [attributeName ]))
301+ attributeAfterJson , _ := protojson .Marshal (after . AttributeForSerialization (attributesAfter [attributeName ]))
289302 differences = append (differences , Difference {
290303 Category : "AttributeAdded" ,
291304 Key : attributeName ,
@@ -308,7 +321,8 @@ func WalkDiffs(before *TargetHashCache, after *TargetHashCache, labelAndConfigur
308321
309322 for _ , ruleInputLabelAndConfigurations := range ruleInputLabelsAndConfigurationsAfter {
310323 ruleInputLabel := ruleInputLabelAndConfigurations .Label
311- if _ , ok := ruleInputLabelsToConfigurationsBefore [ruleInputLabel ]; ! ok {
324+ knownConfigurationsBefore , ok := ruleInputLabelsToConfigurationsBefore [ruleInputLabel ]
325+ if ! ok {
312326 differences = append (differences , Difference {
313327 Category : "RuleInputAdded" ,
314328 Key : ruleInputLabel .String (),
@@ -318,7 +332,6 @@ func WalkDiffs(before *TargetHashCache, after *TargetHashCache, labelAndConfigur
318332 // query information, so we could filter away e.g. host changes when we only have a target dep.
319333 // Unfortunately, Bazel doesn't currently expose this.
320334 // See https://github.com/bazelbuild/bazel/issues/14610#issuecomment-1024460141
321- knownConfigurationsBefore := ruleInputLabelsToConfigurationsBefore [ruleInputLabel ]
322335 knownConfigurationsAfter := ruleInputLabelsToConfigurationsAfter [ruleInputLabel ]
323336
324337 for _ , knownConfigurationAfter := range knownConfigurationsAfter .SortedSlice () {
@@ -369,6 +382,34 @@ func WalkDiffs(before *TargetHashCache, after *TargetHashCache, labelAndConfigur
369382 return differences , nil
370383}
371384
385+ // AttributeForSerialization redacts details about an attribute which don't affect the output of
386+ // building them, and returns equivalent canonical attribute metadata.
387+ // In particular it redacts:
388+ // - Whether an attribute was explicitly specified (because the effective value is all that
389+ // matters).
390+ // - Any attribute named `generator_location`, because these point to absolute paths for
391+ // built-in `cc_toolchain_suite` targets such as `@local_config_cc//:toolchain`.
392+ func (thc * TargetHashCache ) AttributeForSerialization (rawAttr * build.Attribute ) * build.Attribute {
393+ normalized := * rawAttr
394+ normalized .ExplicitlySpecified = nil
395+
396+ // Redact generator_location, which typically contains absolute paths but has no bearing on the
397+ // functioning of a rule.
398+ // This is also done in Bazel's internal target hash computation. See:
399+ // https://github.com/bazelbuild/bazel/blob/6971b016f1e258e3bb567a0f9fe7a88ad565d8f2/src/main/java/com/google/devtools/build/lib/query2/query/output/SyntheticAttributeHashCalculator.java#L78-L81
400+ if normalized .Name != nil {
401+ if * normalized .Name == "generator_location" {
402+ normalized .StringValue = nil
403+ }
404+ }
405+
406+ return thc .normalizer .NormalizeAttribute (& normalized )
407+ }
408+
409+ func equivalentAttributes (left , right * build.Attribute ) bool {
410+ return proto .Equal (left , right )
411+ }
412+
372413func indexByLabel (labelsAndConfigurations []LabelAndConfigurations ) map [gazelle_label.Label ]* ss.SortedSet [Configuration ] {
373414 m := make (map [gazelle_label.Label ]* ss.SortedSet [Configuration ], len (labelsAndConfigurations ))
374415 for _ , labelAndConfigurations := range labelsAndConfigurations {
@@ -443,7 +484,7 @@ func hashTarget(thc *TargetHashCache, labelAndConfiguration LabelAndConfiguratio
443484 return hashRule (thc , target .Rule , configuredTarget .Configuration )
444485 case build .Target_GENERATED_FILE :
445486 hasher := sha256 .New ()
446- generatingLabel , err := ParseCanonicalLabel (* target .GeneratedFile .GeneratingRule )
487+ generatingLabel , err := thc . ParseCanonicalLabel (* target .GeneratedFile .GeneratingRule )
447488 if err != nil {
448489 return nil , fmt .Errorf ("failed to parse generated file generating rule label %s: %w" , * target .GeneratedFile .GeneratingRule , err )
449490 }
@@ -472,15 +513,19 @@ func hashRule(thc *TargetHashCache, rule *build.Rule, configuration *analysis.Co
472513 hasher .Write ([]byte (rule .GetRuleClass ()))
473514 hasher .Write ([]byte (rule .GetSkylarkEnvironmentHashCode ()))
474515 hasher .Write ([]byte (configuration .GetChecksum ()))
516+
475517 // TODO: Consider using `$internal_attr_hash` from https://github.com/bazelbuild/bazel/blob/6971b016f1e258e3bb567a0f9fe7a88ad565d8f2/src/main/java/com/google/devtools/build/lib/query2/query/output/SyntheticAttributeHashCalculator.java
476518 // rather than hashing attributes ourselves.
477519 // On the plus side, this builds in some heuristics from Bazel (e.g. ignoring `generator_location`).
478520 // On the down side, it would even further decouple our "hashing" and "diffing" procedures.
479521 for _ , attr := range rule .GetAttribute () {
480- protoBytes , err := proto .Marshal (AttributeForSerialization (attr ))
522+ normalizedAttribute := thc .AttributeForSerialization (attr )
523+
524+ protoBytes , err := proto .Marshal (normalizedAttribute )
481525 if err != nil {
482526 return nil , err
483527 }
528+
484529 hasher .Write (protoBytes )
485530 }
486531
@@ -512,7 +557,7 @@ func getConfiguredRuleInputs(thc *TargetHashCache, rule *build.Rule, ownConfigur
512557 labelsAndConfigurations := make ([]LabelAndConfigurations , 0 )
513558 if thc .bazelVersionSupportsConfiguredRuleInputs {
514559 for _ , configuredRuleInput := range rule .ConfiguredRuleInput {
515- ruleInputLabel , err := ParseCanonicalLabel (configuredRuleInput .GetLabel ())
560+ ruleInputLabel , err := thc . ParseCanonicalLabel (configuredRuleInput .GetLabel ())
516561 if err != nil {
517562 return nil , fmt .Errorf ("failed to parse configuredRuleInput label %s: %w" , configuredRuleInput .GetLabel (), err )
518563 }
@@ -534,7 +579,7 @@ func getConfiguredRuleInputs(thc *TargetHashCache, rule *build.Rule, ownConfigur
534579 }
535580 } else {
536581 for _ , ruleInputLabelString := range rule .RuleInput {
537- ruleInputLabel , err := ParseCanonicalLabel (ruleInputLabelString )
582+ ruleInputLabel , err := thc . ParseCanonicalLabel (ruleInputLabelString )
538583 if err != nil {
539584 return nil , fmt .Errorf ("failed to parse ruleInput label %s: %w" , ruleInputLabelString , err )
540585 }
0 commit comments