configurability.go 12 KB

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