blob: 4cae21e42bdd1e1d6c1dc4adbad64d0281bb8453 [file] [log] [blame]
Jingwen Chen30f5aaa2020-11-19 05:38:02 -05001// 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
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +000017import (
18 "fmt"
Jingwen Chen63930982021-03-24 10:04:33 -040019 "path/filepath"
Liz Kammera060c452021-03-24 10:14:47 -040020 "regexp"
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +000021 "sort"
Liz Kammer6fd7b3f2021-05-06 13:54:29 -040022 "strings"
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +000023)
Jingwen Chen5d864492021-02-24 07:20:12 -050024
Jingwen Chen73850672020-12-14 08:25:34 -050025// BazelTargetModuleProperties contain properties and metadata used for
26// Blueprint to BUILD file conversion.
27type BazelTargetModuleProperties struct {
28 // The Bazel rule class for this target.
Liz Kammerfc46bc12021-02-19 11:06:17 -050029 Rule_class string `blueprint:"mutated"`
Jingwen Chen40067de2021-01-26 21:58:43 -050030
31 // The target label for the bzl file containing the definition of the rule class.
Liz Kammerfc46bc12021-02-19 11:06:17 -050032 Bzl_load_location string `blueprint:"mutated"`
Jingwen Chen73850672020-12-14 08:25:34 -050033}
Liz Kammer356f7d42021-01-26 09:18:53 -050034
Jingwen Chenfb4692a2021-02-07 10:05:16 -050035const BazelTargetModuleNamePrefix = "__bp2build__"
36
Liz Kammera060c452021-03-24 10:14:47 -040037var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
38
Jingwen Chen38e62642021-04-19 05:00:15 +000039// Label is used to represent a Bazel compatible Label. Also stores the original
40// bp text to support string replacement.
Liz Kammer356f7d42021-01-26 09:18:53 -050041type Label struct {
Jingwen Chen38e62642021-04-19 05:00:15 +000042 // 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
Liz Kammer356f7d42021-01-26 09:18:53 -050060}
61
62// LabelList is used to represent a list of Bazel labels.
63type LabelList struct {
64 Includes []Label
65 Excludes []Label
66}
67
Jingwen Chen63930982021-03-24 10:04:33 -040068// uniqueParentDirectories returns a list of the unique parent directories for
69// all files in ll.Includes.
70func (ll *LabelList) uniqueParentDirectories() []string {
71 dirMap := map[string]bool{}
72 for _, label := range ll.Includes {
73 dirMap[filepath.Dir(label.Label)] = true
74 }
75 dirs := []string{}
76 for dir := range dirMap {
77 dirs = append(dirs, dir)
78 }
79 return dirs
80}
81
Liz Kammer356f7d42021-01-26 09:18:53 -050082// Append appends the fields of other labelList to the corresponding fields of ll.
83func (ll *LabelList) Append(other LabelList) {
84 if len(ll.Includes) > 0 || len(other.Includes) > 0 {
85 ll.Includes = append(ll.Includes, other.Includes...)
86 }
87 if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
88 ll.Excludes = append(other.Excludes, other.Excludes...)
89 }
90}
Jingwen Chen5d864492021-02-24 07:20:12 -050091
Jingwen Chened9c17d2021-04-13 07:14:55 +000092// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
93// the slice in a sorted order.
94func UniqueSortedBazelLabels(originalLabels []Label) []Label {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +000095 uniqueLabelsSet := make(map[Label]bool)
96 for _, l := range originalLabels {
97 uniqueLabelsSet[l] = true
98 }
99 var uniqueLabels []Label
100 for l, _ := range uniqueLabelsSet {
101 uniqueLabels = append(uniqueLabels, l)
102 }
103 sort.SliceStable(uniqueLabels, func(i, j int) bool {
104 return uniqueLabels[i].Label < uniqueLabels[j].Label
105 })
106 return uniqueLabels
107}
108
109func UniqueBazelLabelList(originalLabelList LabelList) LabelList {
110 var uniqueLabelList LabelList
Jingwen Chened9c17d2021-04-13 07:14:55 +0000111 uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
112 uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000113 return uniqueLabelList
114}
115
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000116// Subtract needle from haystack
117func SubtractStrings(haystack []string, needle []string) []string {
118 // This is really a set
119 remainder := make(map[string]bool)
120
121 for _, s := range haystack {
122 remainder[s] = true
123 }
124 for _, s := range needle {
125 delete(remainder, s)
126 }
127
128 var strings []string
129 for s, _ := range remainder {
130 strings = append(strings, s)
131 }
132
133 sort.SliceStable(strings, func(i, j int) bool {
134 return strings[i] < strings[j]
135 })
136
137 return strings
138}
139
Chris Parsons990c4f42021-05-25 12:10:58 -0400140// Return all needles in a given haystack, where needleFn is true for needles.
141func FilterLabelList(haystack LabelList, needleFn func(string) bool) LabelList {
142 var includes []Label
Chris Parsons990c4f42021-05-25 12:10:58 -0400143 for _, inc := range haystack.Includes {
144 if needleFn(inc.Label) {
145 includes = append(includes, inc)
146 }
147 }
148 return LabelList{Includes: includes, Excludes: haystack.Excludes}
149}
150
151// Return all needles in a given haystack, where needleFn is true for needles.
152func FilterLabelListAttribute(haystack LabelListAttribute, needleFn func(string) bool) LabelListAttribute {
153 var result LabelListAttribute
154
155 result.Value = FilterLabelList(haystack.Value, needleFn)
156
157 for arch := range PlatformArchMap {
158 result.SetValueForArch(arch, FilterLabelList(haystack.GetValueForArch(arch), needleFn))
159 }
160
161 for os := range PlatformOsMap {
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400162 result.SetOsValueForTarget(os, FilterLabelList(haystack.GetOsValueForTarget(os), needleFn))
163
164 // TODO(b/187530594): Should we handle arch=CONDITIONS_DEFAULT here? (not in ArchValues)
165 for _, arch := range AllArches {
166 result.SetOsArchValueForTarget(os, arch, FilterLabelList(haystack.GetOsArchValueForTarget(os, arch), needleFn))
167 }
Chris Parsons990c4f42021-05-25 12:10:58 -0400168 }
169
170 return result
171}
172
173// Subtract needle from haystack
174func SubtractBazelLabelListAttribute(haystack LabelListAttribute, needle LabelListAttribute) LabelListAttribute {
175 var result LabelListAttribute
176
177 for arch := range PlatformArchMap {
178 result.SetValueForArch(arch,
179 SubtractBazelLabelList(haystack.GetValueForArch(arch), needle.GetValueForArch(arch)))
180 }
181
182 for os := range PlatformOsMap {
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400183 result.SetOsValueForTarget(os, SubtractBazelLabelList(haystack.GetOsValueForTarget(os), needle.GetOsValueForTarget(os)))
184
185 // TODO(b/187530594): Should we handle arch=CONDITIONS_DEFAULT here? (not in ArchValues)
186 for _, arch := range AllArches {
187 result.SetOsArchValueForTarget(os, arch, SubtractBazelLabelList(haystack.GetOsArchValueForTarget(os, arch), needle.GetOsArchValueForTarget(os, arch)))
188 }
Chris Parsons990c4f42021-05-25 12:10:58 -0400189 }
190
191 result.Value = SubtractBazelLabelList(haystack.Value, needle.Value)
192
193 return result
194}
195
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000196// Subtract needle from haystack
197func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
198 // This is really a set
199 remainder := make(map[Label]bool)
200
201 for _, label := range haystack {
202 remainder[label] = true
203 }
204 for _, label := range needle {
205 delete(remainder, label)
206 }
207
208 var labels []Label
209 for label, _ := range remainder {
210 labels = append(labels, label)
211 }
212
213 sort.SliceStable(labels, func(i, j int) bool {
214 return labels[i].Label < labels[j].Label
215 })
216
217 return labels
218}
219
Chris Parsons484e50a2021-05-13 15:13:04 -0400220// Appends two LabelLists, returning the combined list.
221func AppendBazelLabelLists(a LabelList, b LabelList) LabelList {
222 var result LabelList
223 result.Includes = append(a.Includes, b.Includes...)
224 result.Excludes = append(a.Excludes, b.Excludes...)
225 return result
226}
227
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000228// Subtract needle from haystack
229func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
230 var result LabelList
231 result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
232 // NOTE: Excludes are intentionally not subtracted
233 result.Excludes = haystack.Excludes
234 return result
235}
236
Jingwen Chen07027912021-03-15 06:02:43 -0400237const (
Jingwen Chen91220d72021-03-24 02:18:33 -0400238 // ArchType names in arch.go
Jingwen Chen07027912021-03-15 06:02:43 -0400239 ARCH_ARM = "arm"
240 ARCH_ARM64 = "arm64"
Jingwen Chen91220d72021-03-24 02:18:33 -0400241 ARCH_X86 = "x86"
242 ARCH_X86_64 = "x86_64"
243
244 // OsType names in arch.go
245 OS_ANDROID = "android"
246 OS_DARWIN = "darwin"
247 OS_FUCHSIA = "fuchsia"
248 OS_LINUX = "linux_glibc"
249 OS_LINUX_BIONIC = "linux_bionic"
250 OS_WINDOWS = "windows"
Jingwen Chene32e9e02021-04-23 09:17:24 +0000251
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400252 // Targets in arch.go
253 TARGET_ANDROID_ARM = "android_arm"
254 TARGET_ANDROID_ARM64 = "android_arm64"
255 TARGET_ANDROID_X86 = "android_x86"
256 TARGET_ANDROID_X86_64 = "android_x86_64"
257 TARGET_DARWIN_X86_64 = "darwin_x86_64"
258 TARGET_FUCHSIA_ARM64 = "fuchsia_arm64"
259 TARGET_FUCHSIA_X86_64 = "fuchsia_x86_64"
260 TARGET_LINUX_X86 = "linux_glibc_x86"
261 TARGET_LINUX_x86_64 = "linux_glibc_x86_64"
262 TARGET_LINUX_BIONIC_ARM64 = "linux_bionic_arm64"
263 TARGET_LINUX_BIONIC_X86_64 = "linux_bionic_x86_64"
264 TARGET_WINDOWS_X86 = "windows_x86"
265 TARGET_WINDOWS_X86_64 = "windows_x86_64"
266
Jingwen Chene32e9e02021-04-23 09:17:24 +0000267 // This is the string representation of the default condition wherever a
268 // configurable attribute is used in a select statement, i.e.
269 // //conditions:default for Bazel.
270 //
271 // This is consistently named "conditions_default" to mirror the Soong
272 // config variable default key in an Android.bp file, although there's no
273 // integration with Soong config variables (yet).
274 CONDITIONS_DEFAULT = "conditions_default"
Liz Kammer6fd7b3f2021-05-06 13:54:29 -0400275
276 ConditionsDefaultSelectKey = "//conditions:default"
277
278 productVariableBazelPackage = "//build/bazel/product_variables"
Jingwen Chen07027912021-03-15 06:02:43 -0400279)
280
281var (
Jingwen Chenc1c26502021-04-05 10:35:13 +0000282 // These are the list of OSes and architectures with a Bazel config_setting
283 // and constraint value equivalent. These exist in arch.go, but the android
284 // package depends on the bazel package, so a cyclic dependency prevents
285 // using those variables here.
Jingwen Chen91220d72021-03-24 02:18:33 -0400286
287 // A map of architectures to the Bazel label of the constraint_value
288 // for the @platforms//cpu:cpu constraint_setting
289 PlatformArchMap = map[string]string{
Jingwen Chene32e9e02021-04-23 09:17:24 +0000290 ARCH_ARM: "//build/bazel/platforms/arch:arm",
291 ARCH_ARM64: "//build/bazel/platforms/arch:arm64",
292 ARCH_X86: "//build/bazel/platforms/arch:x86",
293 ARCH_X86_64: "//build/bazel/platforms/arch:x86_64",
Liz Kammer6fd7b3f2021-05-06 13:54:29 -0400294 CONDITIONS_DEFAULT: ConditionsDefaultSelectKey, // The default condition of as arch select map.
Jingwen Chen91220d72021-03-24 02:18:33 -0400295 }
296
297 // A map of target operating systems to the Bazel label of the
298 // constraint_value for the @platforms//os:os constraint_setting
299 PlatformOsMap = map[string]string{
Jingwen Chene32e9e02021-04-23 09:17:24 +0000300 OS_ANDROID: "//build/bazel/platforms/os:android",
301 OS_DARWIN: "//build/bazel/platforms/os:darwin",
302 OS_FUCHSIA: "//build/bazel/platforms/os:fuchsia",
303 OS_LINUX: "//build/bazel/platforms/os:linux",
304 OS_LINUX_BIONIC: "//build/bazel/platforms/os:linux_bionic",
305 OS_WINDOWS: "//build/bazel/platforms/os:windows",
Liz Kammer6fd7b3f2021-05-06 13:54:29 -0400306 CONDITIONS_DEFAULT: ConditionsDefaultSelectKey, // The default condition of an os select map.
Jingwen Chen91220d72021-03-24 02:18:33 -0400307 }
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400308
309 PlatformTargetMap = map[string]string{
310 TARGET_ANDROID_ARM: "//build/bazel/platforms:android_arm",
311 TARGET_ANDROID_ARM64: "//build/bazel/platforms:android_arm64",
312 TARGET_ANDROID_X86: "//build/bazel/platforms:android_x86",
313 TARGET_ANDROID_X86_64: "//build/bazel/platforms:android_x86_64",
314 TARGET_DARWIN_X86_64: "//build/bazel/platforms:darwin_x86_64",
315 TARGET_FUCHSIA_ARM64: "//build/bazel/platforms:fuchsia_arm64",
316 TARGET_FUCHSIA_X86_64: "//build/bazel/platforms:fuchsia_x86_64",
317 TARGET_LINUX_X86: "//build/bazel/platforms:linux_glibc_x86",
318 TARGET_LINUX_x86_64: "//build/bazel/platforms:linux_glibc_x86_64",
319 TARGET_LINUX_BIONIC_ARM64: "//build/bazel/platforms:linux_bionic_arm64",
320 TARGET_LINUX_BIONIC_X86_64: "//build/bazel/platforms:linux_bionic_x86_64",
321 TARGET_WINDOWS_X86: "//build/bazel/platforms:windows_x86",
322 TARGET_WINDOWS_X86_64: "//build/bazel/platforms:windows_x86_64",
323 CONDITIONS_DEFAULT: ConditionsDefaultSelectKey, // The default condition of an os select map.
324 }
325
326 // TODO(b/187530594): Should we add CONDITIONS_DEFAULT here?
327 AllArches = []string{ARCH_ARM, ARCH_ARM64, ARCH_X86, ARCH_X86_64}
Jingwen Chen07027912021-03-15 06:02:43 -0400328)
329
Jingwen Chenc1c26502021-04-05 10:35:13 +0000330type Attribute interface {
331 HasConfigurableValues() bool
332}
333
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400334type labelArchValues struct {
Lukacs T. Berki598dd002021-05-05 09:00:01 +0200335 X86 Label
336 X86_64 Label
337 Arm Label
338 Arm64 Label
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400339
340 ConditionsDefault Label
341}
342
343type labelTargetValue struct {
344 // E.g. for android
345 OsValue Label
346
347 // E.g. for android_arm, android_arm64, ...
348 ArchValues labelArchValues
349}
350
351type labelTargetValues struct {
352 Android labelTargetValue
353 Darwin labelTargetValue
354 Fuchsia labelTargetValue
355 Linux labelTargetValue
356 LinuxBionic labelTargetValue
357 Windows labelTargetValue
358
359 ConditionsDefault labelTargetValue
360}
361
362// Represents an attribute whose value is a single label
363type LabelAttribute struct {
364 Value Label
365
366 ArchValues labelArchValues
367
368 TargetValues labelTargetValues
Lukacs T. Berki1353e592021-04-30 15:35:09 +0200369}
370
Lukacs T. Berki598dd002021-05-05 09:00:01 +0200371func (attr *LabelAttribute) GetValueForArch(arch string) Label {
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400372 var v *Label
373 if v = attr.archValuePtrs()[arch]; v == nil {
374 panic(fmt.Errorf("Unknown arch: %s", arch))
Lukacs T. Berki598dd002021-05-05 09:00:01 +0200375 }
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400376 return *v
Lukacs T. Berki598dd002021-05-05 09:00:01 +0200377}
378
379func (attr *LabelAttribute) SetValueForArch(arch string, value Label) {
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400380 var v *Label
381 if v = attr.archValuePtrs()[arch]; v == nil {
382 panic(fmt.Errorf("Unknown arch: %s", arch))
383 }
384 *v = value
385}
386
387func (attr *LabelAttribute) archValuePtrs() map[string]*Label {
388 return map[string]*Label{
389 ARCH_X86: &attr.ArchValues.X86,
390 ARCH_X86_64: &attr.ArchValues.X86_64,
391 ARCH_ARM: &attr.ArchValues.Arm,
392 ARCH_ARM64: &attr.ArchValues.Arm64,
393 CONDITIONS_DEFAULT: &attr.ArchValues.ConditionsDefault,
Lukacs T. Berki598dd002021-05-05 09:00:01 +0200394 }
395}
396
397func (attr LabelAttribute) HasConfigurableValues() bool {
Rupert Shuttleworth22cd2eb2021-05-27 02:15:54 -0400398 for arch := range PlatformArchMap {
399 if attr.GetValueForArch(arch).Label != "" {
400 return true
401 }
402 }
403
404 for os := range PlatformOsMap {
405 if attr.GetOsValueForTarget(os).Label != "" {
406 return true
407 }
408 // TODO(b/187530594): Should we also check arch=CONDITIONS_DEFAULT (not in AllArches)
409 for _, arch := range AllArches {
410 if attr.GetOsArchValueForTarget(os, arch).Label != "" {
411 return true
412 }
413 }
414 }
415 return false
416}
417
418func (attr *LabelAttribute) getValueForTarget(os string) labelTargetValue {
419 var v *labelTargetValue
420 if v = attr.targetValuePtrs()[os]; v == nil {
421 panic(fmt.Errorf("Unknown os: %s", os))
422 }
423 return *v
424}
425
426func (attr *LabelAttribute) GetOsValueForTarget(os string) Label {
427 var v *labelTargetValue
428 if v = attr.targetValuePtrs()[os]; v == nil {
429 panic(fmt.Errorf("Unknown os: %s", os))
430 }
431 return v.OsValue
432}
433
434func (attr *LabelAttribute) GetOsArchValueForTarget(os string, arch string) Label {
435 var v *labelTargetValue
436 if v = attr.targetValuePtrs()[os]; v == nil {
437 panic(fmt.Errorf("Unknown os: %s", os))
438 }
439 switch arch {
440 case ARCH_X86:
441 return v.ArchValues.X86
442 case ARCH_X86_64:
443 return v.ArchValues.X86_64
444 case ARCH_ARM:
445 return v.ArchValues.Arm
446 case ARCH_ARM64:
447 return v.ArchValues.Arm64
448 case CONDITIONS_DEFAULT:
449 return v.ArchValues.ConditionsDefault
450 default:
451 panic(fmt.Errorf("Unknown arch: %s\n", arch))
452 }
453}
454
455func (attr *LabelAttribute) setValueForTarget(os string, value labelTargetValue) {
456 var v *labelTargetValue
457 if v = attr.targetValuePtrs()[os]; v == nil {
458 panic(fmt.Errorf("Unknown os: %s", os))
459 }
460 *v = value
461}
462
463func (attr *LabelAttribute) SetOsValueForTarget(os string, value Label) {
464 var v *labelTargetValue
465 if v = attr.targetValuePtrs()[os]; v == nil {
466 panic(fmt.Errorf("Unknown os: %s", os))
467 }
468 v.OsValue = value
469}
470
471func (attr *LabelAttribute) SetOsArchValueForTarget(os string, arch string, value Label) {
472 var v *labelTargetValue
473 if v = attr.targetValuePtrs()[os]; v == nil {
474 panic(fmt.Errorf("Unknown os: %s", os))
475 }
476 switch arch {
477 case ARCH_X86:
478 v.ArchValues.X86 = value
479 case ARCH_X86_64:
480 v.ArchValues.X86_64 = value
481 case ARCH_ARM:
482 v.ArchValues.Arm = value
483 case ARCH_ARM64:
484 v.ArchValues.Arm64 = value
485 case CONDITIONS_DEFAULT:
486 v.ArchValues.ConditionsDefault = value
487 default:
488 panic(fmt.Errorf("Unknown arch: %s\n", arch))
489 }
490}
491
492func (attr *LabelAttribute) targetValuePtrs() map[string]*labelTargetValue {
493 return map[string]*labelTargetValue{
494 OS_ANDROID: &attr.TargetValues.Android,
495 OS_DARWIN: &attr.TargetValues.Darwin,
496 OS_FUCHSIA: &attr.TargetValues.Fuchsia,
497 OS_LINUX: &attr.TargetValues.Linux,
498 OS_LINUX_BIONIC: &attr.TargetValues.LinuxBionic,
499 OS_WINDOWS: &attr.TargetValues.Windows,
500 CONDITIONS_DEFAULT: &attr.TargetValues.ConditionsDefault,
501 }
Lukacs T. Berki1353e592021-04-30 15:35:09 +0200502}
503
Jingwen Chen07027912021-03-15 06:02:43 -0400504// Arch-specific label_list typed Bazel attribute values. This should correspond
505// to the types of architectures supported for compilation in arch.go.
506type labelListArchValues struct {
507 X86 LabelList
508 X86_64 LabelList
509 Arm LabelList
510 Arm64 LabelList
Jingwen Chene32e9e02021-04-23 09:17:24 +0000511
512 ConditionsDefault LabelList
Jingwen Chen91220d72021-03-24 02:18:33 -0400513}
514
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400515type labelListTargetValue struct {
516 // E.g. for android
517 OsValue LabelList
Jingwen Chene32e9e02021-04-23 09:17:24 +0000518
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400519 // E.g. for android_arm, android_arm64, ...
520 ArchValues labelListArchValues
521}
522
523func (target *labelListTargetValue) Append(other labelListTargetValue) {
524 target.OsValue.Append(other.OsValue)
525 target.ArchValues.X86.Append(other.ArchValues.X86)
526 target.ArchValues.X86_64.Append(other.ArchValues.X86_64)
527 target.ArchValues.Arm.Append(other.ArchValues.Arm)
528 target.ArchValues.Arm64.Append(other.ArchValues.Arm64)
529 target.ArchValues.ConditionsDefault.Append(other.ArchValues.ConditionsDefault)
530}
531
532type labelListTargetValues struct {
533 Android labelListTargetValue
534 Darwin labelListTargetValue
535 Fuchsia labelListTargetValue
536 Linux labelListTargetValue
537 LinuxBionic labelListTargetValue
538 Windows labelListTargetValue
539
540 ConditionsDefault labelListTargetValue
Jingwen Chen07027912021-03-15 06:02:43 -0400541}
542
543// LabelListAttribute is used to represent a list of Bazel labels as an
544// attribute.
545type LabelListAttribute struct {
546 // The non-arch specific attribute label list Value. Required.
547 Value LabelList
548
549 // The arch-specific attribute label list values. Optional. If used, these
550 // are generated in a select statement and appended to the non-arch specific
551 // label list Value.
552 ArchValues labelListArchValues
Jingwen Chen91220d72021-03-24 02:18:33 -0400553
554 // The os-specific attribute label list values. Optional. If used, these
555 // are generated in a select statement and appended to the non-os specific
556 // label list Value.
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400557 TargetValues labelListTargetValues
Jingwen Chen07027912021-03-15 06:02:43 -0400558}
559
560// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
561func MakeLabelListAttribute(value LabelList) LabelListAttribute {
562 return LabelListAttribute{Value: UniqueBazelLabelList(value)}
563}
564
Jingwen Chened9c17d2021-04-13 07:14:55 +0000565// Append all values, including os and arch specific ones, from another
Jingwen Chen63930982021-03-24 10:04:33 -0400566// LabelListAttribute to this LabelListAttribute.
567func (attrs *LabelListAttribute) Append(other LabelListAttribute) {
568 for arch := range PlatformArchMap {
569 this := attrs.GetValueForArch(arch)
570 that := other.GetValueForArch(arch)
571 this.Append(that)
572 attrs.SetValueForArch(arch, this)
573 }
574
575 for os := range PlatformOsMap {
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400576 this := attrs.getValueForTarget(os)
577 that := other.getValueForTarget(os)
Jingwen Chen63930982021-03-24 10:04:33 -0400578 this.Append(that)
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400579 attrs.setValueForTarget(os, this)
Jingwen Chen63930982021-03-24 10:04:33 -0400580 }
581
582 attrs.Value.Append(other.Value)
583}
584
Jingwen Chen07027912021-03-15 06:02:43 -0400585// HasArchSpecificValues returns true if the attribute contains
586// architecture-specific label_list values.
Jingwen Chenc1c26502021-04-05 10:35:13 +0000587func (attrs LabelListAttribute) HasConfigurableValues() bool {
588 for arch := range PlatformArchMap {
Jingwen Chen91220d72021-03-24 02:18:33 -0400589 if len(attrs.GetValueForArch(arch).Includes) > 0 {
590 return true
591 }
592 }
593
Jingwen Chenc1c26502021-04-05 10:35:13 +0000594 for os := range PlatformOsMap {
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400595 if len(attrs.GetOsValueForTarget(os).Includes) > 0 {
Jingwen Chen07027912021-03-15 06:02:43 -0400596 return true
597 }
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400598 // TODO(b/187530594): Should we also check arch=CONDITIONS_DEFAULT (not in AllArches)
599 for _, arch := range AllArches {
600 if len(attrs.GetOsArchValueForTarget(os, arch).Includes) > 0 {
601 return true
602 }
603 }
Jingwen Chen07027912021-03-15 06:02:43 -0400604 }
605 return false
606}
607
Jingwen Chen91220d72021-03-24 02:18:33 -0400608func (attrs *LabelListAttribute) archValuePtrs() map[string]*LabelList {
609 return map[string]*LabelList{
Jingwen Chene32e9e02021-04-23 09:17:24 +0000610 ARCH_X86: &attrs.ArchValues.X86,
611 ARCH_X86_64: &attrs.ArchValues.X86_64,
612 ARCH_ARM: &attrs.ArchValues.Arm,
613 ARCH_ARM64: &attrs.ArchValues.Arm64,
614 CONDITIONS_DEFAULT: &attrs.ArchValues.ConditionsDefault,
Jingwen Chen91220d72021-03-24 02:18:33 -0400615 }
616}
617
Jingwen Chen07027912021-03-15 06:02:43 -0400618// GetValueForArch returns the label_list attribute value for an architecture.
619func (attrs *LabelListAttribute) GetValueForArch(arch string) LabelList {
Jingwen Chen91220d72021-03-24 02:18:33 -0400620 var v *LabelList
621 if v = attrs.archValuePtrs()[arch]; v == nil {
Jingwen Chen07027912021-03-15 06:02:43 -0400622 panic(fmt.Errorf("Unknown arch: %s", arch))
623 }
Jingwen Chen91220d72021-03-24 02:18:33 -0400624 return *v
Jingwen Chen07027912021-03-15 06:02:43 -0400625}
626
627// SetValueForArch sets the label_list attribute value for an architecture.
628func (attrs *LabelListAttribute) SetValueForArch(arch string, value LabelList) {
Jingwen Chen91220d72021-03-24 02:18:33 -0400629 var v *LabelList
630 if v = attrs.archValuePtrs()[arch]; v == nil {
Jingwen Chen07027912021-03-15 06:02:43 -0400631 panic(fmt.Errorf("Unknown arch: %s", arch))
632 }
Jingwen Chen91220d72021-03-24 02:18:33 -0400633 *v = value
634}
635
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400636func (attrs *LabelListAttribute) targetValuePtrs() map[string]*labelListTargetValue {
637 return map[string]*labelListTargetValue{
638 OS_ANDROID: &attrs.TargetValues.Android,
639 OS_DARWIN: &attrs.TargetValues.Darwin,
640 OS_FUCHSIA: &attrs.TargetValues.Fuchsia,
641 OS_LINUX: &attrs.TargetValues.Linux,
642 OS_LINUX_BIONIC: &attrs.TargetValues.LinuxBionic,
643 OS_WINDOWS: &attrs.TargetValues.Windows,
644 CONDITIONS_DEFAULT: &attrs.TargetValues.ConditionsDefault,
Jingwen Chen91220d72021-03-24 02:18:33 -0400645 }
646}
647
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400648func (attrs *LabelListAttribute) getValueForTarget(os string) labelListTargetValue {
649 var v *labelListTargetValue
650 if v = attrs.targetValuePtrs()[os]; v == nil {
Jingwen Chen91220d72021-03-24 02:18:33 -0400651 panic(fmt.Errorf("Unknown os: %s", os))
652 }
653 return *v
654}
655
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400656func (attrs *LabelListAttribute) GetOsValueForTarget(os string) LabelList {
657 var v *labelListTargetValue
658 if v = attrs.targetValuePtrs()[os]; v == nil {
659 panic(fmt.Errorf("Unknown os: %s", os))
660 }
661 return v.OsValue
662}
663
664func (attrs *LabelListAttribute) GetOsArchValueForTarget(os string, arch string) LabelList {
665 var v *labelListTargetValue
666 if v = attrs.targetValuePtrs()[os]; v == nil {
667 panic(fmt.Errorf("Unknown os: %s", os))
668 }
669 switch arch {
670 case ARCH_X86:
671 return v.ArchValues.X86
672 case ARCH_X86_64:
673 return v.ArchValues.X86_64
674 case ARCH_ARM:
675 return v.ArchValues.Arm
676 case ARCH_ARM64:
677 return v.ArchValues.Arm64
678 case CONDITIONS_DEFAULT:
679 return v.ArchValues.ConditionsDefault
680 default:
681 panic(fmt.Errorf("Unknown arch: %s\n", arch))
682 }
683}
684
685func (attrs *LabelListAttribute) setValueForTarget(os string, value labelListTargetValue) {
686 var v *labelListTargetValue
687 if v = attrs.targetValuePtrs()[os]; v == nil {
Jingwen Chen91220d72021-03-24 02:18:33 -0400688 panic(fmt.Errorf("Unknown os: %s", os))
689 }
690 *v = value
Jingwen Chen07027912021-03-15 06:02:43 -0400691}
692
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400693func (attrs *LabelListAttribute) SetOsValueForTarget(os string, value LabelList) {
694 var v *labelListTargetValue
695 if v = attrs.targetValuePtrs()[os]; v == nil {
696 panic(fmt.Errorf("Unknown os: %s", os))
697 }
698 v.OsValue = value
699}
700
701func (attrs *LabelListAttribute) SetOsArchValueForTarget(os string, arch string, value LabelList) {
702 var v *labelListTargetValue
703 if v = attrs.targetValuePtrs()[os]; v == nil {
704 panic(fmt.Errorf("Unknown os: %s", os))
705 }
706 switch arch {
707 case ARCH_X86:
708 v.ArchValues.X86 = value
709 case ARCH_X86_64:
710 v.ArchValues.X86_64 = value
711 case ARCH_ARM:
712 v.ArchValues.Arm = value
713 case ARCH_ARM64:
714 v.ArchValues.Arm64 = value
715 case CONDITIONS_DEFAULT:
716 v.ArchValues.ConditionsDefault = value
717 default:
718 panic(fmt.Errorf("Unknown arch: %s\n", arch))
719 }
720}
721
Jingwen Chen5d864492021-02-24 07:20:12 -0500722// StringListAttribute corresponds to the string_list Bazel attribute type with
723// support for additional metadata, like configurations.
724type StringListAttribute struct {
725 // The base value of the string list attribute.
726 Value []string
727
Jingwen Chenc1c26502021-04-05 10:35:13 +0000728 // The arch-specific attribute string list values. Optional. If used, these
729 // are generated in a select statement and appended to the non-arch specific
730 // label list Value.
Jingwen Chen5d864492021-02-24 07:20:12 -0500731 ArchValues stringListArchValues
Jingwen Chenc1c26502021-04-05 10:35:13 +0000732
733 // The os-specific attribute string list values. Optional. If used, these
734 // are generated in a select statement and appended to the non-os specific
735 // label list Value.
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400736 TargetValues stringListTargetValues
Liz Kammer6fd7b3f2021-05-06 13:54:29 -0400737
738 // list of product-variable string list values. Optional. if used, each will generate a select
739 // statement appended to the label list Value.
740 ProductValues []ProductVariableValues
Jingwen Chen5d864492021-02-24 07:20:12 -0500741}
742
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000743// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
744func MakeStringListAttribute(value []string) StringListAttribute {
745 // NOTE: These strings are not necessarily unique or sorted.
746 return StringListAttribute{Value: value}
747}
748
Jingwen Chen5d864492021-02-24 07:20:12 -0500749// Arch-specific string_list typed Bazel attribute values. This should correspond
750// to the types of architectures supported for compilation in arch.go.
751type stringListArchValues struct {
Jingwen Chen07027912021-03-15 06:02:43 -0400752 X86 []string
753 X86_64 []string
754 Arm []string
755 Arm64 []string
Jingwen Chene32e9e02021-04-23 09:17:24 +0000756
757 ConditionsDefault []string
Jingwen Chen5d864492021-02-24 07:20:12 -0500758}
759
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400760type stringListTargetValue struct {
761 // E.g. for android
762 OsValue []string
Jingwen Chene32e9e02021-04-23 09:17:24 +0000763
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400764 // E.g. for android_arm, android_arm64, ...
765 ArchValues stringListArchValues
766}
767
768func (target *stringListTargetValue) Append(other stringListTargetValue) {
769 target.OsValue = append(target.OsValue, other.OsValue...)
770 target.ArchValues.X86 = append(target.ArchValues.X86, other.ArchValues.X86...)
771 target.ArchValues.X86_64 = append(target.ArchValues.X86_64, other.ArchValues.X86_64...)
772 target.ArchValues.Arm = append(target.ArchValues.Arm, other.ArchValues.Arm...)
773 target.ArchValues.Arm64 = append(target.ArchValues.Arm64, other.ArchValues.Arm64...)
774 target.ArchValues.ConditionsDefault = append(target.ArchValues.ConditionsDefault, other.ArchValues.ConditionsDefault...)
775}
776
777type stringListTargetValues struct {
778 Android stringListTargetValue
779 Darwin stringListTargetValue
780 Fuchsia stringListTargetValue
781 Linux stringListTargetValue
782 LinuxBionic stringListTargetValue
783 Windows stringListTargetValue
784
785 ConditionsDefault stringListTargetValue
Jingwen Chenc1c26502021-04-05 10:35:13 +0000786}
787
Liz Kammer6fd7b3f2021-05-06 13:54:29 -0400788// Product Variable values for StringListAttribute
789type ProductVariableValues struct {
790 ProductVariable string
791
792 Values []string
793}
794
795// SelectKey returns the appropriate select key for the receiving ProductVariableValues.
796func (p ProductVariableValues) SelectKey() string {
797 return fmt.Sprintf("%s:%s", productVariableBazelPackage, strings.ToLower(p.ProductVariable))
798}
799
Jingwen Chen91220d72021-03-24 02:18:33 -0400800// HasConfigurableValues returns true if the attribute contains
Jingwen Chen5d864492021-02-24 07:20:12 -0500801// architecture-specific string_list values.
Jingwen Chenc1c26502021-04-05 10:35:13 +0000802func (attrs StringListAttribute) HasConfigurableValues() bool {
803 for arch := range PlatformArchMap {
Jingwen Chen5d864492021-02-24 07:20:12 -0500804 if len(attrs.GetValueForArch(arch)) > 0 {
805 return true
806 }
807 }
Jingwen Chenc1c26502021-04-05 10:35:13 +0000808
809 for os := range PlatformOsMap {
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400810 if len(attrs.GetOsValueForTarget(os)) > 0 {
Jingwen Chenc1c26502021-04-05 10:35:13 +0000811 return true
812 }
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400813 // TODO(b/187530594): Should we also check arch=CONDITIONS_DEFAULT? (Not in AllArches)
814 for _, arch := range AllArches {
815 if len(attrs.GetOsArchValueForTarget(os, arch)) > 0 {
816 return true
817 }
818
819 }
Jingwen Chenc1c26502021-04-05 10:35:13 +0000820 }
Liz Kammer6fd7b3f2021-05-06 13:54:29 -0400821
822 return len(attrs.ProductValues) > 0
Jingwen Chen5d864492021-02-24 07:20:12 -0500823}
824
Jingwen Chen91220d72021-03-24 02:18:33 -0400825func (attrs *StringListAttribute) archValuePtrs() map[string]*[]string {
826 return map[string]*[]string{
Jingwen Chene32e9e02021-04-23 09:17:24 +0000827 ARCH_X86: &attrs.ArchValues.X86,
828 ARCH_X86_64: &attrs.ArchValues.X86_64,
829 ARCH_ARM: &attrs.ArchValues.Arm,
830 ARCH_ARM64: &attrs.ArchValues.Arm64,
831 CONDITIONS_DEFAULT: &attrs.ArchValues.ConditionsDefault,
Jingwen Chen91220d72021-03-24 02:18:33 -0400832 }
833}
834
Jingwen Chen5d864492021-02-24 07:20:12 -0500835// GetValueForArch returns the string_list attribute value for an architecture.
836func (attrs *StringListAttribute) GetValueForArch(arch string) []string {
Jingwen Chen91220d72021-03-24 02:18:33 -0400837 var v *[]string
838 if v = attrs.archValuePtrs()[arch]; v == nil {
Jingwen Chen5d864492021-02-24 07:20:12 -0500839 panic(fmt.Errorf("Unknown arch: %s", arch))
840 }
Jingwen Chen91220d72021-03-24 02:18:33 -0400841 return *v
Jingwen Chen5d864492021-02-24 07:20:12 -0500842}
843
844// SetValueForArch sets the string_list attribute value for an architecture.
845func (attrs *StringListAttribute) SetValueForArch(arch string, value []string) {
Jingwen Chen91220d72021-03-24 02:18:33 -0400846 var v *[]string
847 if v = attrs.archValuePtrs()[arch]; v == nil {
Jingwen Chen5d864492021-02-24 07:20:12 -0500848 panic(fmt.Errorf("Unknown arch: %s", arch))
849 }
Jingwen Chen91220d72021-03-24 02:18:33 -0400850 *v = value
Jingwen Chen5d864492021-02-24 07:20:12 -0500851}
Liz Kammera060c452021-03-24 10:14:47 -0400852
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400853func (attrs *StringListAttribute) targetValuePtrs() map[string]*stringListTargetValue {
854 return map[string]*stringListTargetValue{
855 OS_ANDROID: &attrs.TargetValues.Android,
856 OS_DARWIN: &attrs.TargetValues.Darwin,
857 OS_FUCHSIA: &attrs.TargetValues.Fuchsia,
858 OS_LINUX: &attrs.TargetValues.Linux,
859 OS_LINUX_BIONIC: &attrs.TargetValues.LinuxBionic,
860 OS_WINDOWS: &attrs.TargetValues.Windows,
861 CONDITIONS_DEFAULT: &attrs.TargetValues.ConditionsDefault,
Jingwen Chenc1c26502021-04-05 10:35:13 +0000862 }
863}
864
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400865func (attrs *StringListAttribute) getValueForTarget(os string) stringListTargetValue {
866 var v *stringListTargetValue
867 if v = attrs.targetValuePtrs()[os]; v == nil {
Jingwen Chenc1c26502021-04-05 10:35:13 +0000868 panic(fmt.Errorf("Unknown os: %s", os))
869 }
870 return *v
871}
872
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400873func (attrs *StringListAttribute) GetOsValueForTarget(os string) []string {
874 var v *stringListTargetValue
875 if v = attrs.targetValuePtrs()[os]; v == nil {
876 panic(fmt.Errorf("Unknown os: %s", os))
877 }
878 return v.OsValue
879}
880
881func (attrs *StringListAttribute) GetOsArchValueForTarget(os string, arch string) []string {
882 var v *stringListTargetValue
883 if v = attrs.targetValuePtrs()[os]; v == nil {
884 panic(fmt.Errorf("Unknown os: %s", os))
885 }
886 switch arch {
887 case ARCH_X86:
888 return v.ArchValues.X86
889 case ARCH_X86_64:
890 return v.ArchValues.X86_64
891 case ARCH_ARM:
892 return v.ArchValues.Arm
893 case ARCH_ARM64:
894 return v.ArchValues.Arm64
895 case CONDITIONS_DEFAULT:
896 return v.ArchValues.ConditionsDefault
897 default:
898 panic(fmt.Errorf("Unknown arch: %s\n", arch))
899 }
900}
901
902func (attrs *StringListAttribute) setValueForTarget(os string, value stringListTargetValue) {
903 var v *stringListTargetValue
904 if v = attrs.targetValuePtrs()[os]; v == nil {
Jingwen Chenc1c26502021-04-05 10:35:13 +0000905 panic(fmt.Errorf("Unknown os: %s", os))
906 }
907 *v = value
908}
909
Liz Kammere3e4a5f2021-05-10 11:39:53 -0400910func (attrs *StringListAttribute) SortedProductVariables() []ProductVariableValues {
911 vals := attrs.ProductValues[:]
912 sort.Slice(vals, func(i, j int) bool { return vals[i].ProductVariable < vals[j].ProductVariable })
913 return vals
914}
915
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400916func (attrs *StringListAttribute) SetOsValueForTarget(os string, value []string) {
917 var v *stringListTargetValue
918 if v = attrs.targetValuePtrs()[os]; v == nil {
919 panic(fmt.Errorf("Unknown os: %s", os))
920 }
921 v.OsValue = value
922}
923
924func (attrs *StringListAttribute) SetOsArchValueForTarget(os string, arch string, value []string) {
925 var v *stringListTargetValue
926 if v = attrs.targetValuePtrs()[os]; v == nil {
927 panic(fmt.Errorf("Unknown os: %s", os))
928 }
929 switch arch {
930 case ARCH_X86:
931 v.ArchValues.X86 = value
932 case ARCH_X86_64:
933 v.ArchValues.X86_64 = value
934 case ARCH_ARM:
935 v.ArchValues.Arm = value
936 case ARCH_ARM64:
937 v.ArchValues.Arm64 = value
938 case CONDITIONS_DEFAULT:
939 v.ArchValues.ConditionsDefault = value
940 default:
941 panic(fmt.Errorf("Unknown arch: %s\n", arch))
942 }
943}
944
Jingwen Chened9c17d2021-04-13 07:14:55 +0000945// Append appends all values, including os and arch specific ones, from another
946// StringListAttribute to this StringListAttribute
947func (attrs *StringListAttribute) Append(other StringListAttribute) {
948 for arch := range PlatformArchMap {
949 this := attrs.GetValueForArch(arch)
950 that := other.GetValueForArch(arch)
951 this = append(this, that...)
952 attrs.SetValueForArch(arch, this)
953 }
954
955 for os := range PlatformOsMap {
Rupert Shuttleworthc194ffb2021-05-19 06:49:02 -0400956 this := attrs.getValueForTarget(os)
957 that := other.getValueForTarget(os)
958 this.Append(that)
959 attrs.setValueForTarget(os, this)
Jingwen Chened9c17d2021-04-13 07:14:55 +0000960 }
961
Liz Kammer6fd7b3f2021-05-06 13:54:29 -0400962 productValues := make(map[string][]string, 0)
963 for _, pv := range attrs.ProductValues {
964 productValues[pv.ProductVariable] = pv.Values
965 }
966 for _, pv := range other.ProductValues {
967 productValues[pv.ProductVariable] = append(productValues[pv.ProductVariable], pv.Values...)
968 }
969 attrs.ProductValues = make([]ProductVariableValues, 0, len(productValues))
970 for pv, vals := range productValues {
971 attrs.ProductValues = append(attrs.ProductValues, ProductVariableValues{
972 ProductVariable: pv,
973 Values: vals,
974 })
975 }
976
Jingwen Chened9c17d2021-04-13 07:14:55 +0000977 attrs.Value = append(attrs.Value, other.Value...)
978}
979
Liz Kammera060c452021-03-24 10:14:47 -0400980// TryVariableSubstitution, replace string substitution formatting within each string in slice with
981// Starlark string.format compatible tag for productVariable.
982func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
983 ret := make([]string, 0, len(slice))
984 changesMade := false
985 for _, s := range slice {
986 newS, changed := TryVariableSubstitution(s, productVariable)
987 ret = append(ret, newS)
988 changesMade = changesMade || changed
989 }
990 return ret, changesMade
991}
992
993// TryVariableSubstitution, replace string substitution formatting within s with Starlark
994// string.format compatible tag for productVariable.
995func TryVariableSubstitution(s string, productVariable string) (string, bool) {
Liz Kammerba7a9c52021-05-26 08:45:30 -0400996 sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
Liz Kammera060c452021-03-24 10:14:47 -0400997 return sub, s != sub
998}