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 "regexp" 21 "sort" 22 "strings" 23 24 "github.com/google/blueprint" 25) 26 27// BazelTargetModuleProperties contain properties and metadata used for 28// Blueprint to BUILD file conversion. 29type BazelTargetModuleProperties struct { 30 // The Bazel rule class for this target. 31 Rule_class string `blueprint:"mutated"` 32 33 // The target label for the bzl file containing the definition of the rule class. 34 Bzl_load_location string `blueprint:"mutated"` 35} 36 37var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)") 38 39// Label is used to represent a Bazel compatible Label. Also stores the original 40// bp text to support string replacement. 41type Label struct { 42 // The string representation of a Bazel target label. This can be a relative 43 // or fully qualified label. These labels are used for generating BUILD 44 // files with bp2build. 45 Label string 46 47 // The original Soong/Blueprint module name that the label was derived from. 48 // This is used for replacing references to the original name with the new 49 // label, for example in genrule cmds. 50 // 51 // While there is a reversible 1:1 mapping from the module name to Bazel 52 // label with bp2build that could make computing the original module name 53 // from the label automatic, it is not the case for handcrafted targets, 54 // where modules can have a custom label mapping through the { bazel_module: 55 // { label: <label> } } property. 56 // 57 // With handcrafted labels, those modules don't go through bp2build 58 // conversion, but relies on handcrafted targets in the source tree. 59 OriginalModuleName string 60} 61 62// LabelList is used to represent a list of Bazel labels. 63type LabelList struct { 64 Includes []Label 65 Excludes []Label 66} 67 68// MakeLabelList creates a LabelList from a list Label 69func MakeLabelList(labels []Label) LabelList { 70 return LabelList{ 71 Includes: labels, 72 Excludes: nil, 73 } 74} 75 76func (ll *LabelList) Equals(other LabelList) bool { 77 if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) { 78 return false 79 } 80 for i, _ := range ll.Includes { 81 if ll.Includes[i] != other.Includes[i] { 82 return false 83 } 84 } 85 for i, _ := range ll.Excludes { 86 if ll.Excludes[i] != other.Excludes[i] { 87 return false 88 } 89 } 90 return true 91} 92 93func (ll *LabelList) IsNil() bool { 94 return ll.Includes == nil && ll.Excludes == nil 95} 96 97func (ll *LabelList) deepCopy() LabelList { 98 return LabelList{ 99 Includes: ll.Includes[:], 100 Excludes: ll.Excludes[:], 101 } 102} 103 104// uniqueParentDirectories returns a list of the unique parent directories for 105// all files in ll.Includes. 106func (ll *LabelList) uniqueParentDirectories() []string { 107 dirMap := map[string]bool{} 108 for _, label := range ll.Includes { 109 dirMap[filepath.Dir(label.Label)] = true 110 } 111 dirs := []string{} 112 for dir := range dirMap { 113 dirs = append(dirs, dir) 114 } 115 return dirs 116} 117 118// Add inserts the label Label at the end of the LabelList. 119func (ll *LabelList) Add(label *Label) { 120 if label == nil { 121 return 122 } 123 ll.Includes = append(ll.Includes, *label) 124} 125 126// Append appends the fields of other labelList to the corresponding fields of ll. 127func (ll *LabelList) Append(other LabelList) { 128 if len(ll.Includes) > 0 || len(other.Includes) > 0 { 129 ll.Includes = append(ll.Includes, other.Includes...) 130 } 131 if len(ll.Excludes) > 0 || len(other.Excludes) > 0 { 132 ll.Excludes = append(other.Excludes, other.Excludes...) 133 } 134} 135 136// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns 137// the slice in a sorted order. 138func UniqueSortedBazelLabels(originalLabels []Label) []Label { 139 uniqueLabelsSet := make(map[Label]bool) 140 for _, l := range originalLabels { 141 uniqueLabelsSet[l] = true 142 } 143 var uniqueLabels []Label 144 for l, _ := range uniqueLabelsSet { 145 uniqueLabels = append(uniqueLabels, l) 146 } 147 sort.SliceStable(uniqueLabels, func(i, j int) bool { 148 return uniqueLabels[i].Label < uniqueLabels[j].Label 149 }) 150 return uniqueLabels 151} 152 153func FirstUniqueBazelLabels(originalLabels []Label) []Label { 154 var labels []Label 155 found := make(map[Label]bool, len(originalLabels)) 156 for _, l := range originalLabels { 157 if _, ok := found[l]; ok { 158 continue 159 } 160 labels = append(labels, l) 161 found[l] = true 162 } 163 return labels 164} 165 166func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList { 167 var uniqueLabelList LabelList 168 uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes) 169 uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes) 170 return uniqueLabelList 171} 172 173func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList { 174 var uniqueLabelList LabelList 175 uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes) 176 uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes) 177 return uniqueLabelList 178} 179 180// Subtract needle from haystack 181func SubtractStrings(haystack []string, needle []string) []string { 182 // This is really a set 183 needleMap := make(map[string]bool) 184 for _, s := range needle { 185 needleMap[s] = true 186 } 187 188 var strings []string 189 for _, s := range haystack { 190 if exclude := needleMap[s]; !exclude { 191 strings = append(strings, s) 192 } 193 } 194 195 return strings 196} 197 198// Subtract needle from haystack 199func SubtractBazelLabels(haystack []Label, needle []Label) []Label { 200 // This is really a set 201 needleMap := make(map[Label]bool) 202 for _, s := range needle { 203 needleMap[s] = true 204 } 205 206 var labels []Label 207 for _, label := range haystack { 208 if exclude := needleMap[label]; !exclude { 209 labels = append(labels, label) 210 } 211 } 212 213 return labels 214} 215 216// Appends two LabelLists, returning the combined list. 217func AppendBazelLabelLists(a LabelList, b LabelList) LabelList { 218 var result LabelList 219 result.Includes = append(a.Includes, b.Includes...) 220 result.Excludes = append(a.Excludes, b.Excludes...) 221 return result 222} 223 224// Subtract needle from haystack 225func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList { 226 var result LabelList 227 result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes) 228 // NOTE: Excludes are intentionally not subtracted 229 result.Excludes = haystack.Excludes 230 return result 231} 232 233type Attribute interface { 234 HasConfigurableValues() bool 235} 236 237type labelSelectValues map[string]*Label 238 239type configurableLabels map[ConfigurationAxis]labelSelectValues 240 241func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) { 242 if cl[axis] == nil { 243 cl[axis] = make(labelSelectValues) 244 } 245 cl[axis][config] = value 246} 247 248// Represents an attribute whose value is a single label 249type LabelAttribute struct { 250 Value *Label 251 252 ConfigurableValues configurableLabels 253} 254 255func (la *LabelAttribute) axisTypes() map[configurationType]bool { 256 types := map[configurationType]bool{} 257 for k := range la.ConfigurableValues { 258 if len(la.ConfigurableValues[k]) > 0 { 259 types[k.configurationType] = true 260 } 261 } 262 return types 263} 264 265// Collapse reduces the configurable axes of the label attribute to a single axis. 266// This is necessary for final writing to bp2build, as a configurable label 267// attribute can only be comprised by a single select. 268func (la *LabelAttribute) Collapse() error { 269 axisTypes := la.axisTypes() 270 _, containsOs := axisTypes[os] 271 _, containsArch := axisTypes[arch] 272 _, containsOsArch := axisTypes[osArch] 273 _, containsProductVariables := axisTypes[productVariables] 274 if containsProductVariables { 275 if containsOs || containsArch || containsOsArch { 276 return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes") 277 } 278 } 279 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) { 280 // If a bool attribute has both os and arch configuration axes, the only 281 // way to successfully union their values is to increase the granularity 282 // of the configuration criteria to os_arch. 283 for osType, supportedArchs := range osToArchMap { 284 for _, supportedArch := range supportedArchs { 285 osArch := osArchString(osType, supportedArch) 286 if archOsVal := la.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil { 287 // Do nothing, as the arch_os is explicitly defined already. 288 } else { 289 archVal := la.SelectValue(ArchConfigurationAxis, supportedArch) 290 osVal := la.SelectValue(OsConfigurationAxis, osType) 291 if osVal != nil && archVal != nil { 292 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator 293 // runs after os mutator. 294 la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal) 295 } else if osVal != nil && archVal == nil { 296 la.SetSelectValue(OsArchConfigurationAxis, osArch, *osVal) 297 } else if osVal == nil && archVal != nil { 298 la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal) 299 } 300 } 301 } 302 } 303 // All os_arch values are now set. Clear os and arch axes. 304 delete(la.ConfigurableValues, ArchConfigurationAxis) 305 delete(la.ConfigurableValues, OsConfigurationAxis) 306 } 307 return nil 308} 309 310// HasConfigurableValues returns whether there are configurable values set for this label. 311func (la LabelAttribute) HasConfigurableValues() bool { 312 for _, selectValues := range la.ConfigurableValues { 313 if len(selectValues) > 0 { 314 return true 315 } 316 } 317 return false 318} 319 320// SetValue sets the base, non-configured value for the Label 321func (la *LabelAttribute) SetValue(value Label) { 322 la.SetSelectValue(NoConfigAxis, "", value) 323} 324 325// SetSelectValue set a value for a bazel select for the given axis, config and value. 326func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) { 327 axis.validateConfig(config) 328 switch axis.configurationType { 329 case noConfig: 330 la.Value = &value 331 case arch, os, osArch, productVariables: 332 if la.ConfigurableValues == nil { 333 la.ConfigurableValues = make(configurableLabels) 334 } 335 la.ConfigurableValues.setValueForAxis(axis, config, &value) 336 default: 337 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) 338 } 339} 340 341// SelectValue gets a value for a bazel select for the given axis and config. 342func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) *Label { 343 axis.validateConfig(config) 344 switch axis.configurationType { 345 case noConfig: 346 return la.Value 347 case arch, os, osArch, productVariables: 348 return la.ConfigurableValues[axis][config] 349 default: 350 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) 351 } 352} 353 354// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order. 355func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis { 356 keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues)) 357 for k := range la.ConfigurableValues { 358 keys = append(keys, k) 359 } 360 361 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) }) 362 return keys 363} 364 365// MakeLabelAttribute turns a string into a LabelAttribute 366func MakeLabelAttribute(label string) *LabelAttribute { 367 return &LabelAttribute{ 368 Value: &Label{ 369 Label: label, 370 }, 371 } 372} 373 374type configToBools map[string]bool 375 376func (ctb configToBools) setValue(config string, value *bool) { 377 if value == nil { 378 if _, ok := ctb[config]; ok { 379 delete(ctb, config) 380 } 381 return 382 } 383 ctb[config] = *value 384} 385 386type configurableBools map[ConfigurationAxis]configToBools 387 388func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) { 389 if cb[axis] == nil { 390 cb[axis] = make(configToBools) 391 } 392 cb[axis].setValue(config, value) 393} 394 395// BoolAttribute represents an attribute whose value is a single bool but may be configurable.. 396type BoolAttribute struct { 397 Value *bool 398 399 ConfigurableValues configurableBools 400} 401 402// HasConfigurableValues returns whether there are configurable values for this attribute. 403func (ba BoolAttribute) HasConfigurableValues() bool { 404 for _, cfgToBools := range ba.ConfigurableValues { 405 if len(cfgToBools) > 0 { 406 return true 407 } 408 } 409 return false 410} 411 412// SetSelectValue sets value for the given axis/config. 413func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) { 414 axis.validateConfig(config) 415 switch axis.configurationType { 416 case noConfig: 417 ba.Value = value 418 case arch, os, osArch, productVariables: 419 if ba.ConfigurableValues == nil { 420 ba.ConfigurableValues = make(configurableBools) 421 } 422 ba.ConfigurableValues.setValueForAxis(axis, config, value) 423 default: 424 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) 425 } 426} 427 428// ToLabelListAttribute creates and returns a LabelListAttribute from this 429// bool attribute, where each bool in this attribute corresponds to a 430// label list value in the resultant attribute. 431func (ba *BoolAttribute) ToLabelListAttribute(falseVal LabelList, trueVal LabelList) (LabelListAttribute, error) { 432 getLabelList := func(boolPtr *bool) LabelList { 433 if boolPtr == nil { 434 return LabelList{nil, nil} 435 } else if *boolPtr { 436 return trueVal 437 } else { 438 return falseVal 439 } 440 } 441 442 mainVal := getLabelList(ba.Value) 443 if !ba.HasConfigurableValues() { 444 return MakeLabelListAttribute(mainVal), nil 445 } 446 447 result := LabelListAttribute{} 448 if err := ba.Collapse(); err != nil { 449 return result, err 450 } 451 452 for axis, configToBools := range ba.ConfigurableValues { 453 if len(configToBools) < 1 { 454 continue 455 } 456 for config, boolPtr := range configToBools { 457 val := getLabelList(&boolPtr) 458 if !val.Equals(mainVal) { 459 result.SetSelectValue(axis, config, val) 460 } 461 } 462 result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal) 463 } 464 465 return result, nil 466} 467 468// Collapse reduces the configurable axes of the boolean attribute to a single axis. 469// This is necessary for final writing to bp2build, as a configurable boolean 470// attribute can only be comprised by a single select. 471func (ba *BoolAttribute) Collapse() error { 472 axisTypes := ba.axisTypes() 473 _, containsOs := axisTypes[os] 474 _, containsArch := axisTypes[arch] 475 _, containsOsArch := axisTypes[osArch] 476 _, containsProductVariables := axisTypes[productVariables] 477 if containsProductVariables { 478 if containsOs || containsArch || containsOsArch { 479 return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes") 480 } 481 } 482 if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) { 483 // If a bool attribute has both os and arch configuration axes, the only 484 // way to successfully union their values is to increase the granularity 485 // of the configuration criteria to os_arch. 486 for osType, supportedArchs := range osToArchMap { 487 for _, supportedArch := range supportedArchs { 488 osArch := osArchString(osType, supportedArch) 489 if archOsVal := ba.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil { 490 // Do nothing, as the arch_os is explicitly defined already. 491 } else { 492 archVal := ba.SelectValue(ArchConfigurationAxis, supportedArch) 493 osVal := ba.SelectValue(OsConfigurationAxis, osType) 494 if osVal != nil && archVal != nil { 495 // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator 496 // runs after os mutator. 497 ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal) 498 } else if osVal != nil && archVal == nil { 499 ba.SetSelectValue(OsArchConfigurationAxis, osArch, osVal) 500 } else if osVal == nil && archVal != nil { 501 ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal) 502 } 503 } 504 } 505 } 506 // All os_arch values are now set. Clear os and arch axes. 507 delete(ba.ConfigurableValues, ArchConfigurationAxis) 508 delete(ba.ConfigurableValues, OsConfigurationAxis) 509 // Verify post-condition; this should never fail, provided no additional 510 // axes are introduced. 511 if len(ba.ConfigurableValues) > 1 { 512 panic(fmt.Errorf("error in collapsing attribute: %#v", ba)) 513 } 514 } 515 return nil 516} 517 518func (ba *BoolAttribute) axisTypes() map[configurationType]bool { 519 types := map[configurationType]bool{} 520 for k := range ba.ConfigurableValues { 521 if len(ba.ConfigurableValues[k]) > 0 { 522 types[k.configurationType] = true 523 } 524 } 525 return types 526} 527 528// SelectValue gets the value for the given axis/config. 529func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool { 530 axis.validateConfig(config) 531 switch axis.configurationType { 532 case noConfig: 533 return ba.Value 534 case arch, os, osArch, productVariables: 535 if v, ok := ba.ConfigurableValues[axis][config]; ok { 536 return &v 537 } else { 538 return nil 539 } 540 default: 541 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) 542 } 543} 544 545// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order. 546func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis { 547 keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues)) 548 for k := range ba.ConfigurableValues { 549 keys = append(keys, k) 550 } 551 552 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) }) 553 return keys 554} 555 556// labelListSelectValues supports config-specific label_list typed Bazel attribute values. 557type labelListSelectValues map[string]LabelList 558 559func (ll labelListSelectValues) addSelects(label labelSelectValues) { 560 for k, v := range label { 561 if label == nil { 562 continue 563 } 564 l := ll[k] 565 (&l).Add(v) 566 ll[k] = l 567 } 568} 569 570func (ll labelListSelectValues) appendSelects(other labelListSelectValues, forceSpecifyEmptyList bool) { 571 for k, v := range other { 572 l := ll[k] 573 if forceSpecifyEmptyList && l.IsNil() && !v.IsNil() { 574 l.Includes = []Label{} 575 } 576 (&l).Append(v) 577 ll[k] = l 578 } 579} 580 581// HasConfigurableValues returns whether there are configurable values within this set of selects. 582func (ll labelListSelectValues) HasConfigurableValues() bool { 583 for _, v := range ll { 584 if v.Includes != nil { 585 return true 586 } 587 } 588 return false 589} 590 591// LabelListAttribute is used to represent a list of Bazel labels as an 592// attribute. 593type LabelListAttribute struct { 594 // The non-configured attribute label list Value. Required. 595 Value LabelList 596 597 // The configured attribute label list Values. Optional 598 // a map of independent configurability axes 599 ConfigurableValues configurableLabelLists 600 601 // If true, differentiate between "nil" and "empty" list. nil means that 602 // this attribute should not be specified at all, and "empty" means that 603 // the attribute should be explicitly specified as an empty list. 604 // This mode facilitates use of attribute defaults: an empty list should 605 // override the default. 606 ForceSpecifyEmptyList bool 607 608 // If true, signal the intent to the code generator to emit all select keys, 609 // even if the Includes list for that key is empty. This mode facilitates 610 // specific select statements where an empty list for a non-default select 611 // key has a meaning. 612 EmitEmptyList bool 613} 614 615type configurableLabelLists map[ConfigurationAxis]labelListSelectValues 616 617func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) { 618 if list.IsNil() { 619 if _, ok := cll[axis][config]; ok { 620 delete(cll[axis], config) 621 } 622 return 623 } 624 if cll[axis] == nil { 625 cll[axis] = make(labelListSelectValues) 626 } 627 628 cll[axis][config] = list 629} 630 631func (cll configurableLabelLists) Append(other configurableLabelLists, forceSpecifyEmptyList bool) { 632 for axis, otherSelects := range other { 633 selects := cll[axis] 634 if selects == nil { 635 selects = make(labelListSelectValues, len(otherSelects)) 636 } 637 selects.appendSelects(otherSelects, forceSpecifyEmptyList) 638 cll[axis] = selects 639 } 640} 641 642func (lla *LabelListAttribute) Clone() *LabelListAttribute { 643 result := &LabelListAttribute{ForceSpecifyEmptyList: lla.ForceSpecifyEmptyList} 644 return result.Append(*lla) 645} 646 647// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value. 648func MakeLabelListAttribute(value LabelList) LabelListAttribute { 649 return LabelListAttribute{ 650 Value: value, 651 ConfigurableValues: make(configurableLabelLists), 652 } 653} 654 655func (lla *LabelListAttribute) SetValue(list LabelList) { 656 lla.SetSelectValue(NoConfigAxis, "", list) 657} 658 659// SetSelectValue set a value for a bazel select for the given axis, config and value. 660func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) { 661 axis.validateConfig(config) 662 switch axis.configurationType { 663 case noConfig: 664 lla.Value = list 665 case arch, os, osArch, productVariables: 666 if lla.ConfigurableValues == nil { 667 lla.ConfigurableValues = make(configurableLabelLists) 668 } 669 lla.ConfigurableValues.setValueForAxis(axis, config, list) 670 default: 671 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) 672 } 673} 674 675// SelectValue gets a value for a bazel select for the given axis and config. 676func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList { 677 axis.validateConfig(config) 678 switch axis.configurationType { 679 case noConfig: 680 return lla.Value 681 case arch, os, osArch, productVariables: 682 return lla.ConfigurableValues[axis][config] 683 default: 684 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) 685 } 686} 687 688// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order. 689func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis { 690 keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues)) 691 for k := range lla.ConfigurableValues { 692 keys = append(keys, k) 693 } 694 695 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) }) 696 return keys 697} 698 699// Append all values, including os and arch specific ones, from another 700// LabelListAttribute to this LabelListAttribute. Returns this LabelListAttribute. 701func (lla *LabelListAttribute) Append(other LabelListAttribute) *LabelListAttribute { 702 forceSpecifyEmptyList := lla.ForceSpecifyEmptyList || other.ForceSpecifyEmptyList 703 if forceSpecifyEmptyList && lla.Value.IsNil() && !other.Value.IsNil() { 704 lla.Value.Includes = []Label{} 705 } 706 lla.Value.Append(other.Value) 707 if lla.ConfigurableValues == nil { 708 lla.ConfigurableValues = make(configurableLabelLists) 709 } 710 lla.ConfigurableValues.Append(other.ConfigurableValues, forceSpecifyEmptyList) 711 return lla 712} 713 714// Add inserts the labels for each axis of LabelAttribute at the end of corresponding axis's 715// LabelList within the LabelListAttribute 716func (lla *LabelListAttribute) Add(label *LabelAttribute) { 717 if label == nil { 718 return 719 } 720 721 lla.Value.Add(label.Value) 722 if lla.ConfigurableValues == nil && label.ConfigurableValues != nil { 723 lla.ConfigurableValues = make(configurableLabelLists) 724 } 725 for axis, _ := range label.ConfigurableValues { 726 if _, exists := lla.ConfigurableValues[axis]; !exists { 727 lla.ConfigurableValues[axis] = make(labelListSelectValues) 728 } 729 lla.ConfigurableValues[axis].addSelects(label.ConfigurableValues[axis]) 730 } 731} 732 733// HasConfigurableValues returns true if the attribute contains axis-specific label list values. 734func (lla LabelListAttribute) HasConfigurableValues() bool { 735 for _, selectValues := range lla.ConfigurableValues { 736 if len(selectValues) > 0 { 737 return true 738 } 739 } 740 return false 741} 742 743// IsEmpty returns true if the attribute has no values under any configuration. 744func (lla LabelListAttribute) IsEmpty() bool { 745 if len(lla.Value.Includes) > 0 { 746 return false 747 } 748 for axis, _ := range lla.ConfigurableValues { 749 if lla.ConfigurableValues[axis].HasConfigurableValues() { 750 return false 751 } 752 } 753 return true 754} 755 756// IsNil returns true if the attribute has not been set for any configuration. 757func (lla LabelListAttribute) IsNil() bool { 758 if lla.Value.Includes != nil { 759 return false 760 } 761 return !lla.HasConfigurableValues() 762} 763 764// Exclude for the given axis, config, removes Includes in labelList from Includes and appends them 765// to Excludes. This is to special case any excludes that are not specified in a bp file but need to 766// be removed, e.g. if they could cause duplicate element failures. 767func (lla *LabelListAttribute) Exclude(axis ConfigurationAxis, config string, labelList LabelList) { 768 val := lla.SelectValue(axis, config) 769 newList := SubtractBazelLabelList(val, labelList) 770 newList.Excludes = append(newList.Excludes, labelList.Includes...) 771 lla.SetSelectValue(axis, config, newList) 772} 773 774// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from 775// the base value and included in default values as appropriate. 776func (lla *LabelListAttribute) ResolveExcludes() { 777 for axis, configToLabels := range lla.ConfigurableValues { 778 baseLabels := lla.Value.deepCopy() 779 for config, val := range configToLabels { 780 // Exclude config-specific excludes from base value 781 lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes}) 782 783 // add base values to config specific to add labels excluded by others in this axis 784 // then remove all config-specific excludes 785 allLabels := baseLabels.deepCopy() 786 allLabels.Append(val) 787 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes}) 788 } 789 790 // After going through all configs, delete the duplicates in the config 791 // values that are already in the base Value. 792 for config, val := range configToLabels { 793 lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value) 794 } 795 796 // Now that the Value list is finalized for this axis, compare it with 797 // the original list, and union the difference with the default 798 // condition for the axis. 799 difference := SubtractBazelLabelList(baseLabels, lla.Value) 800 existingDefaults := lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] 801 existingDefaults.Append(difference) 802 lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = FirstUniqueBazelLabelList(existingDefaults) 803 804 // if everything ends up without includes, just delete the axis 805 if !lla.ConfigurableValues[axis].HasConfigurableValues() { 806 delete(lla.ConfigurableValues, axis) 807 } 808 } 809} 810 811// OtherModuleContext is a limited context that has methods with information about other modules. 812type OtherModuleContext interface { 813 ModuleFromName(name string) (blueprint.Module, bool) 814 OtherModuleType(m blueprint.Module) string 815 OtherModuleName(m blueprint.Module) string 816 OtherModuleDir(m blueprint.Module) string 817 ModuleErrorf(fmt string, args ...interface{}) 818} 819 820// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed) 821// label and whether it was changed. 822type LabelMapper func(OtherModuleContext, Label) (string, bool) 823 824// LabelPartition contains descriptions of a partition for labels 825type LabelPartition struct { 826 // Extensions to include in this partition 827 Extensions []string 828 // LabelMapper is a function that can map a label to a new label, and indicate whether to include 829 // the mapped label in the partition 830 LabelMapper LabelMapper 831 // Whether to store files not included in any other partition in a group of LabelPartitions 832 // Only one partition in a group of LabelPartitions can enabled Keep_remainder 833 Keep_remainder bool 834} 835 836// LabelPartitions is a map of partition name to a LabelPartition describing the elements of the 837// partition 838type LabelPartitions map[string]LabelPartition 839 840// filter returns a pointer to a label if the label should be included in the partition or nil if 841// not. 842func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label { 843 if lf.LabelMapper != nil { 844 if newLabel, changed := lf.LabelMapper(ctx, label); changed { 845 return &Label{newLabel, label.OriginalModuleName} 846 } 847 } 848 for _, ext := range lf.Extensions { 849 if strings.HasSuffix(label.Label, ext) { 850 return &label 851 } 852 } 853 854 return nil 855} 856 857// PartitionToLabelListAttribute is map of partition name to a LabelListAttribute 858type PartitionToLabelListAttribute map[string]LabelListAttribute 859 860type partitionToLabelList map[string]*LabelList 861 862func (p partitionToLabelList) appendIncludes(partition string, label Label) { 863 if _, ok := p[partition]; !ok { 864 p[partition] = &LabelList{} 865 } 866 p[partition].Includes = append(p[partition].Includes, label) 867} 868 869func (p partitionToLabelList) excludes(partition string, excludes []Label) { 870 if _, ok := p[partition]; !ok { 871 p[partition] = &LabelList{} 872 } 873 p[partition].Excludes = excludes 874} 875 876// PartitionLabelListAttribute partitions a LabelListAttribute into the requested partitions 877func PartitionLabelListAttribute(ctx OtherModuleContext, lla *LabelListAttribute, partitions LabelPartitions) PartitionToLabelListAttribute { 878 ret := PartitionToLabelListAttribute{} 879 var partitionNames []string 880 // Stored as a pointer to distinguish nil (no remainder partition) from empty string partition 881 var remainderPartition *string 882 for p, f := range partitions { 883 partitionNames = append(partitionNames, p) 884 if f.Keep_remainder { 885 if remainderPartition != nil { 886 panic("only one partition can store the remainder") 887 } 888 // If we take the address of p in a loop, we'll end up with the last value of p in 889 // remainderPartition, we want the requested partition 890 capturePartition := p 891 remainderPartition = &capturePartition 892 } 893 } 894 895 partitionLabelList := func(axis ConfigurationAxis, config string) { 896 value := lla.SelectValue(axis, config) 897 partitionToLabels := partitionToLabelList{} 898 for _, item := range value.Includes { 899 wasFiltered := false 900 var inPartition *string 901 for partition, f := range partitions { 902 filtered := f.filter(ctx, item) 903 if filtered == nil { 904 // did not match this filter, keep looking 905 continue 906 } 907 wasFiltered = true 908 partitionToLabels.appendIncludes(partition, *filtered) 909 // don't need to check other partitions if this filter used the item, 910 // continue checking if mapped to another name 911 if *filtered == item { 912 if inPartition != nil { 913 ctx.ModuleErrorf("%q was found in multiple partitions: %q, %q", item.Label, *inPartition, partition) 914 } 915 capturePartition := partition 916 inPartition = &capturePartition 917 } 918 } 919 920 // if not specified in a partition, add to remainder partition if one exists 921 if !wasFiltered && remainderPartition != nil { 922 partitionToLabels.appendIncludes(*remainderPartition, item) 923 } 924 } 925 926 // ensure empty lists are maintained 927 if value.Excludes != nil { 928 for _, partition := range partitionNames { 929 partitionToLabels.excludes(partition, value.Excludes) 930 } 931 } 932 933 for partition, list := range partitionToLabels { 934 val := ret[partition] 935 (&val).SetSelectValue(axis, config, *list) 936 ret[partition] = val 937 } 938 } 939 940 partitionLabelList(NoConfigAxis, "") 941 for axis, configToList := range lla.ConfigurableValues { 942 for config, _ := range configToList { 943 partitionLabelList(axis, config) 944 } 945 } 946 return ret 947} 948 949// StringListAttribute corresponds to the string_list Bazel attribute type with 950// support for additional metadata, like configurations. 951type StringListAttribute struct { 952 // The base value of the string list attribute. 953 Value []string 954 955 // The configured attribute label list Values. Optional 956 // a map of independent configurability axes 957 ConfigurableValues configurableStringLists 958} 959 960type configurableStringLists map[ConfigurationAxis]stringListSelectValues 961 962func (csl configurableStringLists) Append(other configurableStringLists) { 963 for axis, otherSelects := range other { 964 selects := csl[axis] 965 if selects == nil { 966 selects = make(stringListSelectValues, len(otherSelects)) 967 } 968 selects.appendSelects(otherSelects) 969 csl[axis] = selects 970 } 971} 972 973func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) { 974 if csl[axis] == nil { 975 csl[axis] = make(stringListSelectValues) 976 } 977 csl[axis][config] = list 978} 979 980type stringListSelectValues map[string][]string 981 982func (sl stringListSelectValues) appendSelects(other stringListSelectValues) { 983 for k, v := range other { 984 sl[k] = append(sl[k], v...) 985 } 986} 987 988func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool { 989 for _, val := range sl { 990 if len(val) > 0 { 991 return true 992 } 993 } 994 return false 995} 996 997// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value. 998func MakeStringListAttribute(value []string) StringListAttribute { 999 // NOTE: These strings are not necessarily unique or sorted. 1000 return StringListAttribute{ 1001 Value: value, 1002 ConfigurableValues: make(configurableStringLists), 1003 } 1004} 1005 1006// HasConfigurableValues returns true if the attribute contains axis-specific string_list values. 1007func (sla StringListAttribute) HasConfigurableValues() bool { 1008 for _, selectValues := range sla.ConfigurableValues { 1009 if len(selectValues) > 0 { 1010 return true 1011 } 1012 } 1013 return false 1014} 1015 1016// Append appends all values, including os and arch specific ones, from another 1017// StringListAttribute to this StringListAttribute 1018func (sla *StringListAttribute) Append(other StringListAttribute) *StringListAttribute { 1019 sla.Value = append(sla.Value, other.Value...) 1020 if sla.ConfigurableValues == nil { 1021 sla.ConfigurableValues = make(configurableStringLists) 1022 } 1023 sla.ConfigurableValues.Append(other.ConfigurableValues) 1024 return sla 1025} 1026 1027func (sla *StringListAttribute) Clone() *StringListAttribute { 1028 result := &StringListAttribute{} 1029 return result.Append(*sla) 1030} 1031 1032// SetSelectValue set a value for a bazel select for the given axis, config and value. 1033func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) { 1034 axis.validateConfig(config) 1035 switch axis.configurationType { 1036 case noConfig: 1037 sla.Value = list 1038 case arch, os, osArch, productVariables: 1039 if sla.ConfigurableValues == nil { 1040 sla.ConfigurableValues = make(configurableStringLists) 1041 } 1042 sla.ConfigurableValues.setValueForAxis(axis, config, list) 1043 default: 1044 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) 1045 } 1046} 1047 1048// SelectValue gets a value for a bazel select for the given axis and config. 1049func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string { 1050 axis.validateConfig(config) 1051 switch axis.configurationType { 1052 case noConfig: 1053 return sla.Value 1054 case arch, os, osArch, productVariables: 1055 return sla.ConfigurableValues[axis][config] 1056 default: 1057 panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) 1058 } 1059} 1060 1061// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order. 1062func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis { 1063 keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues)) 1064 for k := range sla.ConfigurableValues { 1065 keys = append(keys, k) 1066 } 1067 1068 sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) }) 1069 return keys 1070} 1071 1072// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and 1073// configuration-specific values. For example, if we would convert this StringListAttribute as: 1074// ["a", "b", "c"] + select({ 1075// "//condition:one": ["a", "d"], 1076// "//conditions:default": [], 1077// }) 1078// after this function, we would convert this StringListAttribute as: 1079// ["a", "b", "c"] + select({ 1080// "//condition:one": ["d"], 1081// "//conditions:default": [], 1082// }) 1083func (sla *StringListAttribute) DeduplicateAxesFromBase() { 1084 base := sla.Value 1085 for axis, configToList := range sla.ConfigurableValues { 1086 for config, list := range configToList { 1087 remaining := SubtractStrings(list, base) 1088 if len(remaining) == 0 { 1089 delete(sla.ConfigurableValues[axis], config) 1090 } else { 1091 sla.ConfigurableValues[axis][config] = remaining 1092 } 1093 } 1094 } 1095} 1096 1097// TryVariableSubstitution, replace string substitution formatting within each string in slice with 1098// Starlark string.format compatible tag for productVariable. 1099func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) { 1100 ret := make([]string, 0, len(slice)) 1101 changesMade := false 1102 for _, s := range slice { 1103 newS, changed := TryVariableSubstitution(s, productVariable) 1104 ret = append(ret, newS) 1105 changesMade = changesMade || changed 1106 } 1107 return ret, changesMade 1108} 1109 1110// TryVariableSubstitution, replace string substitution formatting within s with Starlark 1111// string.format compatible tag for productVariable. 1112func TryVariableSubstitution(s string, productVariable string) (string, bool) { 1113 sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")") 1114 return sub, s != sub 1115} 1116