• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2021 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	"math"
20	"sort"
21	"strings"
22)
23
24const (
25	// ArchType names in arch.go
26	archArm     = "arm"
27	archArm64   = "arm64"
28	archRiscv64 = "riscv64"
29	archX86     = "x86"
30	archX86_64  = "x86_64"
31
32	// OsType names in arch.go
33	OsAndroid     = "android"
34	osDarwin      = "darwin"
35	osLinux       = "linux_glibc"
36	osLinuxMusl   = "linux_musl"
37	osLinuxBionic = "linux_bionic"
38	osWindows     = "windows"
39
40	// Targets in arch.go
41	osArchAndroidArm        = "android_arm"
42	osArchAndroidArm64      = "android_arm64"
43	osArchAndroidRiscv64    = "android_riscv64"
44	osArchAndroidX86        = "android_x86"
45	osArchAndroidX86_64     = "android_x86_64"
46	osArchDarwinArm64       = "darwin_arm64"
47	osArchDarwinX86_64      = "darwin_x86_64"
48	osArchLinuxX86          = "linux_glibc_x86"
49	osArchLinuxX86_64       = "linux_glibc_x86_64"
50	osArchLinuxMuslArm      = "linux_musl_arm"
51	osArchLinuxMuslArm64    = "linux_musl_arm64"
52	osArchLinuxMuslX86      = "linux_musl_x86"
53	osArchLinuxMuslX86_64   = "linux_musl_x86_64"
54	osArchLinuxBionicArm64  = "linux_bionic_arm64"
55	osArchLinuxBionicX86_64 = "linux_bionic_x86_64"
56	osArchWindowsX86        = "windows_x86"
57	osArchWindowsX86_64     = "windows_x86_64"
58
59	// This is the string representation of the default condition wherever a
60	// configurable attribute is used in a select statement, i.e.
61	// //conditions:default for Bazel.
62	//
63	// This is consistently named "conditions_default" to mirror the Soong
64	// config variable default key in an Android.bp file, although there's no
65	// integration with Soong config variables (yet).
66	ConditionsDefaultConfigKey = "conditions_default"
67
68	ConditionsDefaultSelectKey = "//conditions:default"
69
70	productVariableBazelPackage = "//build/bazel/product_variables"
71
72	AndroidAndInApex  = "android-in_apex"
73	AndroidAndNonApex = "android-non_apex"
74
75	InApex  = "in_apex"
76	NonApex = "non_apex"
77)
78
79func PowerSetWithoutEmptySet[T any](items []T) [][]T {
80	resultSize := int(math.Pow(2, float64(len(items))))
81	powerSet := make([][]T, 0, resultSize-1)
82	for i := 1; i < resultSize; i++ {
83		combination := make([]T, 0)
84		for j := 0; j < len(items); j++ {
85			if (i>>j)%2 == 1 {
86				combination = append(combination, items[j])
87			}
88		}
89		powerSet = append(powerSet, combination)
90	}
91	return powerSet
92}
93
94func createPlatformArchMap() map[string]string {
95	// Copy of archFeatures from android/arch_list.go because the bazel
96	// package can't access the android package
97	archFeatures := map[string][]string{
98		"arm": {
99			"neon",
100		},
101		"arm64": {
102			"dotprod",
103		},
104		"riscv64": {},
105		"x86": {
106			"ssse3",
107			"sse4",
108			"sse4_1",
109			"sse4_2",
110			"aes_ni",
111			"avx",
112			"avx2",
113			"avx512",
114			"popcnt",
115			"movbe",
116		},
117		"x86_64": {
118			"ssse3",
119			"sse4",
120			"sse4_1",
121			"sse4_2",
122			"aes_ni",
123			"avx",
124			"avx2",
125			"avx512",
126			"popcnt",
127		},
128	}
129	result := make(map[string]string)
130	for arch, allFeatures := range archFeatures {
131		result[arch] = "//build/bazel/platforms/arch:" + arch
132		// Sometimes we want to select on multiple features being active, so
133		// add the power set of all possible features to the map. More details
134		// in android.ModuleBase.GetArchVariantProperties
135		for _, features := range PowerSetWithoutEmptySet(allFeatures) {
136			sort.Strings(features)
137			archFeaturesName := arch + "-" + strings.Join(features, "-")
138			result[archFeaturesName] = "//build/bazel/platforms/arch/variants:" + archFeaturesName
139		}
140	}
141	result[ConditionsDefaultConfigKey] = ConditionsDefaultSelectKey
142	return result
143}
144
145var (
146	// These are the list of OSes and architectures with a Bazel config_setting
147	// and constraint value equivalent. These exist in arch.go, but the android
148	// package depends on the bazel package, so a cyclic dependency prevents
149	// using those variables here.
150
151	// A map of architectures to the Bazel label of the constraint_value
152	// for the @platforms//cpu:cpu constraint_setting
153	platformArchMap = createPlatformArchMap()
154
155	// A map of target operating systems to the Bazel label of the
156	// constraint_value for the @platforms//os:os constraint_setting
157	platformOsMap = map[string]string{
158		OsAndroid:                  "//build/bazel/platforms/os:android",
159		osDarwin:                   "//build/bazel/platforms/os:darwin",
160		osLinux:                    "//build/bazel/platforms/os:linux_glibc",
161		osLinuxMusl:                "//build/bazel/platforms/os:linux_musl",
162		osLinuxBionic:              "//build/bazel/platforms/os:linux_bionic",
163		osWindows:                  "//build/bazel/platforms/os:windows",
164		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map.
165	}
166
167	platformOsArchMap = map[string]string{
168		osArchAndroidArm:           "//build/bazel/platforms/os_arch:android_arm",
169		osArchAndroidArm64:         "//build/bazel/platforms/os_arch:android_arm64",
170		osArchAndroidRiscv64:       "//build/bazel/platforms/os_arch:android_riscv64",
171		osArchAndroidX86:           "//build/bazel/platforms/os_arch:android_x86",
172		osArchAndroidX86_64:        "//build/bazel/platforms/os_arch:android_x86_64",
173		osArchDarwinArm64:          "//build/bazel/platforms/os_arch:darwin_arm64",
174		osArchDarwinX86_64:         "//build/bazel/platforms/os_arch:darwin_x86_64",
175		osArchLinuxX86:             "//build/bazel/platforms/os_arch:linux_glibc_x86",
176		osArchLinuxX86_64:          "//build/bazel/platforms/os_arch:linux_glibc_x86_64",
177		osArchLinuxMuslArm:         "//build/bazel/platforms/os_arch:linux_musl_arm",
178		osArchLinuxMuslArm64:       "//build/bazel/platforms/os_arch:linux_musl_arm64",
179		osArchLinuxMuslX86:         "//build/bazel/platforms/os_arch:linux_musl_x86",
180		osArchLinuxMuslX86_64:      "//build/bazel/platforms/os_arch:linux_musl_x86_64",
181		osArchLinuxBionicArm64:     "//build/bazel/platforms/os_arch:linux_bionic_arm64",
182		osArchLinuxBionicX86_64:    "//build/bazel/platforms/os_arch:linux_bionic_x86_64",
183		osArchWindowsX86:           "//build/bazel/platforms/os_arch:windows_x86",
184		osArchWindowsX86_64:        "//build/bazel/platforms/os_arch:windows_x86_64",
185		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map.
186	}
187
188	// Map where keys are OsType names, and values are slices containing the archs
189	// that that OS supports.
190	// These definitions copied from arch.go.
191	// TODO(cparsons): Source from arch.go; this task is nontrivial, as it currently results
192	// in a cyclic dependency.
193	osToArchMap = map[string][]string{
194		OsAndroid:     {archArm, archArm64, archRiscv64, archX86, archX86_64},
195		osLinux:       {archX86, archX86_64},
196		osLinuxMusl:   {archX86, archX86_64},
197		osDarwin:      {archArm64, archX86_64},
198		osLinuxBionic: {archArm64, archX86_64},
199		// TODO(cparsons): According to arch.go, this should contain archArm, archArm64, as well.
200		osWindows: {archX86, archX86_64},
201	}
202
203	osAndInApexMap = map[string]string{
204		AndroidAndInApex:           "//build/bazel/rules/apex:android-in_apex",
205		AndroidAndNonApex:          "//build/bazel/rules/apex:android-non_apex",
206		osDarwin:                   "//build/bazel/platforms/os:darwin",
207		osLinux:                    "//build/bazel/platforms/os:linux_glibc",
208		osLinuxMusl:                "//build/bazel/platforms/os:linux_musl",
209		osLinuxBionic:              "//build/bazel/platforms/os:linux_bionic",
210		osWindows:                  "//build/bazel/platforms/os:windows",
211		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
212	}
213
214	inApexMap = map[string]string{
215		InApex:                     "//build/bazel/rules/apex:in_apex",
216		NonApex:                    "//build/bazel/rules/apex:non_apex",
217		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
218	}
219)
220
221// basic configuration types
222type configurationType int
223
224const (
225	noConfig configurationType = iota
226	arch
227	os
228	osArch
229	productVariables
230	osAndInApex
231	inApex
232)
233
234func osArchString(os string, arch string) string {
235	return fmt.Sprintf("%s_%s", os, arch)
236}
237
238func (ct configurationType) String() string {
239	return map[configurationType]string{
240		noConfig:         "no_config",
241		arch:             "arch",
242		os:               "os",
243		osArch:           "arch_os",
244		productVariables: "product_variables",
245		osAndInApex:      "os_in_apex",
246		inApex:           "in_apex",
247	}[ct]
248}
249
250func (ct configurationType) validateConfig(config string) {
251	switch ct {
252	case noConfig:
253		if config != "" {
254			panic(fmt.Errorf("Cannot specify config with %s, but got %s", ct, config))
255		}
256	case arch:
257		if _, ok := platformArchMap[config]; !ok {
258			panic(fmt.Errorf("Unknown arch: %s", config))
259		}
260	case os:
261		if _, ok := platformOsMap[config]; !ok {
262			panic(fmt.Errorf("Unknown os: %s", config))
263		}
264	case osArch:
265		if _, ok := platformOsArchMap[config]; !ok {
266			panic(fmt.Errorf("Unknown os+arch: %s", config))
267		}
268	case productVariables:
269		// do nothing
270	case osAndInApex:
271		if _, ok := osAndInApexMap[config]; !ok {
272			panic(fmt.Errorf("Unknown os+in_apex config: %s", config))
273		}
274	case inApex:
275		if _, ok := inApexMap[config]; !ok {
276			panic(fmt.Errorf("Unknown in_apex config: %s", config))
277		}
278	default:
279		panic(fmt.Errorf("Unrecognized ConfigurationType %d", ct))
280	}
281}
282
283// SelectKey returns the Bazel select key for a given configurationType and config string.
284func (ca ConfigurationAxis) SelectKey(config string) string {
285	ca.validateConfig(config)
286	switch ca.configurationType {
287	case noConfig:
288		panic(fmt.Errorf("SelectKey is unnecessary for noConfig ConfigurationType "))
289	case arch:
290		return platformArchMap[config]
291	case os:
292		return platformOsMap[config]
293	case osArch:
294		return platformOsArchMap[config]
295	case productVariables:
296		if strings.HasSuffix(config, ConditionsDefaultConfigKey) {
297			// e.g. "acme__feature1__conditions_default" or "android__board__conditions_default"
298			return ConditionsDefaultSelectKey
299		}
300		return fmt.Sprintf("%s:%s", productVariableBazelPackage, config)
301	case osAndInApex:
302		return osAndInApexMap[config]
303	case inApex:
304		return inApexMap[config]
305	default:
306		panic(fmt.Errorf("Unrecognized ConfigurationType %d", ca.configurationType))
307	}
308}
309
310var (
311	// Indicating there is no configuration axis
312	NoConfigAxis = ConfigurationAxis{configurationType: noConfig}
313	// An axis for architecture-specific configurations
314	ArchConfigurationAxis = ConfigurationAxis{configurationType: arch}
315	// An axis for os-specific configurations
316	OsConfigurationAxis = ConfigurationAxis{configurationType: os}
317	// An axis for arch+os-specific configurations
318	OsArchConfigurationAxis = ConfigurationAxis{configurationType: osArch}
319	// An axis for os+in_apex-specific configurations
320	OsAndInApexAxis = ConfigurationAxis{configurationType: osAndInApex}
321	// An axis for in_apex-specific configurations
322	InApexAxis = ConfigurationAxis{configurationType: inApex}
323)
324
325// ProductVariableConfigurationAxis returns an axis for the given product variable
326func ProductVariableConfigurationAxis(variable string, outerAxis ConfigurationAxis) ConfigurationAxis {
327	return ConfigurationAxis{
328		configurationType: productVariables,
329		subType:           variable,
330		outerAxisType:     outerAxis.configurationType,
331	}
332}
333
334// ConfigurationAxis is an independent axis for configuration, there should be no overlap between
335// elements within an axis.
336type ConfigurationAxis struct {
337	configurationType
338	// some configuration types (e.g. productVariables) have multiple independent axes, subType helps
339	// distinguish between them without needing to list all 17 product variables.
340	subType string
341	// used to keep track of which product variables are arch variant
342	outerAxisType configurationType
343}
344
345func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool {
346	if ca.configurationType == other.configurationType {
347		return ca.subType < other.subType
348	}
349	return ca.configurationType < other.configurationType
350}
351