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