configurability.go 11 KB

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