xref: /aosp_15_r20/build/bazel/rules/apex/apex_available.bzl (revision 7594170e27e0732bc44b93d1440d87a54b6ffe7c)
1# Copyright (C) 2022 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("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
16load("@soong_injection//apex_toolchain:constants.bzl", "apex_available_baseline")
17load("//build/bazel/rules:common.bzl", "get_dep_targets", "strip_bp2build_label_suffix")
18load("//build/bazel/rules:prebuilt_file.bzl", "PrebuiltFileInfo")
19load("//build/bazel/rules/apex:cc.bzl", "CC_ATTR_ASPECTS")
20load("//build/bazel/rules/cc:cc_library_static.bzl", "CcStaticLibraryInfo")
21load("//build/bazel/rules/cc:cc_stub_library.bzl", "CcStubLibrarySharedInfo")
22
23ApexAvailableInfo = provider(
24    "ApexAvailableInfo collects APEX availability metadata.",
25    fields = {
26        "apex_available_names": "names of APEXs that this target is available to",
27        "platform_available": "whether this target is available for the platform",
28        "transitive_invalid_targets": "list of targets that had an invalid apex_available attribute",
29        "transitive_unvalidated_targets": "list of targets that were skipped in the apex_available_validation function",
30    },
31)
32
33# Validates if a target is made available as a transitive dependency of an APEX. The return
34# value is tri-state: True, False, string. Strings are used when a target is _not checked_
35# and the string itself contains the reason.
36def _validate_apex_available(target, ctx, *, apex_available_tags, apex_name, base_apex_name):
37    # testonly apexes aren't checked.
38    if ctx.attr.testonly:
39        return "testonly"
40
41    # Macro-internal manual targets aren't checked.
42    if "manual" in ctx.rule.attr.tags and "apex_available_checked_manual_for_testing" not in ctx.rule.attr.tags:
43        return "manual"
44
45    # prebuilt_file targets don't specify apex_available, and aren't checked.
46    if PrebuiltFileInfo in target:
47        return "prebuilt"
48
49    # stubs are APIs, and don't specify apex_available, and aren't checked.
50    if CcStubLibrarySharedInfo in target:
51        return "stubs"
52
53    if "//apex_available:anyapex" in apex_available_tags:
54        return "//apex_available:anyapex"
55
56    # https://cs.android.com/android/platform/superproject/+/master:build/soong/apex/apex.go;l=2910;drc=862c0d68fff500d7fe59bc2fcfc9c7d75596e5b5
57    # Bp2build-generated cc_library_static target from stubs-providing lib
58    # doesn't have apex_available tag.
59    # If its shared variant is directly in the apex, skip validation
60    # Otherwise, it will be invalidated.
61    direct_deps = ctx.attr._direct_deps[BuildSettingInfo].value
62    if CcStaticLibraryInfo in target and str(target.label).removesuffix("_bp2build_cc_library_static") in direct_deps:
63        return "has shared variant directly included"
64
65    if base_apex_name in apex_available_tags or apex_name in apex_available_tags:
66        return True
67
68    target_name = strip_bp2build_label_suffix(target.label.name)
69    baselines = [
70        apex_available_baseline.get(base_apex_name, []),
71        apex_available_baseline.get(apex_name, []),
72        apex_available_baseline.get("//apex_available:anyapex", []),
73    ]
74    if any([target_name in b for b in baselines]):
75        return True
76
77    return False
78
79_IGNORED_ATTRS = [
80    "certificate",
81    "key",
82    "android_manifest",
83    "applicable_licenses",
84    "androidmk_static_deps",
85    "androidmk_whole_archive_deps",
86    "androidmk_dynamic_deps",
87    "androidmk_deps",
88]
89
90def _apex_available_aspect_impl(target, ctx):
91    apex_available_tags = [
92        t.removeprefix("apex_available=")
93        for t in ctx.rule.attr.tags
94        if t.startswith("apex_available=")
95    ]
96    platform_available = (
97        "//apex_available:platform" in apex_available_tags or
98        len(apex_available_tags) == 0
99    )
100    apex_name = ctx.attr._apex_name[BuildSettingInfo].value
101
102    dep_targets = get_dep_targets(
103        ctx.rule.attr,
104        predicate = lambda target: ApexAvailableInfo in target,
105    )
106    transitive_unvalidated_targets = []
107    transitive_invalid_targets = []
108    for attr, attr_targets in dep_targets.items():
109        for t in attr_targets:
110            info = t[ApexAvailableInfo]
111            transitive_unvalidated_targets.append(info.transitive_unvalidated_targets)
112            if attr in CC_ATTR_ASPECTS:
113                transitive_invalid_targets.append(info.transitive_invalid_targets)
114            if attr not in _IGNORED_ATTRS:
115                if info.platform_available != None:
116                    platform_available = platform_available and info.platform_available
117
118    if "manual" in ctx.rule.attr.tags and "apex_available_checked_manual_for_testing" not in ctx.rule.attr.tags:
119        platform_available = None
120
121    if CcStubLibrarySharedInfo in target:
122        # stub libraries libraries are always available to platform
123        # https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/cc.go;l=3670;drc=89ff729d1d65fb0ce2945ec6b8c4777a9d78dcab
124        platform_available = True
125
126    skipped_reason = _validate_apex_available(
127        target,
128        ctx,
129        apex_available_tags = apex_available_tags,
130        apex_name = apex_name,
131        base_apex_name = ctx.attr._base_apex_name[BuildSettingInfo].value,
132    )
133
134    return [
135        ApexAvailableInfo(
136            platform_available = platform_available,
137            apex_available_names = apex_available_tags,
138            transitive_unvalidated_targets = depset(
139                direct = [(ctx.label, skipped_reason)] if type(skipped_reason) == type("") else None,
140                transitive = transitive_unvalidated_targets,
141            ),
142            transitive_invalid_targets = depset(
143                direct = [(target, tuple(apex_available_tags))] if skipped_reason == False else None,
144                transitive = transitive_invalid_targets,
145            ),
146        ),
147    ]
148
149apex_available_aspect = aspect(
150    implementation = _apex_available_aspect_impl,
151    provides = [ApexAvailableInfo],
152    apply_to_generating_rules = True,
153    attr_aspects = ["*"],
154    attrs = {
155        "testonly": attr.bool(default = False),  # propagated from the apex
156        "_apex_name": attr.label(default = "//build/bazel/rules/apex:apex_name"),
157        "_base_apex_name": attr.label(default = "//build/bazel/rules/apex:base_apex_name"),
158        "_direct_deps": attr.label(default = "//build/bazel/rules/apex:apex_direct_deps"),
159    },
160)
161