| // Copyright 2021 Google Inc. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package bazel |
| |
| import ( |
| "fmt" |
| "math" |
| "sort" |
| "strings" |
| ) |
| |
| const ( |
| // ArchType names in arch.go |
| archArm = "arm" |
| archArm64 = "arm64" |
| archRiscv64 = "riscv64" |
| archX86 = "x86" |
| archX86_64 = "x86_64" |
| |
| // OsType names in arch.go |
| OsAndroid = "android" |
| osDarwin = "darwin" |
| osLinux = "linux_glibc" |
| osLinuxMusl = "linux_musl" |
| osLinuxBionic = "linux_bionic" |
| osWindows = "windows" |
| |
| // Targets in arch.go |
| osArchAndroidArm = "android_arm" |
| osArchAndroidArm64 = "android_arm64" |
| osArchAndroidRiscv64 = "android_riscv64" |
| osArchAndroidX86 = "android_x86" |
| osArchAndroidX86_64 = "android_x86_64" |
| osArchDarwinArm64 = "darwin_arm64" |
| osArchDarwinX86_64 = "darwin_x86_64" |
| osArchLinuxX86 = "linux_glibc_x86" |
| osArchLinuxX86_64 = "linux_glibc_x86_64" |
| osArchLinuxMuslArm = "linux_musl_arm" |
| osArchLinuxMuslArm64 = "linux_musl_arm64" |
| osArchLinuxMuslX86 = "linux_musl_x86" |
| osArchLinuxMuslX86_64 = "linux_musl_x86_64" |
| osArchLinuxBionicArm64 = "linux_bionic_arm64" |
| osArchLinuxBionicX86_64 = "linux_bionic_x86_64" |
| osArchWindowsX86 = "windows_x86" |
| osArchWindowsX86_64 = "windows_x86_64" |
| |
| // This is the string representation of the default condition wherever a |
| // configurable attribute is used in a select statement, i.e. |
| // //conditions:default for Bazel. |
| // |
| // This is consistently named "conditions_default" to mirror the Soong |
| // config variable default key in an Android.bp file, although there's no |
| // integration with Soong config variables (yet). |
| ConditionsDefaultConfigKey = "conditions_default" |
| |
| ConditionsDefaultSelectKey = "//conditions:default" |
| |
| productVariableBazelPackage = "//build/bazel/product_variables" |
| |
| AndroidAndInApex = "android-in_apex" |
| AndroidAndNonApex = "android-non_apex" |
| |
| InApex = "in_apex" |
| NonApex = "non_apex" |
| ) |
| |
| func PowerSetWithoutEmptySet[T any](items []T) [][]T { |
| resultSize := int(math.Pow(2, float64(len(items)))) |
| powerSet := make([][]T, 0, resultSize-1) |
| for i := 1; i < resultSize; i++ { |
| combination := make([]T, 0) |
| for j := 0; j < len(items); j++ { |
| if (i>>j)%2 == 1 { |
| combination = append(combination, items[j]) |
| } |
| } |
| powerSet = append(powerSet, combination) |
| } |
| return powerSet |
| } |
| |
| func createPlatformArchMap() map[string]string { |
| // Copy of archFeatures from android/arch_list.go because the bazel |
| // package can't access the android package |
| archFeatures := map[string][]string{ |
| "arm": { |
| "neon", |
| }, |
| "arm64": { |
| "dotprod", |
| }, |
| "riscv64": {}, |
| "x86": { |
| "ssse3", |
| "sse4", |
| "sse4_1", |
| "sse4_2", |
| "aes_ni", |
| "avx", |
| "avx2", |
| "avx512", |
| "popcnt", |
| "movbe", |
| }, |
| "x86_64": { |
| "ssse3", |
| "sse4", |
| "sse4_1", |
| "sse4_2", |
| "aes_ni", |
| "avx", |
| "avx2", |
| "avx512", |
| "popcnt", |
| }, |
| } |
| result := make(map[string]string) |
| for arch, allFeatures := range archFeatures { |
| result[arch] = "//build/bazel/platforms/arch:" + arch |
| // Sometimes we want to select on multiple features being active, so |
| // add the power set of all possible features to the map. More details |
| // in android.ModuleBase.GetArchVariantProperties |
| for _, features := range PowerSetWithoutEmptySet(allFeatures) { |
| sort.Strings(features) |
| archFeaturesName := arch + "-" + strings.Join(features, "-") |
| result[archFeaturesName] = "//build/bazel/platforms/arch/variants:" + archFeaturesName |
| } |
| } |
| result[ConditionsDefaultConfigKey] = ConditionsDefaultSelectKey |
| return result |
| } |
| |
| var ( |
| // These are the list of OSes and architectures with a Bazel config_setting |
| // and constraint value equivalent. These exist in arch.go, but the android |
| // package depends on the bazel package, so a cyclic dependency prevents |
| // using those variables here. |
| |
| // A map of architectures to the Bazel label of the constraint_value |
| // for the @platforms//cpu:cpu constraint_setting |
| platformArchMap = createPlatformArchMap() |
| |
| // A map of target operating systems to the Bazel label of the |
| // constraint_value for the @platforms//os:os constraint_setting |
| platformOsMap = map[string]string{ |
| OsAndroid: "//build/bazel/platforms/os:android", |
| osDarwin: "//build/bazel/platforms/os:darwin", |
| osLinux: "//build/bazel/platforms/os:linux_glibc", |
| osLinuxMusl: "//build/bazel/platforms/os:linux_musl", |
| osLinuxBionic: "//build/bazel/platforms/os:linux_bionic", |
| osWindows: "//build/bazel/platforms/os:windows", |
| ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map. |
| } |
| |
| platformOsArchMap = map[string]string{ |
| osArchAndroidArm: "//build/bazel/platforms/os_arch:android_arm", |
| osArchAndroidArm64: "//build/bazel/platforms/os_arch:android_arm64", |
| osArchAndroidRiscv64: "//build/bazel/platforms/os_arch:android_riscv64", |
| osArchAndroidX86: "//build/bazel/platforms/os_arch:android_x86", |
| osArchAndroidX86_64: "//build/bazel/platforms/os_arch:android_x86_64", |
| osArchDarwinArm64: "//build/bazel/platforms/os_arch:darwin_arm64", |
| osArchDarwinX86_64: "//build/bazel/platforms/os_arch:darwin_x86_64", |
| osArchLinuxX86: "//build/bazel/platforms/os_arch:linux_glibc_x86", |
| osArchLinuxX86_64: "//build/bazel/platforms/os_arch:linux_glibc_x86_64", |
| osArchLinuxMuslArm: "//build/bazel/platforms/os_arch:linux_musl_arm", |
| osArchLinuxMuslArm64: "//build/bazel/platforms/os_arch:linux_musl_arm64", |
| osArchLinuxMuslX86: "//build/bazel/platforms/os_arch:linux_musl_x86", |
| osArchLinuxMuslX86_64: "//build/bazel/platforms/os_arch:linux_musl_x86_64", |
| osArchLinuxBionicArm64: "//build/bazel/platforms/os_arch:linux_bionic_arm64", |
| osArchLinuxBionicX86_64: "//build/bazel/platforms/os_arch:linux_bionic_x86_64", |
| osArchWindowsX86: "//build/bazel/platforms/os_arch:windows_x86", |
| osArchWindowsX86_64: "//build/bazel/platforms/os_arch:windows_x86_64", |
| ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map. |
| } |
| |
| // Map where keys are OsType names, and values are slices containing the archs |
| // that that OS supports. |
| // These definitions copied from arch.go. |
| // TODO(cparsons): Source from arch.go; this task is nontrivial, as it currently results |
| // in a cyclic dependency. |
| osToArchMap = map[string][]string{ |
| OsAndroid: {archArm, archArm64, archRiscv64, archX86, archX86_64}, |
| osLinux: {archX86, archX86_64}, |
| osLinuxMusl: {archX86, archX86_64}, |
| osDarwin: {archArm64, archX86_64}, |
| osLinuxBionic: {archArm64, archX86_64}, |
| // TODO(cparsons): According to arch.go, this should contain archArm, archArm64, as well. |
| osWindows: {archX86, archX86_64}, |
| } |
| |
| osAndInApexMap = map[string]string{ |
| AndroidAndInApex: "//build/bazel/rules/apex:android-in_apex", |
| AndroidAndNonApex: "//build/bazel/rules/apex:android-non_apex", |
| osDarwin: "//build/bazel/platforms/os:darwin", |
| osLinux: "//build/bazel/platforms/os:linux_glibc", |
| osLinuxMusl: "//build/bazel/platforms/os:linux_musl", |
| osLinuxBionic: "//build/bazel/platforms/os:linux_bionic", |
| osWindows: "//build/bazel/platforms/os:windows", |
| ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, |
| } |
| |
| inApexMap = map[string]string{ |
| InApex: "//build/bazel/rules/apex:in_apex", |
| NonApex: "//build/bazel/rules/apex:non_apex", |
| ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, |
| } |
| ) |
| |
| // basic configuration types |
| type configurationType int |
| |
| const ( |
| noConfig configurationType = iota |
| arch |
| os |
| osArch |
| productVariables |
| osAndInApex |
| inApex |
| ) |
| |
| func osArchString(os string, arch string) string { |
| return fmt.Sprintf("%s_%s", os, arch) |
| } |
| |
| func (ct configurationType) String() string { |
| return map[configurationType]string{ |
| noConfig: "no_config", |
| arch: "arch", |
| os: "os", |
| osArch: "arch_os", |
| productVariables: "product_variables", |
| osAndInApex: "os_in_apex", |
| inApex: "in_apex", |
| }[ct] |
| } |
| |
| func (ct configurationType) validateConfig(config string) { |
| switch ct { |
| case noConfig: |
| if config != "" { |
| panic(fmt.Errorf("Cannot specify config with %s, but got %s", ct, config)) |
| } |
| case arch: |
| if _, ok := platformArchMap[config]; !ok { |
| panic(fmt.Errorf("Unknown arch: %s", config)) |
| } |
| case os: |
| if _, ok := platformOsMap[config]; !ok { |
| panic(fmt.Errorf("Unknown os: %s", config)) |
| } |
| case osArch: |
| if _, ok := platformOsArchMap[config]; !ok { |
| panic(fmt.Errorf("Unknown os+arch: %s", config)) |
| } |
| case productVariables: |
| // do nothing |
| case osAndInApex: |
| if _, ok := osAndInApexMap[config]; !ok { |
| panic(fmt.Errorf("Unknown os+in_apex config: %s", config)) |
| } |
| case inApex: |
| if _, ok := inApexMap[config]; !ok { |
| panic(fmt.Errorf("Unknown in_apex config: %s", config)) |
| } |
| default: |
| panic(fmt.Errorf("Unrecognized ConfigurationType %d", ct)) |
| } |
| } |
| |
| // SelectKey returns the Bazel select key for a given configurationType and config string. |
| func (ca ConfigurationAxis) SelectKey(config string) string { |
| ca.validateConfig(config) |
| switch ca.configurationType { |
| case noConfig: |
| panic(fmt.Errorf("SelectKey is unnecessary for noConfig ConfigurationType ")) |
| case arch: |
| return platformArchMap[config] |
| case os: |
| return platformOsMap[config] |
| case osArch: |
| return platformOsArchMap[config] |
| case productVariables: |
| if strings.HasSuffix(config, ConditionsDefaultConfigKey) { |
| // e.g. "acme__feature1__conditions_default" or "android__board__conditions_default" |
| return ConditionsDefaultSelectKey |
| } |
| return fmt.Sprintf("%s:%s", productVariableBazelPackage, config) |
| case osAndInApex: |
| return osAndInApexMap[config] |
| case inApex: |
| return inApexMap[config] |
| default: |
| panic(fmt.Errorf("Unrecognized ConfigurationType %d", ca.configurationType)) |
| } |
| } |
| |
| var ( |
| // Indicating there is no configuration axis |
| NoConfigAxis = ConfigurationAxis{configurationType: noConfig} |
| // An axis for architecture-specific configurations |
| ArchConfigurationAxis = ConfigurationAxis{configurationType: arch} |
| // An axis for os-specific configurations |
| OsConfigurationAxis = ConfigurationAxis{configurationType: os} |
| // An axis for arch+os-specific configurations |
| OsArchConfigurationAxis = ConfigurationAxis{configurationType: osArch} |
| // An axis for os+in_apex-specific configurations |
| OsAndInApexAxis = ConfigurationAxis{configurationType: osAndInApex} |
| // An axis for in_apex-specific configurations |
| InApexAxis = ConfigurationAxis{configurationType: inApex} |
| ) |
| |
| // ProductVariableConfigurationAxis returns an axis for the given product variable |
| func ProductVariableConfigurationAxis(variable string, outerAxis ConfigurationAxis) ConfigurationAxis { |
| return ConfigurationAxis{ |
| configurationType: productVariables, |
| subType: variable, |
| outerAxisType: outerAxis.configurationType, |
| } |
| } |
| |
| // ConfigurationAxis is an independent axis for configuration, there should be no overlap between |
| // elements within an axis. |
| type ConfigurationAxis struct { |
| configurationType |
| // some configuration types (e.g. productVariables) have multiple independent axes, subType helps |
| // distinguish between them without needing to list all 17 product variables. |
| subType string |
| // used to keep track of which product variables are arch variant |
| outerAxisType configurationType |
| } |
| |
| func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool { |
| if ca.configurationType == other.configurationType { |
| return ca.subType < other.subType |
| } |
| return ca.configurationType < other.configurationType |
| } |