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)
23
24// BazelTargetModuleProperties contain properties and metadata used for
25// Blueprint to BUILD file conversion.
26type BazelTargetModuleProperties struct {
27	// The Bazel rule class for this target.
28	Rule_class string `blueprint:"mutated"`
29
30	// The target label for the bzl file containing the definition of the rule class.
31	Bzl_load_location string `blueprint:"mutated"`
32}
33
34const BazelTargetModuleNamePrefix = "__bp2build__"
35
36var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
37
38// Label is used to represent a Bazel compatible Label. Also stores the original
39// bp text to support string replacement.
40type Label struct {
41	// The string representation of a Bazel target label. This can be a relative
42	// or fully qualified label. These labels are used for generating BUILD
43	// files with bp2build.
44	Label string
45
46	// The original Soong/Blueprint module name that the label was derived from.
47	// This is used for replacing references to the original name with the new
48	// label, for example in genrule cmds.
49	//
50	// While there is a reversible 1:1 mapping from the module name to Bazel
51	// label with bp2build that could make computing the original module name
52	// from the label automatic, it is not the case for handcrafted targets,
53	// where modules can have a custom label mapping through the { bazel_module:
54	// { label: <label> } } property.
55	//
56	// With handcrafted labels, those modules don't go through bp2build
57	// conversion, but relies on handcrafted targets in the source tree.
58	OriginalModuleName string
59}
60
61// LabelList is used to represent a list of Bazel labels.
62type LabelList struct {
63	Includes []Label
64	Excludes []Label
65}
66
67// uniqueParentDirectories returns a list of the unique parent directories for
68// all files in ll.Includes.
69func (ll *LabelList) uniqueParentDirectories() []string {
70	dirMap := map[string]bool{}
71	for _, label := range ll.Includes {
72		dirMap[filepath.Dir(label.Label)] = true
73	}
74	dirs := []string{}
75	for dir := range dirMap {
76		dirs = append(dirs, dir)
77	}
78	return dirs
79}
80
81// Append appends the fields of other labelList to the corresponding fields of ll.
82func (ll *LabelList) Append(other LabelList) {
83	if len(ll.Includes) > 0 || len(other.Includes) > 0 {
84		ll.Includes = append(ll.Includes, other.Includes...)
85	}
86	if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
87		ll.Excludes = append(other.Excludes, other.Excludes...)
88	}
89}
90
91// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
92// the slice in a sorted order.
93func UniqueSortedBazelLabels(originalLabels []Label) []Label {
94	uniqueLabelsSet := make(map[Label]bool)
95	for _, l := range originalLabels {
96		uniqueLabelsSet[l] = true
97	}
98	var uniqueLabels []Label
99	for l, _ := range uniqueLabelsSet {
100		uniqueLabels = append(uniqueLabels, l)
101	}
102	sort.SliceStable(uniqueLabels, func(i, j int) bool {
103		return uniqueLabels[i].Label < uniqueLabels[j].Label
104	})
105	return uniqueLabels
106}
107
108func UniqueBazelLabelList(originalLabelList LabelList) LabelList {
109	var uniqueLabelList LabelList
110	uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
111	uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
112	return uniqueLabelList
113}
114
115// Subtract needle from haystack
116func SubtractStrings(haystack []string, needle []string) []string {
117	// This is really a set
118	remainder := make(map[string]bool)
119
120	for _, s := range haystack {
121		remainder[s] = true
122	}
123	for _, s := range needle {
124		delete(remainder, s)
125	}
126
127	var strings []string
128	for s, _ := range remainder {
129		strings = append(strings, s)
130	}
131
132	sort.SliceStable(strings, func(i, j int) bool {
133		return strings[i] < strings[j]
134	})
135
136	return strings
137}
138
139// Subtract needle from haystack
140func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
141	// This is really a set
142	remainder := make(map[Label]bool)
143
144	for _, label := range haystack {
145		remainder[label] = true
146	}
147	for _, label := range needle {
148		delete(remainder, label)
149	}
150
151	var labels []Label
152	for label, _ := range remainder {
153		labels = append(labels, label)
154	}
155
156	sort.SliceStable(labels, func(i, j int) bool {
157		return labels[i].Label < labels[j].Label
158	})
159
160	return labels
161}
162
163// Subtract needle from haystack
164func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
165	var result LabelList
166	result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
167	// NOTE: Excludes are intentionally not subtracted
168	result.Excludes = haystack.Excludes
169	return result
170}
171
172const (
173	// ArchType names in arch.go
174	ARCH_ARM    = "arm"
175	ARCH_ARM64  = "arm64"
176	ARCH_X86    = "x86"
177	ARCH_X86_64 = "x86_64"
178
179	// OsType names in arch.go
180	OS_ANDROID      = "android"
181	OS_DARWIN       = "darwin"
182	OS_FUCHSIA      = "fuchsia"
183	OS_LINUX        = "linux_glibc"
184	OS_LINUX_BIONIC = "linux_bionic"
185	OS_WINDOWS      = "windows"
186
187	// This is the string representation of the default condition wherever a
188	// configurable attribute is used in a select statement, i.e.
189	// //conditions:default for Bazel.
190	//
191	// This is consistently named "conditions_default" to mirror the Soong
192	// config variable default key in an Android.bp file, although there's no
193	// integration with Soong config variables (yet).
194	CONDITIONS_DEFAULT = "conditions_default"
195)
196
197var (
198	// These are the list of OSes and architectures with a Bazel config_setting
199	// and constraint value equivalent. These exist in arch.go, but the android
200	// package depends on the bazel package, so a cyclic dependency prevents
201	// using those variables here.
202
203	// A map of architectures to the Bazel label of the constraint_value
204	// for the @platforms//cpu:cpu constraint_setting
205	PlatformArchMap = map[string]string{
206		ARCH_ARM:           "//build/bazel/platforms/arch:arm",
207		ARCH_ARM64:         "//build/bazel/platforms/arch:arm64",
208		ARCH_X86:           "//build/bazel/platforms/arch:x86",
209		ARCH_X86_64:        "//build/bazel/platforms/arch:x86_64",
210		CONDITIONS_DEFAULT: "//conditions:default", // The default condition of as arch select map.
211	}
212
213	// A map of target operating systems to the Bazel label of the
214	// constraint_value for the @platforms//os:os constraint_setting
215	PlatformOsMap = map[string]string{
216		OS_ANDROID:         "//build/bazel/platforms/os:android",
217		OS_DARWIN:          "//build/bazel/platforms/os:darwin",
218		OS_FUCHSIA:         "//build/bazel/platforms/os:fuchsia",
219		OS_LINUX:           "//build/bazel/platforms/os:linux",
220		OS_LINUX_BIONIC:    "//build/bazel/platforms/os:linux_bionic",
221		OS_WINDOWS:         "//build/bazel/platforms/os:windows",
222		CONDITIONS_DEFAULT: "//conditions:default", // The default condition of an os select map.
223	}
224)
225
226type Attribute interface {
227	HasConfigurableValues() bool
228}
229
230// Represents an attribute whose value is a single label
231type LabelAttribute struct {
232	Value  Label
233	X86    Label
234	X86_64 Label
235	Arm    Label
236	Arm64  Label
237}
238
239func (attr *LabelAttribute) GetValueForArch(arch string) Label {
240	switch arch {
241	case ARCH_ARM:
242		return attr.Arm
243	case ARCH_ARM64:
244		return attr.Arm64
245	case ARCH_X86:
246		return attr.X86
247	case ARCH_X86_64:
248		return attr.X86_64
249	case CONDITIONS_DEFAULT:
250		return attr.Value
251	default:
252		panic("Invalid arch type")
253	}
254}
255
256func (attr *LabelAttribute) SetValueForArch(arch string, value Label) {
257	switch arch {
258	case ARCH_ARM:
259		attr.Arm = value
260	case ARCH_ARM64:
261		attr.Arm64 = value
262	case ARCH_X86:
263		attr.X86 = value
264	case ARCH_X86_64:
265		attr.X86_64 = value
266	default:
267		panic("Invalid arch type")
268	}
269}
270
271func (attr LabelAttribute) HasConfigurableValues() bool {
272	return attr.Arm.Label != "" || attr.Arm64.Label != "" || attr.X86.Label != "" || attr.X86_64.Label != ""
273}
274
275// Arch-specific label_list typed Bazel attribute values. This should correspond
276// to the types of architectures supported for compilation in arch.go.
277type labelListArchValues struct {
278	X86    LabelList
279	X86_64 LabelList
280	Arm    LabelList
281	Arm64  LabelList
282	Common LabelList
283
284	ConditionsDefault LabelList
285}
286
287type labelListOsValues struct {
288	Android     LabelList
289	Darwin      LabelList
290	Fuchsia     LabelList
291	Linux       LabelList
292	LinuxBionic LabelList
293	Windows     LabelList
294
295	ConditionsDefault LabelList
296}
297
298// LabelListAttribute is used to represent a list of Bazel labels as an
299// attribute.
300type LabelListAttribute struct {
301	// The non-arch specific attribute label list Value. Required.
302	Value LabelList
303
304	// The arch-specific attribute label list values. Optional. If used, these
305	// are generated in a select statement and appended to the non-arch specific
306	// label list Value.
307	ArchValues labelListArchValues
308
309	// The os-specific attribute label list values. Optional. If used, these
310	// are generated in a select statement and appended to the non-os specific
311	// label list Value.
312	OsValues labelListOsValues
313}
314
315// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
316func MakeLabelListAttribute(value LabelList) LabelListAttribute {
317	return LabelListAttribute{Value: UniqueBazelLabelList(value)}
318}
319
320// Append all values, including os and arch specific ones, from another
321// LabelListAttribute to this LabelListAttribute.
322func (attrs *LabelListAttribute) Append(other LabelListAttribute) {
323	for arch := range PlatformArchMap {
324		this := attrs.GetValueForArch(arch)
325		that := other.GetValueForArch(arch)
326		this.Append(that)
327		attrs.SetValueForArch(arch, this)
328	}
329
330	for os := range PlatformOsMap {
331		this := attrs.GetValueForOS(os)
332		that := other.GetValueForOS(os)
333		this.Append(that)
334		attrs.SetValueForOS(os, this)
335	}
336
337	attrs.Value.Append(other.Value)
338}
339
340// HasArchSpecificValues returns true if the attribute contains
341// architecture-specific label_list values.
342func (attrs LabelListAttribute) HasConfigurableValues() bool {
343	for arch := range PlatformArchMap {
344		if len(attrs.GetValueForArch(arch).Includes) > 0 {
345			return true
346		}
347	}
348
349	for os := range PlatformOsMap {
350		if len(attrs.GetValueForOS(os).Includes) > 0 {
351			return true
352		}
353	}
354	return false
355}
356
357func (attrs *LabelListAttribute) archValuePtrs() map[string]*LabelList {
358	return map[string]*LabelList{
359		ARCH_X86:           &attrs.ArchValues.X86,
360		ARCH_X86_64:        &attrs.ArchValues.X86_64,
361		ARCH_ARM:           &attrs.ArchValues.Arm,
362		ARCH_ARM64:         &attrs.ArchValues.Arm64,
363		CONDITIONS_DEFAULT: &attrs.ArchValues.ConditionsDefault,
364	}
365}
366
367// GetValueForArch returns the label_list attribute value for an architecture.
368func (attrs *LabelListAttribute) GetValueForArch(arch string) LabelList {
369	var v *LabelList
370	if v = attrs.archValuePtrs()[arch]; v == nil {
371		panic(fmt.Errorf("Unknown arch: %s", arch))
372	}
373	return *v
374}
375
376// SetValueForArch sets the label_list attribute value for an architecture.
377func (attrs *LabelListAttribute) SetValueForArch(arch string, value LabelList) {
378	var v *LabelList
379	if v = attrs.archValuePtrs()[arch]; v == nil {
380		panic(fmt.Errorf("Unknown arch: %s", arch))
381	}
382	*v = value
383}
384
385func (attrs *LabelListAttribute) osValuePtrs() map[string]*LabelList {
386	return map[string]*LabelList{
387		OS_ANDROID:         &attrs.OsValues.Android,
388		OS_DARWIN:          &attrs.OsValues.Darwin,
389		OS_FUCHSIA:         &attrs.OsValues.Fuchsia,
390		OS_LINUX:           &attrs.OsValues.Linux,
391		OS_LINUX_BIONIC:    &attrs.OsValues.LinuxBionic,
392		OS_WINDOWS:         &attrs.OsValues.Windows,
393		CONDITIONS_DEFAULT: &attrs.OsValues.ConditionsDefault,
394	}
395}
396
397// GetValueForOS returns the label_list attribute value for an OS target.
398func (attrs *LabelListAttribute) GetValueForOS(os string) LabelList {
399	var v *LabelList
400	if v = attrs.osValuePtrs()[os]; v == nil {
401		panic(fmt.Errorf("Unknown os: %s", os))
402	}
403	return *v
404}
405
406// SetValueForArch sets the label_list attribute value for an OS target.
407func (attrs *LabelListAttribute) SetValueForOS(os string, value LabelList) {
408	var v *LabelList
409	if v = attrs.osValuePtrs()[os]; v == nil {
410		panic(fmt.Errorf("Unknown os: %s", os))
411	}
412	*v = value
413}
414
415// StringListAttribute corresponds to the string_list Bazel attribute type with
416// support for additional metadata, like configurations.
417type StringListAttribute struct {
418	// The base value of the string list attribute.
419	Value []string
420
421	// The arch-specific attribute string list values. Optional. If used, these
422	// are generated in a select statement and appended to the non-arch specific
423	// label list Value.
424	ArchValues stringListArchValues
425
426	// The os-specific attribute string list values. Optional. If used, these
427	// are generated in a select statement and appended to the non-os specific
428	// label list Value.
429	OsValues stringListOsValues
430}
431
432// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
433func MakeStringListAttribute(value []string) StringListAttribute {
434	// NOTE: These strings are not necessarily unique or sorted.
435	return StringListAttribute{Value: value}
436}
437
438// Arch-specific string_list typed Bazel attribute values. This should correspond
439// to the types of architectures supported for compilation in arch.go.
440type stringListArchValues struct {
441	X86    []string
442	X86_64 []string
443	Arm    []string
444	Arm64  []string
445	Common []string
446
447	ConditionsDefault []string
448}
449
450type stringListOsValues struct {
451	Android     []string
452	Darwin      []string
453	Fuchsia     []string
454	Linux       []string
455	LinuxBionic []string
456	Windows     []string
457
458	ConditionsDefault []string
459}
460
461// HasConfigurableValues returns true if the attribute contains
462// architecture-specific string_list values.
463func (attrs StringListAttribute) HasConfigurableValues() bool {
464	for arch := range PlatformArchMap {
465		if len(attrs.GetValueForArch(arch)) > 0 {
466			return true
467		}
468	}
469
470	for os := range PlatformOsMap {
471		if len(attrs.GetValueForOS(os)) > 0 {
472			return true
473		}
474	}
475	return false
476}
477
478func (attrs *StringListAttribute) archValuePtrs() map[string]*[]string {
479	return map[string]*[]string{
480		ARCH_X86:           &attrs.ArchValues.X86,
481		ARCH_X86_64:        &attrs.ArchValues.X86_64,
482		ARCH_ARM:           &attrs.ArchValues.Arm,
483		ARCH_ARM64:         &attrs.ArchValues.Arm64,
484		CONDITIONS_DEFAULT: &attrs.ArchValues.ConditionsDefault,
485	}
486}
487
488// GetValueForArch returns the string_list attribute value for an architecture.
489func (attrs *StringListAttribute) GetValueForArch(arch string) []string {
490	var v *[]string
491	if v = attrs.archValuePtrs()[arch]; v == nil {
492		panic(fmt.Errorf("Unknown arch: %s", arch))
493	}
494	return *v
495}
496
497// SetValueForArch sets the string_list attribute value for an architecture.
498func (attrs *StringListAttribute) SetValueForArch(arch string, value []string) {
499	var v *[]string
500	if v = attrs.archValuePtrs()[arch]; v == nil {
501		panic(fmt.Errorf("Unknown arch: %s", arch))
502	}
503	*v = value
504}
505
506func (attrs *StringListAttribute) osValuePtrs() map[string]*[]string {
507	return map[string]*[]string{
508		OS_ANDROID:         &attrs.OsValues.Android,
509		OS_DARWIN:          &attrs.OsValues.Darwin,
510		OS_FUCHSIA:         &attrs.OsValues.Fuchsia,
511		OS_LINUX:           &attrs.OsValues.Linux,
512		OS_LINUX_BIONIC:    &attrs.OsValues.LinuxBionic,
513		OS_WINDOWS:         &attrs.OsValues.Windows,
514		CONDITIONS_DEFAULT: &attrs.OsValues.ConditionsDefault,
515	}
516}
517
518// GetValueForOS returns the string_list attribute value for an OS target.
519func (attrs *StringListAttribute) GetValueForOS(os string) []string {
520	var v *[]string
521	if v = attrs.osValuePtrs()[os]; v == nil {
522		panic(fmt.Errorf("Unknown os: %s", os))
523	}
524	return *v
525}
526
527// SetValueForArch sets the string_list attribute value for an OS target.
528func (attrs *StringListAttribute) SetValueForOS(os string, value []string) {
529	var v *[]string
530	if v = attrs.osValuePtrs()[os]; v == nil {
531		panic(fmt.Errorf("Unknown os: %s", os))
532	}
533	*v = value
534}
535
536// Append appends all values, including os and arch specific ones, from another
537// StringListAttribute to this StringListAttribute
538func (attrs *StringListAttribute) Append(other StringListAttribute) {
539	for arch := range PlatformArchMap {
540		this := attrs.GetValueForArch(arch)
541		that := other.GetValueForArch(arch)
542		this = append(this, that...)
543		attrs.SetValueForArch(arch, this)
544	}
545
546	for os := range PlatformOsMap {
547		this := attrs.GetValueForOS(os)
548		that := other.GetValueForOS(os)
549		this = append(this, that...)
550		attrs.SetValueForOS(os, this)
551	}
552
553	attrs.Value = append(attrs.Value, other.Value...)
554}
555
556// TryVariableSubstitution, replace string substitution formatting within each string in slice with
557// Starlark string.format compatible tag for productVariable.
558func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
559	ret := make([]string, 0, len(slice))
560	changesMade := false
561	for _, s := range slice {
562		newS, changed := TryVariableSubstitution(s, productVariable)
563		ret = append(ret, newS)
564		changesMade = changesMade || changed
565	}
566	return ret, changesMade
567}
568
569// TryVariableSubstitution, replace string substitution formatting within s with Starlark
570// string.format compatible tag for productVariable.
571func TryVariableSubstitution(s string, productVariable string) (string, bool) {
572	sub := productVariableSubstitutionPattern.ReplaceAllString(s, "{"+productVariable+"}")
573	return sub, s != sub
574}
575