• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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