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