• 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_config/config_settings"
71
72	AndroidAndInApex = "android-in_apex"
73	AndroidPlatform  = "system"
74	Unbundled_app    = "unbundled_app"
75
76	InApex  = "in_apex"
77	NonApex = "non_apex"
78
79	ErrorproneDisabled = "errorprone_disabled"
80	// TODO: b/294868620 - Remove when completing the bug
81	SanitizersEnabled = "sanitizers_enabled"
82)
83
84func PowerSetWithoutEmptySet[T any](items []T) [][]T {
85	resultSize := int(math.Pow(2, float64(len(items))))
86	powerSet := make([][]T, 0, resultSize-1)
87	for i := 1; i < resultSize; i++ {
88		combination := make([]T, 0)
89		for j := 0; j < len(items); j++ {
90			if (i>>j)%2 == 1 {
91				combination = append(combination, items[j])
92			}
93		}
94		powerSet = append(powerSet, combination)
95	}
96	return powerSet
97}
98
99func createPlatformArchMap() map[string]string {
100	// Copy of archFeatures from android/arch_list.go because the bazel
101	// package can't access the android package
102	archFeatures := map[string][]string{
103		"arm": {
104			"neon",
105		},
106		"arm64": {
107			"dotprod",
108		},
109		"riscv64": {},
110		"x86": {
111			"ssse3",
112			"sse4",
113			"sse4_1",
114			"sse4_2",
115			"aes_ni",
116			"avx",
117			"avx2",
118			"avx512",
119			"popcnt",
120			"movbe",
121		},
122		"x86_64": {
123			"ssse3",
124			"sse4",
125			"sse4_1",
126			"sse4_2",
127			"aes_ni",
128			"avx",
129			"avx2",
130			"avx512",
131			"popcnt",
132		},
133	}
134	result := make(map[string]string)
135	for arch, allFeatures := range archFeatures {
136		result[arch] = "//build/bazel_common_rules/platforms/arch:" + arch
137		// Sometimes we want to select on multiple features being active, so
138		// add the power set of all possible features to the map. More details
139		// in android.ModuleBase.GetArchVariantProperties
140		for _, features := range PowerSetWithoutEmptySet(allFeatures) {
141			sort.Strings(features)
142			archFeaturesName := arch + "-" + strings.Join(features, "-")
143			result[archFeaturesName] = "//build/bazel/platforms/arch/variants:" + archFeaturesName
144		}
145	}
146	result[ConditionsDefaultConfigKey] = ConditionsDefaultSelectKey
147	return result
148}
149
150var (
151	// These are the list of OSes and architectures with a Bazel config_setting
152	// and constraint value equivalent. These exist in arch.go, but the android
153	// package depends on the bazel package, so a cyclic dependency prevents
154	// using those variables here.
155
156	// A map of architectures to the Bazel label of the constraint_value
157	// for the @platforms//cpu:cpu constraint_setting
158	platformArchMap = createPlatformArchMap()
159
160	// A map of target operating systems to the Bazel label of the
161	// constraint_value for the @platforms//os:os constraint_setting
162	platformOsMap = map[string]string{
163		OsAndroid:                  "//build/bazel_common_rules/platforms/os:android",
164		OsDarwin:                   "//build/bazel_common_rules/platforms/os:darwin",
165		OsLinux:                    "//build/bazel_common_rules/platforms/os:linux_glibc",
166		osLinuxMusl:                "//build/bazel_common_rules/platforms/os:linux_musl",
167		osLinuxBionic:              "//build/bazel_common_rules/platforms/os:linux_bionic",
168		OsWindows:                  "//build/bazel_common_rules/platforms/os:windows",
169		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map.
170	}
171
172	platformOsArchMap = map[string]string{
173		osArchAndroidArm:           "//build/bazel_common_rules/platforms/os_arch:android_arm",
174		OsArchAndroidArm64:         "//build/bazel_common_rules/platforms/os_arch:android_arm64",
175		osArchAndroidRiscv64:       "//build/bazel_common_rules/platforms/os_arch:android_riscv64",
176		osArchAndroidX86:           "//build/bazel_common_rules/platforms/os_arch:android_x86",
177		osArchAndroidX86_64:        "//build/bazel_common_rules/platforms/os_arch:android_x86_64",
178		osArchDarwinArm64:          "//build/bazel_common_rules/platforms/os_arch:darwin_arm64",
179		osArchDarwinX86_64:         "//build/bazel_common_rules/platforms/os_arch:darwin_x86_64",
180		osArchLinuxX86:             "//build/bazel_common_rules/platforms/os_arch:linux_glibc_x86",
181		osArchLinuxX86_64:          "//build/bazel_common_rules/platforms/os_arch:linux_glibc_x86_64",
182		osArchLinuxMuslArm:         "//build/bazel_common_rules/platforms/os_arch:linux_musl_arm",
183		osArchLinuxMuslArm64:       "//build/bazel_common_rules/platforms/os_arch:linux_musl_arm64",
184		osArchLinuxMuslX86:         "//build/bazel_common_rules/platforms/os_arch:linux_musl_x86",
185		osArchLinuxMuslX86_64:      "//build/bazel_common_rules/platforms/os_arch:linux_musl_x86_64",
186		osArchLinuxBionicArm64:     "//build/bazel_common_rules/platforms/os_arch:linux_bionic_arm64",
187		osArchLinuxBionicX86_64:    "//build/bazel_common_rules/platforms/os_arch:linux_bionic_x86_64",
188		osArchWindowsX86:           "//build/bazel_common_rules/platforms/os_arch:windows_x86",
189		osArchWindowsX86_64:        "//build/bazel_common_rules/platforms/os_arch:windows_x86_64",
190		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map.
191	}
192
193	// Map where keys are OsType names, and values are slices containing the archs
194	// that that OS supports.
195	// These definitions copied from arch.go.
196	// TODO(cparsons): Source from arch.go; this task is nontrivial, as it currently results
197	// in a cyclic dependency.
198	osToArchMap = map[string][]string{
199		OsAndroid:     {archArm, archArm64, archRiscv64, archX86, archX86_64},
200		OsLinux:       {archX86, archX86_64},
201		osLinuxMusl:   {archX86, archX86_64},
202		OsDarwin:      {archArm64, archX86_64},
203		osLinuxBionic: {archArm64, archX86_64},
204		// TODO(cparsons): According to arch.go, this should contain archArm, archArm64, as well.
205		OsWindows: {archX86, archX86_64},
206	}
207
208	osAndInApexMap = map[string]string{
209		AndroidAndInApex:           "//build/bazel/rules/apex:android-in_apex",
210		AndroidPlatform:            "//build/bazel/rules/apex:system",
211		Unbundled_app:              "//build/bazel/rules/apex:unbundled_app",
212		OsDarwin:                   "//build/bazel_common_rules/platforms/os:darwin",
213		OsLinux:                    "//build/bazel_common_rules/platforms/os:linux_glibc",
214		osLinuxMusl:                "//build/bazel_common_rules/platforms/os:linux_musl",
215		osLinuxBionic:              "//build/bazel_common_rules/platforms/os:linux_bionic",
216		OsWindows:                  "//build/bazel_common_rules/platforms/os:windows",
217		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
218	}
219
220	inApexMap = map[string]string{
221		InApex:                     "//build/bazel/rules/apex:in_apex",
222		NonApex:                    "//build/bazel/rules/apex:non_apex",
223		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
224	}
225
226	errorProneMap = map[string]string{
227		ErrorproneDisabled:         "//build/bazel/rules/java/errorprone:errorprone_globally_disabled",
228		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
229	}
230
231	// TODO: b/294868620 - Remove when completing the bug
232	sanitizersEnabledMap = map[string]string{
233		SanitizersEnabled:          "//build/bazel/rules/cc:sanitizers_enabled",
234		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
235	}
236)
237
238// basic configuration types
239type configurationType int
240
241const (
242	noConfig configurationType = iota
243	arch
244	os
245	osArch
246	productVariables
247	osAndInApex
248	inApex
249	errorProneDisabled
250	// TODO: b/294868620 - Remove when completing the bug
251	sanitizersEnabled
252)
253
254func osArchString(os string, arch string) string {
255	return fmt.Sprintf("%s_%s", os, arch)
256}
257
258func (ct configurationType) String() string {
259	return map[configurationType]string{
260		noConfig:           "no_config",
261		arch:               "arch",
262		os:                 "os",
263		osArch:             "arch_os",
264		productVariables:   "product_variables",
265		osAndInApex:        "os_in_apex",
266		inApex:             "in_apex",
267		errorProneDisabled: "errorprone_disabled",
268		// TODO: b/294868620 - Remove when completing the bug
269		sanitizersEnabled: "sanitizers_enabled",
270	}[ct]
271}
272
273func (ct configurationType) validateConfig(config string) {
274	switch ct {
275	case noConfig:
276		if config != "" {
277			panic(fmt.Errorf("Cannot specify config with %s, but got %s", ct, config))
278		}
279	case arch:
280		if _, ok := platformArchMap[config]; !ok {
281			panic(fmt.Errorf("Unknown arch: %s", config))
282		}
283	case os:
284		if _, ok := platformOsMap[config]; !ok {
285			panic(fmt.Errorf("Unknown os: %s", config))
286		}
287	case osArch:
288		if _, ok := platformOsArchMap[config]; !ok {
289			panic(fmt.Errorf("Unknown os+arch: %s", config))
290		}
291	case productVariables:
292		// do nothing
293	case osAndInApex:
294		// do nothing
295		// this axis can contain additional per-apex keys
296	case inApex:
297		if _, ok := inApexMap[config]; !ok {
298			panic(fmt.Errorf("Unknown in_apex config: %s", config))
299		}
300	case errorProneDisabled:
301		if _, ok := errorProneMap[config]; !ok {
302			panic(fmt.Errorf("Unknown errorprone config: %s", config))
303		}
304	// TODO: b/294868620 - Remove when completing the bug
305	case sanitizersEnabled:
306		if _, ok := sanitizersEnabledMap[config]; !ok {
307			panic(fmt.Errorf("Unknown sanitizers_enabled config: %s", config))
308		}
309	default:
310		panic(fmt.Errorf("Unrecognized ConfigurationType %d", ct))
311	}
312}
313
314// SelectKey returns the Bazel select key for a given configurationType and config string.
315func (ca ConfigurationAxis) SelectKey(config string) string {
316	ca.validateConfig(config)
317	switch ca.configurationType {
318	case noConfig:
319		panic(fmt.Errorf("SelectKey is unnecessary for noConfig ConfigurationType "))
320	case arch:
321		return platformArchMap[config]
322	case os:
323		return platformOsMap[config]
324	case osArch:
325		return platformOsArchMap[config]
326	case productVariables:
327		if config == ConditionsDefaultConfigKey {
328			return ConditionsDefaultSelectKey
329		}
330		return fmt.Sprintf("%s:%s", productVariableBazelPackage, config)
331	case osAndInApex:
332		if ret, exists := osAndInApexMap[config]; exists {
333			return ret
334		}
335		return config
336	case inApex:
337		return inApexMap[config]
338	case errorProneDisabled:
339		return errorProneMap[config]
340	// TODO: b/294868620 - Remove when completing the bug
341	case sanitizersEnabled:
342		return sanitizersEnabledMap[config]
343	default:
344		panic(fmt.Errorf("Unrecognized ConfigurationType %d", ca.configurationType))
345	}
346}
347
348var (
349	// Indicating there is no configuration axis
350	NoConfigAxis = ConfigurationAxis{configurationType: noConfig}
351	// An axis for architecture-specific configurations
352	ArchConfigurationAxis = ConfigurationAxis{configurationType: arch}
353	// An axis for os-specific configurations
354	OsConfigurationAxis = ConfigurationAxis{configurationType: os}
355	// An axis for arch+os-specific configurations
356	OsArchConfigurationAxis = ConfigurationAxis{configurationType: osArch}
357	// An axis for os+in_apex-specific configurations
358	OsAndInApexAxis = ConfigurationAxis{configurationType: osAndInApex}
359	// An axis for in_apex-specific configurations
360	InApexAxis = ConfigurationAxis{configurationType: inApex}
361
362	ErrorProneAxis = ConfigurationAxis{configurationType: errorProneDisabled}
363
364	// TODO: b/294868620 - Remove when completing the bug
365	SanitizersEnabledAxis = ConfigurationAxis{configurationType: sanitizersEnabled}
366)
367
368// ProductVariableConfigurationAxis returns an axis for the given product variable
369func ProductVariableConfigurationAxis(archVariant bool, variable string) ConfigurationAxis {
370	return ConfigurationAxis{
371		configurationType: productVariables,
372		subType:           variable,
373		archVariant:       archVariant,
374	}
375}
376
377// ConfigurationAxis is an independent axis for configuration, there should be no overlap between
378// elements within an axis.
379type ConfigurationAxis struct {
380	configurationType
381	// some configuration types (e.g. productVariables) have multiple independent axes, subType helps
382	// distinguish between them without needing to list all 17 product variables.
383	subType string
384
385	archVariant bool
386}
387
388func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool {
389	if ca.configurationType == other.configurationType {
390		return ca.subType < other.subType
391	}
392	return ca.configurationType < other.configurationType
393}
394