1// Copyright 2020 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package bazel 16 17import ( 18 "fmt" 19 "path/filepath" 20 "reflect" 21 "regexp" 22 "sort" 23 "strings" 24 25 "github.com/google/blueprint" 26) 27 28// BazelTargetModuleProperties contain properties and metadata used for 29// Blueprint to BUILD file conversion. 30type BazelTargetModuleProperties struct { 31 // The Bazel rule class for this target. 32 Rule_class string `blueprint:"mutated"` 33 34 // The target label for the bzl file containing the definition of the rule class. 35 Bzl_load_location string `blueprint:"mutated"` 36} 37 38var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)") 39 40// Label is used to represent a Bazel compatible Label. Also stores the original 41// bp text to support string replacement. 42type Label struct { 43 // The string representation of a Bazel target label. This can be a relative 44 // or fully qualified label. These labels are used for generating BUILD 45 // files with bp2build. 46 Label string 47 48 // The original Soong/Blueprint module name that the label was derived from. 49 // This is used for replacing references to the original name with the new 50 // label, for example in genrule cmds. 51 // 52 // While there is a reversible 1:1 mapping from the module name to Bazel 53 // label with bp2build that could make computing the original module name 54 // from the label automatic, it is not the case for handcrafted targets, 55 // where modules can have a custom label mapping through the { bazel_module: 56 // { label: <label> } } property. 57 // 58 // With handcrafted labels, those modules don't go through bp2build 59 // conversion, but relies on handcrafted targets in the source tree. 60 OriginalModuleName string 61} 62 63// LabelList is used to represent a list of Bazel labels. 64type LabelList struct { 65 Includes []Label 66 Excludes []Label 67} 68 69// MakeLabelList creates a LabelList from a list Label 70func MakeLabelList(labels []Label) LabelList { 71 return LabelList{ 72 Includes: labels, 73 Excludes: nil, 74 } 75} 76 77func SortedConfigurationAxes[T any](m map[ConfigurationAxis]T) []ConfigurationAxis { 78 keys := make([]ConfigurationAxis, 0, len(m)) 79 for k := range m { 80 keys = append(keys, k) 81 } 82 83 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) }) 84 return keys 85} 86 87// MakeLabelListFromTargetNames creates a LabelList from unqualified target names 88// This is a utiltity function for bp2build converters of Soong modules that have 1:many generated targets 89func MakeLabelListFromTargetNames(targetNames []string) LabelList { 90 labels := []Label{} 91 for _, name := range targetNames { 92 label := Label{Label: ":" + name} 93 labels = append(labels, label) 94 } 95 return MakeLabelList(labels) 96} 97 98func (ll *LabelList) Equals(other LabelList) bool { 99 if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) { 100 return false 101 } 102 for i, _ := range ll.Includes { 103 if ll.Includes[i] != other.Includes[i] { 104 return false 105 } 106 } 107 for i, _ := range ll.Excludes { 108 if ll.Excludes[i] != other.Excludes[i] { 109 return false 110 } 111 } 112 return true 113} 114 115func (ll *LabelList) IsNil() bool { 116 return ll.Includes == nil && ll.Excludes == nil 117} 118 119func (ll *LabelList) IsEmpty() bool { 120 return len(ll.Includes) == 0 && len(ll.Excludes) == 0 121} 122 123func (ll *LabelList) deepCopy() LabelList { 124 return LabelList{ 125 Includes: ll.Includes[:], 126 Excludes: ll.Excludes[:], 127 } 128} 129 130// uniqueParentDirectories returns a list of the unique parent directories for 131// all files in ll.Includes. 132func (ll *LabelList) uniqueParentDirectories() []string { 133 dirMap := map[string]bool{} 134 for _, label := range ll.Includes { 135 dirMap[filepath.Dir(label.Label)] = true 136 } 137 dirs := []string{} 138 for dir := range dirMap { 139 dirs = append(dirs, dir) 140 } 141 return dirs 142} 143 144// Add inserts the label Label at the end of the LabelList.Includes. 145func (ll *LabelList) Add(label *Label) { 146 if label == nil { 147 return 148 } 149 ll.Includes = append(ll.Includes, *label) 150} 151 152// AddExclude inserts the label Label at the end of the LabelList.Excludes. 153func (ll *LabelList) AddExclude(label *Label) { 154 if label == nil { 155 return 156 } 157 ll.Excludes = append(ll.Excludes, *label) 158} 159 160// Append appends the fields of other labelList to the corresponding fields of ll. 161func (ll *LabelList) Append(other LabelList) { 162 if len(ll.Includes) > 0 || len(other.Includes) > 0 { 163 ll.Includes = append(ll.Includes, other.Includes...) 164 } 165 if len(ll.Excludes) > 0 || len(other.Excludes) > 0 { 166 ll.Excludes = append(ll.Excludes, other.Excludes...) 167 } 168} 169 170// Partition splits a LabelList into two LabelLists depending on the return value 171// of the predicate. 172// This function preserves the Includes and Excludes, but it does not provide 173// that information to the partition function. 174func (ll *LabelList) Partition(predicate func(label Label) bool) (LabelList, LabelList) { 175 predicated := LabelList{} 176 unpredicated := LabelList{} 177 for _, include := range ll.Includes { 178 if predicate(include) { 179 predicated.Add(&include) 180 } else { 181 unpredicated.Add(&include) 182 } 183 } 184 for _, exclude := range ll.Excludes { 185 if predicate(exclude) { 186 predicated.AddExclude(&exclude) 187 } else { 188 unpredicated.AddExclude(&exclude) 189 } 190 } 191 return predicated, unpredicated 192} 193 194// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns 195// the slice in a sorted order. 196func UniqueSortedBazelLabels(originalLabels []Label) []Label { 197 uniqueLabels := FirstUniqueBazelLabels(originalLabels) 198 sort.SliceStable(uniqueLabels, func(i, j int) bool { 199 return uniqueLabels[i].Label < uniqueLabels[j].Label 200 }) 201 return uniqueLabels 202} 203 204func FirstUniqueBazelLabels(originalLabels []Label) []Label { 205 var labels []Label 206 found := make(map[string]bool, len(originalLabels)) 207 for _, l := range originalLabels { 208 if _, ok := found[l.Label]; ok { 209 continue 210 } 211 labels = append(labels, l) 212 found[l.Label] = true 213 } 214 return labels 215} 216 217func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList { 218 var uniqueLabelList LabelList 219 uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes) 220 uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes) 221 return uniqueLabelList 222} 223 224func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList { 225 var uniqueLabelList LabelList 226 uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes) 227 uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes) 228 return uniqueLabelList 229} 230 231// Subtract needle from haystack 232func SubtractStrings(haystack []string, needle []string) []string { 233 // This is really a set 234 needleMap := make(map[string]bool) 235 for _, s := range needle { 236 needleMap[s] = true 237 } 238 239 var strings []string 240 for _, s := range haystack { 241 if exclude := needleMap[s]; !exclude { 242 strings = append(strings, s) 243 } 244 } 245 246 return strings 247} 248 249// Subtract needle from haystack 250func SubtractBazelLabels(haystack []Label, needle []Label) []Label { 251 // This is really a set 252 needleMap := make(map[Label]bool) 253 for _, s := range needle { 254 needleMap[s] = true 255 } 256 257 var labels []Label 258 for _, label := range haystack { 259 if exclude := needleMap[label]; !exclude { 260 labels = append(labels, label) 261 } 262 } 263 264 return labels 265} 266 267// Appends two LabelLists, returning the combined list. 268func AppendBazelLabelLists(a LabelList, b LabelList) LabelList { 269 var result LabelList 270 result.Includes = append(a.Includes, b.Includes...) 271 result.Excludes = append(a.Excludes, b.Excludes...) 272 return result 273} 274 275// Subtract needle from haystack 276func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList { 277 var result LabelList 278 result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes) 279 // NOTE: Excludes are intentionally not subtracted 280 result.Excludes = haystack.Excludes 281 return result 282} 283 284// FirstUniqueBazelLabelListAttribute takes a LabelListAttribute and makes the LabelList for 285// each axis/configuration by keeping the first instance of a Label and omitting all subsequent 286// repetitions. 287func FirstUniqueBazelLabelListAttribute(attr LabelListAttribute) LabelListAttribute { 288 var result LabelListAttribute 289 result.Value = FirstUniqueBazelLabelList(attr.Value) 290 if attr.HasConfigurableValues() { 291 result.ConfigurableValues = make(configurableLabelLists) 292 } 293 for axis, configToLabels := range attr.ConfigurableValues { 294 for c, l := range configToLabels { 295 result.SetSelectValue(axis, c, FirstUniqueBazelLabelList(l)) 296 } 297 } 298 299 return result 300} 301 302// SubtractBazelLabelListAttribute subtract needle from haystack for LabelList in each 303// axis/configuration. 304func SubtractBazelLabelListAttribute(haystack LabelListAttribute, needle LabelListAttribute) LabelListAttribute { 305 var result LabelListAttribute 306 result.Value = SubtractBazelLabelList(haystack.Value, needle.Value) 307 if haystack.HasConfigurableValues() { 308 result.ConfigurableValues = make(configurableLabelLists) 309 } 310 for axis, configToLabels := range haystack.ConfigurableValues { 311 for haystackConfig, haystackLabels := range configToLabels { 312 result.SetSelectValue(axis, haystackConfig, SubtractBazelLabelList(haystackLabels, needle.SelectValue(axis, haystackConfig))) 313 } 314 } 315 316 return result 317} 318 319type Attribute interface { 320 HasConfigurableValues() bool 321} 322 323type labelSelectValues map[string]*Label 324 325type configurableLabels map[ConfigurationAxis]labelSelectValues 326 327func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) { 328 if cl[axis] == nil { 329 cl[axis] = make(labelSelectValues) 330 } 331 cl[axis][config] = value 332} 333 334// Represents an attribute whose value is a single label 335type LabelAttribute struct { 336 Value *Label 337 338 ConfigurableValues configurableLabels 339} 340 341func (la *LabelAttribute) axisTypes() map[configurationType]bool { 342 types := map[configurationType]bool{} 343 for k := range la.ConfigurableValues { 344 if len(la.ConfigurableValues[k]) > 0 { 345 types[k.configurationType] = true 346 } 347 } 348 return types 349} 350 351// Collapse reduces the configurable axes of the label attribute to a single axis. 352// This is necessary for final writing to bp2build, as a configurable label 353// attribute can only be comprised by a single select. 354func (la *LabelAttribute) Collapse() error { 355 axisTypes := la.axisTypes() 356 _, containsOs := axisTypes[os] 357 _, containsArch := axisTypes[arch] 358 _, containsOsArch := axisTypes[osArch] 359 _, containsProductVariables := axisTypes[productVariables] 360 if containsProductVariables { 361 if containsOs || containsArch || containsOsArch { 362 if containsArch { 363 allProductVariablesAreArchVariant := true 364 for k := range la.ConfigurableValues { 365 if k.configurationType == productVariables && !k.archVariant { 366 allProductVariablesAreArchVariant = false 367 } 368 } 369 if !allProductVariablesAreArchVariant { 370 return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes") 371 } 372 } else { 373 return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes") 374 } 375 } 376 } 377 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) { 378 // If a bool attribute has both os and arch configuration axes, the only 379 // way to successfully union their values is to increase the granularity 380 // of the configuration criteria to os_arch. 381 for osType, supportedArchs := range osToArchMap { 382 for _, supportedArch := range supportedArchs { 383 osArch := osArchString(osType, supportedArch) 384 if archOsVal := la.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil { 385 // Do nothing, as the arch_os is explicitly defined already. 386 } else { 387 archVal := la.SelectValue(ArchConfigurationAxis, supportedArch) 388 osVal := la.SelectValue(OsConfigurationAxis, osType) 389 if osVal != nil && archVal != nil { 390 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator 391 // runs after os mutator. 392 la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal) 393 } else if osVal != nil && archVal == nil { 394 la.SetSelectValue(OsArchConfigurationAxis, osArch, *osVal) 395 } else if osVal == nil && archVal != nil { 396 la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal) 397 } 398 } 399 } 400 } 401 // All os_arch values are now set. Clear os and arch axes. 402 delete(la.ConfigurableValues, ArchConfigurationAxis) 403 delete(la.ConfigurableValues, OsConfigurationAxis) 404 } 405 return nil 406} 407 408// HasConfigurableValues returns whether there are configurable values set for this label. 409func (la LabelAttribute) HasConfigurableValues() bool { 410 for _, selectValues := range la.ConfigurableValues { 411 if len(selectValues) > 0 { 412 return true 413 } 414 } 415 return false 416} 417 418// SetValue sets the base, non-configured value for the Label 419func (la *LabelAttribute) SetValue(value Label) { 420 la.SetSelectValue(NoConfigAxis, "", value) 421} 422 423// SetSelectValue set a value for a bazel select for the given axis, config and value. 424func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) { 425 axis.validateConfig(config) 426 switch axis.configurationType { 427 case noConfig: 428 la.Value = &value 429 case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled: 430 if la.ConfigurableValues == nil { 431 la.ConfigurableValues = make(configurableLabels) 432 } 433 la.ConfigurableValues.setValueForAxis(axis, config, &value) 434 default: 435 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) 436 } 437} 438 439// SelectValue gets a value for a bazel select for the given axis and config. 440func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) *Label { 441 axis.validateConfig(config) 442 switch axis.configurationType { 443 case noConfig: 444 return la.Value 445 case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled: 446 return la.ConfigurableValues[axis][config] 447 default: 448 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) 449 } 450} 451 452// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order. 453func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis { 454 return SortedConfigurationAxes(la.ConfigurableValues) 455} 456 457// MakeLabelAttribute turns a string into a LabelAttribute 458func MakeLabelAttribute(label string) *LabelAttribute { 459 return &LabelAttribute{ 460 Value: &Label{ 461 Label: label, 462 }, 463 } 464} 465 466type configToBools map[string]bool 467 468func (ctb configToBools) setValue(config string, value *bool) { 469 if value == nil { 470 if _, ok := ctb[config]; ok { 471 delete(ctb, config) 472 } 473 return 474 } 475 ctb[config] = *value 476} 477 478type configurableBools map[ConfigurationAxis]configToBools 479 480func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) { 481 if cb[axis] == nil { 482 cb[axis] = make(configToBools) 483 } 484 cb[axis].setValue(config, value) 485} 486 487// BoolAttribute represents an attribute whose value is a single bool but may be configurable.. 488type BoolAttribute struct { 489 Value *bool 490 491 ConfigurableValues configurableBools 492} 493 494// HasConfigurableValues returns whether there are configurable values for this attribute. 495func (ba BoolAttribute) HasConfigurableValues() bool { 496 for _, cfgToBools := range ba.ConfigurableValues { 497 if len(cfgToBools) > 0 { 498 return true 499 } 500 } 501 return false 502} 503 504// SetValue sets value for the no config axis 505func (ba *BoolAttribute) SetValue(value *bool) { 506 ba.SetSelectValue(NoConfigAxis, "", value) 507} 508 509// SetSelectValue sets value for the given axis/config. 510func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) { 511 axis.validateConfig(config) 512 switch axis.configurationType { 513 case noConfig: 514 ba.Value = value 515 case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled: 516 if ba.ConfigurableValues == nil { 517 ba.ConfigurableValues = make(configurableBools) 518 } 519 ba.ConfigurableValues.setValueForAxis(axis, config, value) 520 default: 521 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) 522 } 523} 524 525// ToLabelListAttribute creates and returns a LabelListAttribute from this 526// bool attribute, where each bool in this attribute corresponds to a 527// label list value in the resultant attribute. 528func (ba *BoolAttribute) ToLabelListAttribute(falseVal LabelList, trueVal LabelList) (LabelListAttribute, error) { 529 getLabelList := func(boolPtr *bool) LabelList { 530 if boolPtr == nil { 531 return LabelList{nil, nil} 532 } else if *boolPtr { 533 return trueVal 534 } else { 535 return falseVal 536 } 537 } 538 539 mainVal := getLabelList(ba.Value) 540 if !ba.HasConfigurableValues() { 541 return MakeLabelListAttribute(mainVal), nil 542 } 543 544 result := LabelListAttribute{} 545 if err := ba.Collapse(); err != nil { 546 return result, err 547 } 548 549 for axis, configToBools := range ba.ConfigurableValues { 550 if len(configToBools) < 1 { 551 continue 552 } 553 for config, boolPtr := range configToBools { 554 val := getLabelList(&boolPtr) 555 if !val.Equals(mainVal) { 556 result.SetSelectValue(axis, config, val) 557 } 558 } 559 result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal) 560 } 561 562 return result, nil 563} 564 565// ToStringListAttribute creates a StringListAttribute from this BoolAttribute, 566// where each bool corresponds to a string list value generated by the provided 567// function. 568// TODO(b/271425661): Generalize this 569func (ba *BoolAttribute) ToStringListAttribute(valueFunc func(boolPtr *bool, axis ConfigurationAxis, config string) []string) (StringListAttribute, error) { 570 mainVal := valueFunc(ba.Value, NoConfigAxis, "") 571 if !ba.HasConfigurableValues() { 572 return MakeStringListAttribute(mainVal), nil 573 } 574 575 result := StringListAttribute{} 576 if err := ba.Collapse(); err != nil { 577 return result, err 578 } 579 580 for axis, configToBools := range ba.ConfigurableValues { 581 if len(configToBools) < 1 { 582 continue 583 } 584 for config, boolPtr := range configToBools { 585 val := valueFunc(&boolPtr, axis, config) 586 if !reflect.DeepEqual(val, mainVal) { 587 result.SetSelectValue(axis, config, val) 588 } 589 } 590 result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal) 591 } 592 593 return result, nil 594} 595 596// Collapse reduces the configurable axes of the boolean attribute to a single axis. 597// This is necessary for final writing to bp2build, as a configurable boolean 598// attribute can only be comprised by a single select. 599func (ba *BoolAttribute) Collapse() error { 600 axisTypes := ba.axisTypes() 601 _, containsOs := axisTypes[os] 602 _, containsArch := axisTypes[arch] 603 _, containsOsArch := axisTypes[osArch] 604 _, containsProductVariables := axisTypes[productVariables] 605 if containsProductVariables { 606 if containsOs || containsArch || containsOsArch { 607 return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes") 608 } 609 } 610 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) { 611 // If a bool attribute has both os and arch configuration axes, the only 612 // way to successfully union their values is to increase the granularity 613 // of the configuration criteria to os_arch. 614 for osType, supportedArchs := range osToArchMap { 615 for _, supportedArch := range supportedArchs { 616 osArch := osArchString(osType, supportedArch) 617 if archOsVal := ba.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil { 618 // Do nothing, as the arch_os is explicitly defined already. 619 } else { 620 archVal := ba.SelectValue(ArchConfigurationAxis, supportedArch) 621 osVal := ba.SelectValue(OsConfigurationAxis, osType) 622 if osVal != nil && archVal != nil { 623 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator 624 // runs after os mutator. 625 ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal) 626 } else if osVal != nil && archVal == nil { 627 ba.SetSelectValue(OsArchConfigurationAxis, osArch, osVal) 628 } else if osVal == nil && archVal != nil { 629 ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal) 630 } 631 } 632 } 633 } 634 // All os_arch values are now set. Clear os and arch axes. 635 delete(ba.ConfigurableValues, ArchConfigurationAxis) 636 delete(ba.ConfigurableValues, OsConfigurationAxis) 637 // Verify post-condition; this should never fail, provided no additional 638 // axes are introduced. 639 if len(ba.ConfigurableValues) > 1 { 640 panic(fmt.Errorf("error in collapsing attribute: %#v", ba)) 641 } 642 } 643 return nil 644} 645 646func (ba *BoolAttribute) axisTypes() map[configurationType]bool { 647 types := map[configurationType]bool{} 648 for k := range ba.ConfigurableValues { 649 if len(ba.ConfigurableValues[k]) > 0 { 650 types[k.configurationType] = true 651 } 652 } 653 return types 654} 655 656// SelectValue gets the value for the given axis/config. 657func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool { 658 axis.validateConfig(config) 659 switch axis.configurationType { 660 case noConfig: 661 return ba.Value 662 case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled: 663 if v, ok := ba.ConfigurableValues[axis][config]; ok { 664 return &v 665 } else { 666 return nil 667 } 668 default: 669 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) 670 } 671} 672 673// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order. 674func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis { 675 return SortedConfigurationAxes(ba.ConfigurableValues) 676} 677 678// labelListSelectValues supports config-specific label_list typed Bazel attribute values. 679type labelListSelectValues map[string]LabelList 680 681func (ll labelListSelectValues) addSelects(label labelSelectValues) { 682 for k, v := range label { 683 if label == nil { 684 continue 685 } 686 l := ll[k] 687 (&l).Add(v) 688 ll[k] = l 689 } 690} 691 692func (ll labelListSelectValues) appendSelects(other labelListSelectValues, forceSpecifyEmptyList bool) { 693 for k, v := range other { 694 l := ll[k] 695 if forceSpecifyEmptyList && l.IsNil() && !v.IsNil() { 696 l.Includes = []Label{} 697 } 698 (&l).Append(v) 699 ll[k] = l 700 } 701} 702 703// HasConfigurableValues returns whether there are configurable values within this set of selects. 704func (ll labelListSelectValues) HasConfigurableValues() bool { 705 for _, v := range ll { 706 if v.Includes != nil { 707 return true 708 } 709 } 710 return false 711} 712 713// LabelListAttribute is used to represent a list of Bazel labels as an 714// attribute. 715type LabelListAttribute struct { 716 // The non-configured attribute label list Value. Required. 717 Value LabelList 718 719 // The configured attribute label list Values. Optional 720 // a map of independent configurability axes 721 ConfigurableValues configurableLabelLists 722 723 // If true, differentiate between "nil" and "empty" list. nil means that 724 // this attribute should not be specified at all, and "empty" means that 725 // the attribute should be explicitly specified as an empty list. 726 // This mode facilitates use of attribute defaults: an empty list should 727 // override the default. 728 ForceSpecifyEmptyList bool 729 730 // If true, signal the intent to the code generator to emit all select keys, 731 // even if the Includes list for that key is empty. This mode facilitates 732 // specific select statements where an empty list for a non-default select 733 // key has a meaning. 734 EmitEmptyList bool 735 736 // If a property has struct tag "variant_prepend", this value should 737 // be set to True, so that when bp2build generates BUILD.bazel, variant 738 // properties(select ...) come before general properties. 739 Prepend bool 740} 741 742type configurableLabelLists map[ConfigurationAxis]labelListSelectValues 743 744func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) { 745 if list.IsNil() { 746 if _, ok := cll[axis][config]; ok { 747 delete(cll[axis], config) 748 } 749 return 750 } 751 if cll[axis] == nil { 752 cll[axis] = make(labelListSelectValues) 753 } 754 755 cll[axis][config] = list 756} 757 758func (cll configurableLabelLists) Append(other configurableLabelLists, forceSpecifyEmptyList bool) { 759 for axis, otherSelects := range other { 760 selects := cll[axis] 761 if selects == nil { 762 selects = make(labelListSelectValues, len(otherSelects)) 763 } 764 selects.appendSelects(otherSelects, forceSpecifyEmptyList) 765 cll[axis] = selects 766 } 767} 768 769func (lla *LabelListAttribute) Clone() *LabelListAttribute { 770 result := &LabelListAttribute{ForceSpecifyEmptyList: lla.ForceSpecifyEmptyList} 771 return result.Append(*lla) 772} 773 774// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value. 775func MakeLabelListAttribute(value LabelList) LabelListAttribute { 776 return LabelListAttribute{ 777 Value: value, 778 ConfigurableValues: make(configurableLabelLists), 779 } 780} 781 782// MakeSingleLabelListAttribute initializes a LabelListAttribute as a non-arch specific list with 1 element, the given Label. 783func MakeSingleLabelListAttribute(value Label) LabelListAttribute { 784 return MakeLabelListAttribute(MakeLabelList([]Label{value})) 785} 786 787func (lla *LabelListAttribute) SetValue(list LabelList) { 788 lla.SetSelectValue(NoConfigAxis, "", list) 789} 790 791// SetSelectValue set a value for a bazel select for the given axis, config and value. 792func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) { 793 axis.validateConfig(config) 794 switch axis.configurationType { 795 case noConfig: 796 lla.Value = list 797 case arch, os, osArch, productVariables, osAndInApex, inApex, errorProneDisabled, sanitizersEnabled: 798 if lla.ConfigurableValues == nil { 799 lla.ConfigurableValues = make(configurableLabelLists) 800 } 801 lla.ConfigurableValues.setValueForAxis(axis, config, list) 802 default: 803 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) 804 } 805} 806 807// SelectValue gets a value for a bazel select for the given axis and config. 808func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList { 809 axis.validateConfig(config) 810 switch axis.configurationType { 811 case noConfig: 812 return lla.Value 813 case arch, os, osArch, productVariables, osAndInApex, inApex, errorProneDisabled, sanitizersEnabled: 814 return lla.ConfigurableValues[axis][config] 815 default: 816 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) 817 } 818} 819 820// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order. 821func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis { 822 return SortedConfigurationAxes(lla.ConfigurableValues) 823} 824 825// Append all values, including os and arch specific ones, from another 826// LabelListAttribute to this LabelListAttribute. Returns this LabelListAttribute. 827func (lla *LabelListAttribute) Append(other LabelListAttribute) *LabelListAttribute { 828 forceSpecifyEmptyList := lla.ForceSpecifyEmptyList || other.ForceSpecifyEmptyList 829 if forceSpecifyEmptyList && lla.Value.IsNil() && !other.Value.IsNil() { 830 lla.Value.Includes = []Label{} 831 } 832 lla.Value.Append(other.Value) 833 if lla.ConfigurableValues == nil { 834 lla.ConfigurableValues = make(configurableLabelLists) 835 } 836 lla.ConfigurableValues.Append(other.ConfigurableValues, forceSpecifyEmptyList) 837 return lla 838} 839 840// Add inserts the labels for each axis of LabelAttribute at the end of corresponding axis's 841// LabelList within the LabelListAttribute 842func (lla *LabelListAttribute) Add(label *LabelAttribute) { 843 if label == nil { 844 return 845 } 846 847 lla.Value.Add(label.Value) 848 if lla.ConfigurableValues == nil && label.ConfigurableValues != nil { 849 lla.ConfigurableValues = make(configurableLabelLists) 850 } 851 for axis, _ := range label.ConfigurableValues { 852 if _, exists := lla.ConfigurableValues[axis]; !exists { 853 lla.ConfigurableValues[axis] = make(labelListSelectValues) 854 } 855 lla.ConfigurableValues[axis].addSelects(label.ConfigurableValues[axis]) 856 } 857} 858 859// HasConfigurableValues returns true if the attribute contains axis-specific label list values. 860func (lla LabelListAttribute) HasConfigurableValues() bool { 861 for _, selectValues := range lla.ConfigurableValues { 862 if len(selectValues) > 0 { 863 return true 864 } 865 } 866 return false 867} 868 869// HasAxisSpecificValues returns true if the attribute contains axis specific label list values from a given axis 870func (lla LabelListAttribute) HasAxisSpecificValues(axis ConfigurationAxis) bool { 871 for _, values := range lla.ConfigurableValues[axis] { 872 if !values.IsNil() { 873 return true 874 } 875 } 876 return false 877} 878 879// IsEmpty returns true if the attribute has no values under any configuration. 880func (lla LabelListAttribute) IsEmpty() bool { 881 if len(lla.Value.Includes) > 0 { 882 return false 883 } 884 for axis, _ := range lla.ConfigurableValues { 885 if lla.ConfigurableValues[axis].HasConfigurableValues() { 886 return false 887 } 888 } 889 return true 890} 891 892// IsNil returns true if the attribute has not been set for any configuration. 893func (lla LabelListAttribute) IsNil() bool { 894 if lla.Value.Includes != nil { 895 return false 896 } 897 return !lla.HasConfigurableValues() 898} 899 900// Exclude for the given axis, config, removes Includes in labelList from Includes and appends them 901// to Excludes. This is to special case any excludes that are not specified in a bp file but need to 902// be removed, e.g. if they could cause duplicate element failures. 903func (lla *LabelListAttribute) Exclude(axis ConfigurationAxis, config string, labelList LabelList) { 904 val := lla.SelectValue(axis, config) 905 newList := SubtractBazelLabelList(val, labelList) 906 newList.Excludes = append(newList.Excludes, labelList.Includes...) 907 lla.SetSelectValue(axis, config, newList) 908} 909 910// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from 911// the base value and included in default values as appropriate. 912func (lla *LabelListAttribute) ResolveExcludes() { 913 // If there are OsAndInApexAxis, we need to use 914 // * includes from the OS & in APEX Axis for non-Android configs for libraries that need to be 915 // included in non-Android OSes 916 // * excludes from the OS Axis for non-Android configs, to exclude libraries that should _not_ 917 // be included in the non-Android OSes 918 if _, ok := lla.ConfigurableValues[OsAndInApexAxis]; ok { 919 inApexLabels := lla.ConfigurableValues[OsAndInApexAxis][ConditionsDefaultConfigKey] 920 for config, labels := range lla.ConfigurableValues[OsConfigurationAxis] { 921 // OsAndroid has already handled its excludes. 922 // We only need to copy the excludes from other arches, so if there are none, skip it. 923 if config == OsAndroid || len(labels.Excludes) == 0 { 924 continue 925 } 926 lla.ConfigurableValues[OsAndInApexAxis][config] = LabelList{ 927 Includes: inApexLabels.Includes, 928 Excludes: labels.Excludes, 929 } 930 } 931 } 932 933 for axis, configToLabels := range lla.ConfigurableValues { 934 baseLabels := lla.Value.deepCopy() 935 for config, val := range configToLabels { 936 // Exclude config-specific excludes from base value 937 lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes}) 938 939 // add base values to config specific to add labels excluded by others in this axis 940 // then remove all config-specific excludes 941 allLabels := baseLabels.deepCopy() 942 allLabels.Append(val) 943 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: allLabels.Excludes}) 944 } 945 946 // After going through all configs, delete the duplicates in the config 947 // values that are already in the base Value. 948 for config, val := range configToLabels { 949 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value) 950 } 951 952 // Now that the Value list is finalized for this axis, compare it with 953 // the original list, and union the difference with the default 954 // condition for the axis. 955 difference := SubtractBazelLabelList(baseLabels, lla.Value) 956 existingDefaults := lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] 957 existingDefaults.Append(difference) 958 lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = FirstUniqueBazelLabelList(existingDefaults) 959 960 // if everything ends up without includes, just delete the axis 961 if !lla.ConfigurableValues[axis].HasConfigurableValues() { 962 delete(lla.ConfigurableValues, axis) 963 } 964 } 965} 966 967// Partition splits a LabelListAttribute into two LabelListAttributes depending 968// on the return value of the predicate. 969// This function preserves the Includes and Excludes, but it does not provide 970// that information to the partition function. 971func (lla LabelListAttribute) Partition(predicate func(label Label) bool) (LabelListAttribute, LabelListAttribute) { 972 predicated := LabelListAttribute{} 973 unpredicated := LabelListAttribute{} 974 975 valuePartitionTrue, valuePartitionFalse := lla.Value.Partition(predicate) 976 predicated.SetValue(valuePartitionTrue) 977 unpredicated.SetValue(valuePartitionFalse) 978 979 for axis, selectValueLabelLists := range lla.ConfigurableValues { 980 for config, labelList := range selectValueLabelLists { 981 configPredicated, configUnpredicated := labelList.Partition(predicate) 982 predicated.SetSelectValue(axis, config, configPredicated) 983 unpredicated.SetSelectValue(axis, config, configUnpredicated) 984 } 985 } 986 987 return predicated, unpredicated 988} 989 990// OtherModuleContext is a limited context that has methods with information about other modules. 991type OtherModuleContext interface { 992 ModuleFromName(name string) (blueprint.Module, bool) 993 OtherModuleType(m blueprint.Module) string 994 OtherModuleName(m blueprint.Module) string 995 OtherModuleDir(m blueprint.Module) string 996 ModuleErrorf(fmt string, args ...interface{}) 997} 998 999// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed) 1000// label and whether it was changed. 1001type LabelMapper func(OtherModuleContext, Label) (string, bool) 1002 1003// LabelPartition contains descriptions of a partition for labels 1004type LabelPartition struct { 1005 // Extensions to include in this partition 1006 Extensions []string 1007 // LabelMapper is a function that can map a label to a new label, and indicate whether to include 1008 // the mapped label in the partition 1009 LabelMapper LabelMapper 1010 // Whether to store files not included in any other partition in a group of LabelPartitions 1011 // Only one partition in a group of LabelPartitions can enabled Keep_remainder 1012 Keep_remainder bool 1013} 1014 1015// LabelPartitions is a map of partition name to a LabelPartition describing the elements of the 1016// partition 1017type LabelPartitions map[string]LabelPartition 1018 1019// filter returns a pointer to a label if the label should be included in the partition or nil if 1020// not. 1021func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label { 1022 if lf.LabelMapper != nil { 1023 if newLabel, changed := lf.LabelMapper(ctx, label); changed { 1024 return &Label{newLabel, label.OriginalModuleName} 1025 } 1026 } 1027 for _, ext := range lf.Extensions { 1028 if strings.HasSuffix(label.Label, ext) { 1029 return &label 1030 } 1031 } 1032 1033 return nil 1034} 1035 1036// PartitionToLabelListAttribute is map of partition name to a LabelListAttribute 1037type PartitionToLabelListAttribute map[string]LabelListAttribute 1038 1039type partitionToLabelList map[string]*LabelList 1040 1041func (p partitionToLabelList) appendIncludes(partition string, label Label) { 1042 if _, ok := p[partition]; !ok { 1043 p[partition] = &LabelList{} 1044 } 1045 p[partition].Includes = append(p[partition].Includes, label) 1046} 1047 1048func (p partitionToLabelList) excludes(partition string, excludes []Label) { 1049 if _, ok := p[partition]; !ok { 1050 p[partition] = &LabelList{} 1051 } 1052 p[partition].Excludes = excludes 1053} 1054 1055// PartitionLabelListAttribute partitions a LabelListAttribute into the requested partitions 1056func PartitionLabelListAttribute(ctx OtherModuleContext, lla *LabelListAttribute, partitions LabelPartitions) PartitionToLabelListAttribute { 1057 ret := PartitionToLabelListAttribute{} 1058 var partitionNames []string 1059 // Stored as a pointer to distinguish nil (no remainder partition) from empty string partition 1060 var remainderPartition *string 1061 for p, f := range partitions { 1062 partitionNames = append(partitionNames, p) 1063 if f.Keep_remainder { 1064 if remainderPartition != nil { 1065 panic("only one partition can store the remainder") 1066 } 1067 // If we take the address of p in a loop, we'll end up with the last value of p in 1068 // remainderPartition, we want the requested partition 1069 capturePartition := p 1070 remainderPartition = &capturePartition 1071 } 1072 } 1073 1074 partitionLabelList := func(axis ConfigurationAxis, config string) { 1075 value := lla.SelectValue(axis, config) 1076 partitionToLabels := partitionToLabelList{} 1077 for _, item := range value.Includes { 1078 wasFiltered := false 1079 var inPartition *string 1080 for partition, f := range partitions { 1081 filtered := f.filter(ctx, item) 1082 if filtered == nil { 1083 // did not match this filter, keep looking 1084 continue 1085 } 1086 wasFiltered = true 1087 partitionToLabels.appendIncludes(partition, *filtered) 1088 // don't need to check other partitions if this filter used the item, 1089 // continue checking if mapped to another name 1090 if *filtered == item { 1091 if inPartition != nil { 1092 ctx.ModuleErrorf("%q was found in multiple partitions: %q, %q", item.Label, *inPartition, partition) 1093 } 1094 capturePartition := partition 1095 inPartition = &capturePartition 1096 } 1097 } 1098 1099 // if not specified in a partition, add to remainder partition if one exists 1100 if !wasFiltered && remainderPartition != nil { 1101 partitionToLabels.appendIncludes(*remainderPartition, item) 1102 } 1103 } 1104 1105 // ensure empty lists are maintained 1106 if value.Excludes != nil { 1107 for _, partition := range partitionNames { 1108 partitionToLabels.excludes(partition, value.Excludes) 1109 } 1110 } 1111 1112 for partition, list := range partitionToLabels { 1113 val := ret[partition] 1114 (&val).SetSelectValue(axis, config, *list) 1115 ret[partition] = val 1116 } 1117 } 1118 1119 partitionLabelList(NoConfigAxis, "") 1120 for axis, configToList := range lla.ConfigurableValues { 1121 for config, _ := range configToList { 1122 partitionLabelList(axis, config) 1123 } 1124 } 1125 return ret 1126} 1127 1128// StringAttribute corresponds to the string Bazel attribute type with 1129// support for additional metadata, like configurations. 1130type StringAttribute struct { 1131 // The base value of the string attribute. 1132 Value *string 1133 1134 // The configured attribute label list Values. Optional 1135 // a map of independent configurability axes 1136 ConfigurableValues configurableStrings 1137} 1138 1139type configurableStrings map[ConfigurationAxis]stringSelectValues 1140 1141func (cs configurableStrings) setValueForAxis(axis ConfigurationAxis, config string, str *string) { 1142 if cs[axis] == nil { 1143 cs[axis] = make(stringSelectValues) 1144 } 1145 cs[axis][config] = str 1146} 1147 1148type stringSelectValues map[string]*string 1149 1150// HasConfigurableValues returns true if the attribute contains axis-specific string values. 1151func (sa StringAttribute) HasConfigurableValues() bool { 1152 for _, selectValues := range sa.ConfigurableValues { 1153 if len(selectValues) > 0 { 1154 return true 1155 } 1156 } 1157 return false 1158} 1159 1160// SetValue sets the base, non-configured value for the Label 1161func (sa *StringAttribute) SetValue(value string) { 1162 sa.SetSelectValue(NoConfigAxis, "", &value) 1163} 1164 1165// SetSelectValue set a value for a bazel select for the given axis, config and value. 1166func (sa *StringAttribute) SetSelectValue(axis ConfigurationAxis, config string, str *string) { 1167 axis.validateConfig(config) 1168 switch axis.configurationType { 1169 case noConfig: 1170 sa.Value = str 1171 case arch, os, osArch, productVariables, sanitizersEnabled: 1172 if sa.ConfigurableValues == nil { 1173 sa.ConfigurableValues = make(configurableStrings) 1174 } 1175 sa.ConfigurableValues.setValueForAxis(axis, config, str) 1176 default: 1177 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) 1178 } 1179} 1180 1181// SelectValue gets a value for a bazel select for the given axis and config. 1182func (sa *StringAttribute) SelectValue(axis ConfigurationAxis, config string) *string { 1183 axis.validateConfig(config) 1184 switch axis.configurationType { 1185 case noConfig: 1186 return sa.Value 1187 case arch, os, osArch, productVariables, sanitizersEnabled: 1188 if v, ok := sa.ConfigurableValues[axis][config]; ok { 1189 return v 1190 } else { 1191 return nil 1192 } 1193 default: 1194 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) 1195 } 1196} 1197 1198// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order. 1199func (sa *StringAttribute) SortedConfigurationAxes() []ConfigurationAxis { 1200 return SortedConfigurationAxes(sa.ConfigurableValues) 1201} 1202 1203// Collapse reduces the configurable axes of the string attribute to a single axis. 1204// This is necessary for final writing to bp2build, as a configurable string 1205// attribute can only be comprised by a single select. 1206func (sa *StringAttribute) Collapse() error { 1207 axisTypes := sa.axisTypes() 1208 _, containsOs := axisTypes[os] 1209 _, containsArch := axisTypes[arch] 1210 _, containsOsArch := axisTypes[osArch] 1211 _, containsProductVariables := axisTypes[productVariables] 1212 if containsProductVariables { 1213 if containsOs || containsArch || containsOsArch { 1214 return fmt.Errorf("string attribute could not be collapsed as it has two or more unrelated axes") 1215 } 1216 } 1217 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) { 1218 // If a bool attribute has both os and arch configuration axes, the only 1219 // way to successfully union their values is to increase the granularity 1220 // of the configuration criteria to os_arch. 1221 for osType, supportedArchs := range osToArchMap { 1222 for _, supportedArch := range supportedArchs { 1223 osArch := osArchString(osType, supportedArch) 1224 if archOsVal := sa.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil { 1225 // Do nothing, as the arch_os is explicitly defined already. 1226 } else { 1227 archVal := sa.SelectValue(ArchConfigurationAxis, supportedArch) 1228 osVal := sa.SelectValue(OsConfigurationAxis, osType) 1229 if osVal != nil && archVal != nil { 1230 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator 1231 // runs after os mutator. 1232 sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal) 1233 } else if osVal != nil && archVal == nil { 1234 sa.SetSelectValue(OsArchConfigurationAxis, osArch, osVal) 1235 } else if osVal == nil && archVal != nil { 1236 sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal) 1237 } 1238 } 1239 } 1240 } 1241 /// All os_arch values are now set. Clear os and arch axes. 1242 delete(sa.ConfigurableValues, ArchConfigurationAxis) 1243 delete(sa.ConfigurableValues, OsConfigurationAxis) 1244 // Verify post-condition; this should never fail, provided no additional 1245 // axes are introduced. 1246 if len(sa.ConfigurableValues) > 1 { 1247 panic(fmt.Errorf("error in collapsing attribute: %#v", sa)) 1248 } 1249 } else if containsProductVariables { 1250 usedBaseValue := false 1251 for a, configToProp := range sa.ConfigurableValues { 1252 if a.configurationType == productVariables { 1253 for c, p := range configToProp { 1254 if p == nil { 1255 sa.SetSelectValue(a, c, sa.Value) 1256 usedBaseValue = true 1257 } 1258 } 1259 } 1260 } 1261 if usedBaseValue { 1262 sa.Value = nil 1263 } 1264 } 1265 return nil 1266} 1267 1268func (sa *StringAttribute) axisTypes() map[configurationType]bool { 1269 types := map[configurationType]bool{} 1270 for k := range sa.ConfigurableValues { 1271 if strs := sa.ConfigurableValues[k]; len(strs) > 0 { 1272 types[k.configurationType] = true 1273 } 1274 } 1275 return types 1276} 1277 1278// StringListAttribute corresponds to the string_list Bazel attribute type with 1279// support for additional metadata, like configurations. 1280type StringListAttribute struct { 1281 // The base value of the string list attribute. 1282 Value []string 1283 1284 // The configured attribute label list Values. Optional 1285 // a map of independent configurability axes 1286 ConfigurableValues configurableStringLists 1287 1288 // If a property has struct tag "variant_prepend", this value should 1289 // be set to True, so that when bp2build generates BUILD.bazel, variant 1290 // properties(select ...) come before general properties. 1291 Prepend bool 1292} 1293 1294// IsEmpty returns true if the attribute has no values under any configuration. 1295func (sla StringListAttribute) IsEmpty() bool { 1296 return len(sla.Value) == 0 && !sla.HasConfigurableValues() 1297} 1298 1299type configurableStringLists map[ConfigurationAxis]stringListSelectValues 1300 1301func (csl configurableStringLists) Append(other configurableStringLists) { 1302 for axis, otherSelects := range other { 1303 selects := csl[axis] 1304 if selects == nil { 1305 selects = make(stringListSelectValues, len(otherSelects)) 1306 } 1307 selects.appendSelects(otherSelects) 1308 csl[axis] = selects 1309 } 1310} 1311 1312func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) { 1313 if csl[axis] == nil { 1314 csl[axis] = make(stringListSelectValues) 1315 } 1316 csl[axis][config] = list 1317} 1318 1319type stringListSelectValues map[string][]string 1320 1321func (sl stringListSelectValues) appendSelects(other stringListSelectValues) { 1322 for k, v := range other { 1323 sl[k] = append(sl[k], v...) 1324 } 1325} 1326 1327func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool { 1328 for _, val := range sl { 1329 if len(val) > 0 { 1330 return true 1331 } 1332 } 1333 return false 1334} 1335 1336// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value. 1337func MakeStringListAttribute(value []string) StringListAttribute { 1338 // NOTE: These strings are not necessarily unique or sorted. 1339 return StringListAttribute{ 1340 Value: value, 1341 ConfigurableValues: make(configurableStringLists), 1342 } 1343} 1344 1345// HasConfigurableValues returns true if the attribute contains axis-specific string_list values. 1346func (sla StringListAttribute) HasConfigurableValues() bool { 1347 for _, selectValues := range sla.ConfigurableValues { 1348 if len(selectValues) > 0 { 1349 return true 1350 } 1351 } 1352 return false 1353} 1354 1355// Append appends all values, including os and arch specific ones, from another 1356// StringListAttribute to this StringListAttribute 1357func (sla *StringListAttribute) Append(other StringListAttribute) *StringListAttribute { 1358 sla.Value = append(sla.Value, other.Value...) 1359 if sla.ConfigurableValues == nil { 1360 sla.ConfigurableValues = make(configurableStringLists) 1361 } 1362 sla.ConfigurableValues.Append(other.ConfigurableValues) 1363 return sla 1364} 1365 1366func (sla *StringListAttribute) Clone() *StringListAttribute { 1367 result := &StringListAttribute{} 1368 return result.Append(*sla) 1369} 1370 1371// SetSelectValue set a value for a bazel select for the given axis, config and value. 1372func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) { 1373 axis.validateConfig(config) 1374 switch axis.configurationType { 1375 case noConfig: 1376 sla.Value = list 1377 case arch, os, osArch, productVariables, osAndInApex, errorProneDisabled, sanitizersEnabled: 1378 if sla.ConfigurableValues == nil { 1379 sla.ConfigurableValues = make(configurableStringLists) 1380 } 1381 sla.ConfigurableValues.setValueForAxis(axis, config, list) 1382 default: 1383 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) 1384 } 1385} 1386 1387// SelectValue gets a value for a bazel select for the given axis and config. 1388func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string { 1389 axis.validateConfig(config) 1390 switch axis.configurationType { 1391 case noConfig: 1392 return sla.Value 1393 case arch, os, osArch, productVariables, osAndInApex, errorProneDisabled, sanitizersEnabled: 1394 return sla.ConfigurableValues[axis][config] 1395 default: 1396 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) 1397 } 1398} 1399 1400// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order. 1401func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis { 1402 return SortedConfigurationAxes(sla.ConfigurableValues) 1403} 1404 1405// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and 1406// configuration-specific values. For example, if we would convert this StringListAttribute as: 1407// 1408// ["a", "b", "c"] + select({ 1409// "//condition:one": ["a", "d"], 1410// "//conditions:default": [], 1411// }) 1412// 1413// after this function, we would convert this StringListAttribute as: 1414// 1415// ["a", "b", "c"] + select({ 1416// "//condition:one": ["d"], 1417// "//conditions:default": [], 1418// }) 1419func (sla *StringListAttribute) DeduplicateAxesFromBase() { 1420 base := sla.Value 1421 for axis, configToList := range sla.ConfigurableValues { 1422 for config, list := range configToList { 1423 remaining := SubtractStrings(list, base) 1424 if len(remaining) == 0 { 1425 delete(sla.ConfigurableValues[axis], config) 1426 } else { 1427 sla.ConfigurableValues[axis][config] = remaining 1428 } 1429 } 1430 } 1431} 1432 1433// TryVariableSubstitution, replace string substitution formatting within each string in slice with 1434// Starlark string.format compatible tag for productVariable. 1435func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) { 1436 if len(slice) == 0 { 1437 return slice, false 1438 } 1439 ret := make([]string, 0, len(slice)) 1440 changesMade := false 1441 for _, s := range slice { 1442 newS, changed := TryVariableSubstitution(s, productVariable) 1443 ret = append(ret, newS) 1444 changesMade = changesMade || changed 1445 } 1446 return ret, changesMade 1447} 1448 1449// TryVariableSubstitution, replace string substitution formatting within s with Starlark 1450// string.format compatible tag for productVariable. 1451func TryVariableSubstitution(s string, productVariable string) (string, bool) { 1452 sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")") 1453 return sub, s != sub 1454} 1455 1456// StringMapAttribute is a map of strings. 1457// The use case for this is storing the flag_values in a config_setting object. 1458// Bazel rules do not support map attributes, and this should NOT be used in Bazel rules. 1459type StringMapAttribute map[string]string 1460 1461// ConfigSettingAttributes stores the keys of a config_setting object. 1462type ConfigSettingAttributes struct { 1463 // Each key in Flag_values is a label to a custom string_setting 1464 Flag_values StringMapAttribute 1465 // Each element in Constraint_values is a label to a constraint_value 1466 Constraint_values LabelListAttribute 1467} 1468