1# Copyright (C) 2023 The Android Open Source Project 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 15load("@//build/bazel/tests/products:product_labels.bzl", _test_product_labels = "product_labels") 16load("@soong_injection//product_config_platforms:product_labels.bzl", _product_labels = "product_labels") 17load("//build/bazel/platforms/arch/variants:constants.bzl", _arch_constants = "constants") 18load("//build/bazel/product_variables:constants.bzl", "constants") 19load( 20 "//prebuilts/clang/host/linux-x86:cc_toolchain_constants.bzl", 21 "arch_to_variants", 22 "variant_constraints", 23 "variant_name", 24) 25load(":product_variables_providing_rule.bzl", "product_variables_providing_rule") 26 27all_android_product_labels = _product_labels + _test_product_labels 28 29def _is_variant_default(arch, variant): 30 return variant == None or variant in (arch, "generic") 31 32def _soong_arch_config_to_struct(soong_arch_config): 33 return struct( 34 arch = soong_arch_config["arch"], 35 arch_variant = soong_arch_config["arch_variant"], 36 cpu_variant = soong_arch_config["cpu_variant"], 37 ) 38 39def _determine_target_arches_from_config(config): 40 arches = [] 41 42 # ndk_abis and aml_abis explicitly get handled first as they override any setting 43 # for DeviceArch, DeviceSecondaryArch in Soong: 44 # https://cs.android.com/android/platform/superproject/+/master:build/soong/android/config.go;l=455-468;drc=b45a2ea782074944f79fc388df20b06e01f265f7 45 if config.get("Ndk_abis"): 46 for arch_config in _arch_constants.ndk_arches: 47 arches.append(_soong_arch_config_to_struct(arch_config)) 48 return arches 49 elif config.get("Aml_abis"): 50 for arch_config in _arch_constants.aml_arches: 51 arches.append(_soong_arch_config_to_struct(arch_config)) 52 return arches 53 54 arch = config.get("DeviceArch") 55 arch_variant = config.get("DeviceArchVariant") 56 cpu_variant = config.get("DeviceCpuVariant") 57 58 if _is_variant_default(arch, arch_variant): 59 arch_variant = "" 60 if _is_variant_default(arch, cpu_variant): 61 cpu_variant = "" 62 63 if not arch: 64 # TODO(b/258839711): determine how to better id whether a config is actually host only or we're just missing the target config 65 if "DeviceArch" in config: 66 fail("No architecture was specified in the product config, expected one of Ndk_abis, Aml_abis, or DeviceArch to be set:\n%s" % config) 67 else: 68 return arches 69 70 arches.append(struct( 71 arch = arch, 72 arch_variant = arch_variant, 73 cpu_variant = cpu_variant, 74 )) 75 76 arch = config.get("DeviceSecondaryArch") 77 arch_variant = config.get("DeviceSecondaryArchVariant") 78 cpu_variant = config.get("DeviceSecondaryCpuVariant") 79 80 if _is_variant_default(arch, arch_variant): 81 arch_variant = "" 82 if _is_variant_default(arch, cpu_variant): 83 cpu_variant = "" 84 85 if arch: 86 arches.append(struct( 87 arch = arch, 88 arch_variant = arch_variant, 89 cpu_variant = cpu_variant, 90 )) 91 return arches 92 93def _product_variable_constraint_settings(variables): 94 constraints = [] 95 96 local_vars = dict(variables) 97 98 # Native_coverage is not set within soong.variables, but is hardcoded 99 # within config.go NewConfig 100 local_vars["Native_coverage"] = ( 101 local_vars.get("ClangCoverage", False) or 102 local_vars.get("GcovCoverage", False) 103 ) 104 105 # Some attributes on rules are able to access the values of product 106 # variables via make-style expansion (like $(foo)). We collect the values 107 # of the relevant product variables here so that it can be passed to 108 # product_variables_providing_rule, which exports a 109 # platform_common.TemplateVariableInfo provider to allow the substitution. 110 attribute_vars = {} 111 112 def add_attribute_var(typ, var, value): 113 if typ == "bool": 114 attribute_vars[var] = "1" if value else "0" 115 elif typ == "list": 116 attribute_vars[var] = ",".join(value) 117 elif typ == "int": 118 attribute_vars[var] = str(value) 119 elif typ == "string": 120 attribute_vars[var] = value 121 122 # Generate constraints for Soong config variables (bool, value, string typed). 123 vendor_vars = local_vars.pop("VendorVars", default = {}) 124 for (namespace, variables) in vendor_vars.items(): 125 for (var, value) in variables.items(): 126 # All vendor vars are Starlark string-typed, even though they may be 127 # boxed bools/strings/arbitrary printf'd values, like numbers, so 128 # we'll need to do some translation work here by referring to 129 # soong_injection's generated data. 130 131 if value == "": 132 # Variable is not set so skip adding this as a constraint. 133 continue 134 135 # Create the identifier for the constraint var (or select key) 136 config_var = namespace + "__" + var 137 138 # List of all soong_config_module_type variables. 139 if not config_var in constants.SoongConfigVariables: 140 continue 141 142 # Normalize all constraint vars (i.e. select keys) to be lowercased. 143 constraint_var = config_var.lower() 144 145 if config_var in constants.SoongConfigBoolVariables: 146 constraints.append("@//build/bazel/product_variables:" + constraint_var) 147 elif config_var in constants.SoongConfigStringVariables: 148 # The string value is part of the the select key. 149 constraints.append("@//build/bazel/product_variables:" + constraint_var + "__" + value.lower()) 150 elif config_var in constants.SoongConfigValueVariables: 151 # For value variables, providing_vars add support for substituting 152 # the value using TemplateVariableInfo. 153 constraints.append("@//build/bazel/product_variables:" + constraint_var) 154 add_attribute_var("string", constraint_var, value) 155 156 for (var, value) in local_vars.items(): 157 # TODO(b/187323817): determine how to handle remaining product 158 # variables not used in product_variables 159 constraint_var = var.lower() 160 if not constants.ProductVariables.get(constraint_var): 161 continue 162 163 # variable.go excludes nil values 164 add_constraint = (value != None) 165 add_attribute_var(type(value), var, value) 166 if type(value) == "bool": 167 # variable.go special cases bools 168 add_constraint = value 169 170 if add_constraint: 171 constraints.append("@//build/bazel/product_variables:" + constraint_var) 172 173 return constraints, attribute_vars 174 175def _define_platform_for_arch(name, common_constraints, arch, secondary_arch = None): 176 if secondary_arch == None: 177 # When there is no secondary arch, we'll pretend it exists but is the same as the primary arch 178 secondary_arch = arch 179 native.platform( 180 name = name, 181 constraint_values = common_constraints + [ 182 "@//build/bazel/platforms/arch:" + arch.arch, 183 "@//build/bazel/platforms/arch:secondary_" + secondary_arch.arch, 184 "@//build/bazel/platforms/os:android", 185 ] + ["@" + v for v in variant_constraints( 186 arch, 187 _arch_constants.AndroidArchToVariantToFeatures[arch.arch], 188 )], 189 ) 190 191def _define_platform_for_arch_with_secondary(name, common_constraints, arch, secondary_arch = None): 192 if secondary_arch != None: 193 _define_platform_for_arch(name, common_constraints, arch, secondary_arch) 194 _define_platform_for_arch(name + "_secondary", common_constraints, secondary_arch) 195 else: 196 _define_platform_for_arch(name, common_constraints, arch) 197 native.alias( 198 name = name + "_secondary", 199 actual = ":" + name, 200 ) 201 202def _verify_product_is_registered(name): 203 """ 204 Verifies that this android_product() is listed in all_android_product_labels. 205 206 all_android_product_labels is used to build a select statement from each product to its 207 _product_vars rule. This is because we store most product configuration in a rule instead of 208 constraint settings or build settings due to limitations in bazel. (constraint settings can't 209 be unbounded, typed, or have dependencies, build settings can't be set with --platforms) 210 """ 211 my_label = native.repository_name() + "//" + native.package_name() + ":" + name 212 for label in all_android_product_labels: 213 if my_label == label: 214 return 215 fail("All android_product() instances must be listed in all_android_product_labels in " + 216 "//build/bazel/product_config/android_product.bzl. By default the products generated " + 217 "from legacy android product configurations are included, additional platforms (like " + 218 "testing-specific platforms) must be manually listed in " + 219 "//build/bazel/tests/products/product_labels.bzl.") 220 221def android_product(name, soong_variables): 222 """ 223 android_product integrates product variables into Bazel platforms. 224 225 This uses soong.variables to create constraints and platforms used by the 226 bazel build. The soong.variables file itself contains a post-processed list of 227 variables derived from Make variables, through soong_config.mk, generated 228 during the product config step. 229 230 Some constraints used here are handcrafted in 231 //build/bazel/platforms/{arch,os}. The rest are dynamically generated. 232 233 If you're looking for what --config=android, --config=linux_x86_64 or most 234 select statements in the BUILD files (ultimately) refer to, they're all 235 created here. 236 """ 237 _verify_product_is_registered(name) 238 239 product_var_constraints, attribute_vars = _product_variable_constraint_settings(soong_variables) 240 arch_configs = _determine_target_arches_from_config(soong_variables) 241 242 product_variables_providing_rule( 243 name = name + "_product_vars", 244 attribute_vars = attribute_vars, 245 product_vars = soong_variables, 246 ) 247 248 native.constraint_value( 249 name = name + "_constraint_value", 250 constraint_setting = "@//build/bazel/product_config:current_product", 251 ) 252 253 common_constraints = product_var_constraints + [name + "_constraint_value"] 254 255 # TODO(b/258802089): figure out how to deal with multiple arches for target 256 if len(arch_configs) > 0: 257 arch = arch_configs[0] 258 secondary_arch = None 259 if len(arch_configs) > 1: 260 secondary_arch = arch_configs[1] 261 262 _define_platform_for_arch_with_secondary(name, common_constraints, arch, secondary_arch) 263 264 # These variants are mostly for mixed builds, which may request a 265 # module with a certain arch 266 for arch, variants in arch_to_variants.items(): 267 for variant in variants: 268 native.platform( 269 name = name + "_android_" + arch + variant_name(variant), 270 constraint_values = common_constraints + [ 271 "@//build/bazel/platforms/arch:" + arch, 272 "@//build/bazel/platforms/arch:secondary_" + arch, 273 "@//build/bazel/platforms/os:android", 274 ] + ["@" + v for v in variant_constraints( 275 variant, 276 _arch_constants.AndroidArchToVariantToFeatures[arch], 277 )], 278 ) 279 280 arch_transitions = [ 281 struct( 282 name = "arm", 283 arch = struct( 284 arch = "arm", 285 arch_variant = "armv7-a-neon", 286 cpu_variant = "", 287 ), 288 secondary_arch = None, 289 ), 290 struct( 291 name = "arm64", 292 arch = struct( 293 arch = "arm64", 294 arch_variant = "armv8-a", 295 cpu_variant = "", 296 ), 297 secondary_arch = struct( 298 arch = "arm", 299 arch_variant = "armv7-a-neon", 300 cpu_variant = "", 301 ), 302 ), 303 struct( 304 name = "arm64only", 305 arch = struct( 306 arch = "arm64", 307 arch_variant = "armv8-a", 308 cpu_variant = "", 309 ), 310 secondary_arch = None, 311 ), 312 struct( 313 name = "x86", 314 arch = struct( 315 arch = "x86", 316 arch_variant = "", 317 cpu_variant = "", 318 ), 319 secondary_arch = None, 320 ), 321 struct( 322 name = "x86_64", 323 arch = struct( 324 arch = "x86_64", 325 arch_variant = "", 326 cpu_variant = "", 327 ), 328 secondary_arch = struct( 329 arch = "x86", 330 arch_variant = "", 331 cpu_variant = "", 332 ), 333 ), 334 struct( 335 name = "x86_64only", 336 arch = struct( 337 arch = "x86_64", 338 arch_variant = "", 339 cpu_variant = "", 340 ), 341 secondary_arch = None, 342 ), 343 ] 344 345 # TODO(b/249685973): Remove this, this is currently just for aabs 346 # to build each architecture 347 for arch in arch_transitions: 348 _define_platform_for_arch_with_secondary(name + "__internal_" + arch.name, common_constraints, arch.arch, arch.secondary_arch) 349 350 # Now define the host platforms. We need a host platform per product because 351 # the host platforms still use the product variables. 352 # TODO(b/262753134): Investigate making the host platforms product-independant 353 native.platform( 354 name = name + "_linux_x86", 355 constraint_values = common_constraints + [ 356 "@//build/bazel/platforms/arch:x86", 357 "@//build/bazel/platforms/os:linux", 358 ], 359 ) 360 361 native.platform( 362 name = name + "_linux_x86_64", 363 constraint_values = common_constraints + [ 364 "@//build/bazel/platforms/arch:x86_64", 365 "@//build/bazel/platforms/os:linux", 366 ], 367 ) 368 369 native.platform( 370 name = name + "_linux_musl_x86", 371 constraint_values = common_constraints + [ 372 "@//build/bazel/platforms/arch:x86", 373 "@//build/bazel/platforms/os:linux_musl", 374 ], 375 ) 376 377 native.platform( 378 name = name + "_linux_musl_x86_64", 379 constraint_values = common_constraints + [ 380 "@//build/bazel/platforms/arch:x86_64", 381 "@//build/bazel/platforms/os:linux_musl", 382 ], 383 ) 384 385 # linux_bionic is the OS for the Linux kernel plus the Bionic libc runtime, but 386 # without the rest of Android. 387 native.platform( 388 name = name + "_linux_bionic_arm64", 389 constraint_values = common_constraints + [ 390 "@//build/bazel/platforms/arch:arm64", 391 "@//build/bazel/platforms/os:linux_bionic", 392 ], 393 ) 394 395 native.platform( 396 name = name + "_linux_bionic_x86_64", 397 constraint_values = common_constraints + [ 398 "@//build/bazel/platforms/arch:x86_64", 399 "@//build/bazel/platforms/os:linux_bionic", 400 ], 401 ) 402 403 native.platform( 404 name = name + "_darwin_arm64", 405 constraint_values = common_constraints + [ 406 "@//build/bazel/platforms/arch:arm64", 407 "@//build/bazel/platforms/os:darwin", 408 ], 409 ) 410 411 native.platform( 412 name = name + "_darwin_x86_64", 413 constraint_values = common_constraints + [ 414 "@//build/bazel/platforms/arch:x86_64", 415 "@//build/bazel/platforms/os:darwin", 416 ], 417 ) 418 419 native.platform( 420 name = name + "_windows_x86", 421 constraint_values = common_constraints + [ 422 "@//build/bazel/platforms/arch:x86", 423 "@//build/bazel/platforms/os:windows", 424 ], 425 ) 426 427 native.platform( 428 name = name + "_windows_x86_64", 429 constraint_values = common_constraints + [ 430 "@//build/bazel/platforms/arch:x86_64", 431 "@//build/bazel/platforms/os:windows", 432 ], 433 ) 434