xref: /aosp_15_r20/build/bazel/rules/apex/apex_deps_validation.bzl (revision 7594170e27e0732bc44b93d1440d87a54b6ffe7c)
1*7594170eSAndroid Build Coastguard Worker# Copyright (C) 2022 The Android Open Source Project
2*7594170eSAndroid Build Coastguard Worker#
3*7594170eSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
4*7594170eSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
5*7594170eSAndroid Build Coastguard Worker# You may obtain a copy of the License at
6*7594170eSAndroid Build Coastguard Worker#
7*7594170eSAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
8*7594170eSAndroid Build Coastguard Worker#
9*7594170eSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
10*7594170eSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
11*7594170eSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*7594170eSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
13*7594170eSAndroid Build Coastguard Worker# limitations under the License.
14*7594170eSAndroid Build Coastguard Worker
15*7594170eSAndroid Build Coastguard Workerload("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
16*7594170eSAndroid Build Coastguard Workerload("//build/bazel/rules:common.bzl", "get_dep_targets", "strip_bp2build_label_suffix")
17*7594170eSAndroid Build Coastguard Workerload("//build/bazel/rules/android:android_app_certificate.bzl", "AndroidAppCertificateInfo")
18*7594170eSAndroid Build Coastguard Workerload(":apex_available.bzl", "ApexAvailableInfo")
19*7594170eSAndroid Build Coastguard Workerload(":apex_info.bzl", "ApexInfo")
20*7594170eSAndroid Build Coastguard Workerload(":apex_key.bzl", "ApexKeyInfo")
21*7594170eSAndroid Build Coastguard Workerload(":cc.bzl", "get_min_sdk_version")
22*7594170eSAndroid Build Coastguard Worker
23*7594170eSAndroid Build Coastguard WorkerApexDepsInfo = provider(
24*7594170eSAndroid Build Coastguard Worker    "ApexDepsInfo collects transitive deps for dependency validation.",
25*7594170eSAndroid Build Coastguard Worker    fields = {
26*7594170eSAndroid Build Coastguard Worker        "transitive_deps": "Labels of targets that are depended on by this APEX.",
27*7594170eSAndroid Build Coastguard Worker    },
28*7594170eSAndroid Build Coastguard Worker)
29*7594170eSAndroid Build Coastguard Worker
30*7594170eSAndroid Build Coastguard WorkerApexDepInfo = provider(
31*7594170eSAndroid Build Coastguard Worker    "ApexDepInfo collects metadata about dependencies of APEXs.",
32*7594170eSAndroid Build Coastguard Worker    fields = {
33*7594170eSAndroid Build Coastguard Worker        "is_external": "True if this target is an external dep to the APEX.",
34*7594170eSAndroid Build Coastguard Worker        "label": "Label of target",
35*7594170eSAndroid Build Coastguard Worker        "min_sdk_version": "min_sdk_version of target",
36*7594170eSAndroid Build Coastguard Worker    },
37*7594170eSAndroid Build Coastguard Worker)
38*7594170eSAndroid Build Coastguard Worker
39*7594170eSAndroid Build Coastguard Worker_IGNORED_PACKAGES = [
40*7594170eSAndroid Build Coastguard Worker    "build/bazel/platforms",
41*7594170eSAndroid Build Coastguard Worker]
42*7594170eSAndroid Build Coastguard Worker_IGNORED_REPOSITORIES = [
43*7594170eSAndroid Build Coastguard Worker    "bazel_tools",
44*7594170eSAndroid Build Coastguard Worker]
45*7594170eSAndroid Build Coastguard Worker_IGNORED_RULE_KINDS = [
46*7594170eSAndroid Build Coastguard Worker    # No validation for language-agnostic targets.  In general language
47*7594170eSAndroid Build Coastguard Worker    # agnostic rules to support AIDL, HIDL, Sysprop do not have an analogous
48*7594170eSAndroid Build Coastguard Worker    # module type in Soong and do not have an apex_available property, often
49*7594170eSAndroid Build Coastguard Worker    # relying on language-specific apex_available properties.  Because a
50*7594170eSAndroid Build Coastguard Worker    # language-specific rule is required for a language-agnostic rule to be
51*7594170eSAndroid Build Coastguard Worker    # within the transitive deps of an apex and impact the apex contents, this
52*7594170eSAndroid Build Coastguard Worker    # is safe.
53*7594170eSAndroid Build Coastguard Worker    "aidl_library",
54*7594170eSAndroid Build Coastguard Worker    "hidl_library",
55*7594170eSAndroid Build Coastguard Worker    "sysprop_library",
56*7594170eSAndroid Build Coastguard Worker
57*7594170eSAndroid Build Coastguard Worker    # Build settings, these have no built artifact and thus will not be
58*7594170eSAndroid Build Coastguard Worker    # included in an apex.
59*7594170eSAndroid Build Coastguard Worker    "string_list_setting",
60*7594170eSAndroid Build Coastguard Worker    "string_setting",
61*7594170eSAndroid Build Coastguard Worker
62*7594170eSAndroid Build Coastguard Worker    # These rule kinds cannot be skipped by checking providers because most
63*7594170eSAndroid Build Coastguard Worker    # targets have a License provider
64*7594170eSAndroid Build Coastguard Worker    "_license",
65*7594170eSAndroid Build Coastguard Worker    "_license_kind",
66*7594170eSAndroid Build Coastguard Worker]
67*7594170eSAndroid Build Coastguard Worker_IGNORED_PROVIDERS = [
68*7594170eSAndroid Build Coastguard Worker    AndroidAppCertificateInfo,
69*7594170eSAndroid Build Coastguard Worker    ApexKeyInfo,
70*7594170eSAndroid Build Coastguard Worker    ProtoInfo,
71*7594170eSAndroid Build Coastguard Worker]
72*7594170eSAndroid Build Coastguard Worker_IGNORED_ATTRS = [
73*7594170eSAndroid Build Coastguard Worker    "androidmk_static_deps",
74*7594170eSAndroid Build Coastguard Worker    "androidmk_whole_archive_deps",
75*7594170eSAndroid Build Coastguard Worker    "androidmk_dynamic_deps",
76*7594170eSAndroid Build Coastguard Worker    "androidmk_deps",
77*7594170eSAndroid Build Coastguard Worker]
78*7594170eSAndroid Build Coastguard Worker_IGNORED_TARGETS = [
79*7594170eSAndroid Build Coastguard Worker    "default_metadata_file",
80*7594170eSAndroid Build Coastguard Worker]
81*7594170eSAndroid Build Coastguard Worker
82*7594170eSAndroid Build Coastguard Workerdef _should_skip_apex_dep(target, ctx):
83*7594170eSAndroid Build Coastguard Worker    # Ignore Bazel-specific targets like platform/os/arch constraints,
84*7594170eSAndroid Build Coastguard Worker    # anything from @bazel_tools, and rule types that we dont care about
85*7594170eSAndroid Build Coastguard Worker    # for dependency validation like licenses, certificates, etc.
86*7594170eSAndroid Build Coastguard Worker    #TODO(b/261715581) update allowed_deps.txt to include Bazel-specific targets
87*7594170eSAndroid Build Coastguard Worker    return (
88*7594170eSAndroid Build Coastguard Worker        ctx.label.workspace_name in _IGNORED_REPOSITORIES or
89*7594170eSAndroid Build Coastguard Worker        ctx.label.package in _IGNORED_PACKAGES or
90*7594170eSAndroid Build Coastguard Worker        ctx.rule.kind in _IGNORED_RULE_KINDS or
91*7594170eSAndroid Build Coastguard Worker        True in [p in target for p in _IGNORED_PROVIDERS] or
92*7594170eSAndroid Build Coastguard Worker        target.label.name in _IGNORED_TARGETS
93*7594170eSAndroid Build Coastguard Worker    )
94*7594170eSAndroid Build Coastguard Worker
95*7594170eSAndroid Build Coastguard Workerdef _apex_dep_validation_aspect_impl(target, ctx):
96*7594170eSAndroid Build Coastguard Worker    transitive_deps = []
97*7594170eSAndroid Build Coastguard Worker    for attr, attr_deps in get_dep_targets(ctx.rule.attr, predicate = lambda target: ApexDepsInfo in target).items():
98*7594170eSAndroid Build Coastguard Worker        if attr in _IGNORED_ATTRS:
99*7594170eSAndroid Build Coastguard Worker            continue
100*7594170eSAndroid Build Coastguard Worker        for dep in attr_deps:
101*7594170eSAndroid Build Coastguard Worker            transitive_deps.append(dep[ApexDepsInfo].transitive_deps)
102*7594170eSAndroid Build Coastguard Worker
103*7594170eSAndroid Build Coastguard Worker    if _should_skip_apex_dep(target, ctx):
104*7594170eSAndroid Build Coastguard Worker        return ApexDepsInfo(
105*7594170eSAndroid Build Coastguard Worker            transitive_deps = depset(
106*7594170eSAndroid Build Coastguard Worker                transitive = transitive_deps,
107*7594170eSAndroid Build Coastguard Worker            ),
108*7594170eSAndroid Build Coastguard Worker        )
109*7594170eSAndroid Build Coastguard Worker
110*7594170eSAndroid Build Coastguard Worker    is_external = False
111*7594170eSAndroid Build Coastguard Worker    include_self_in_transitive_deps = True
112*7594170eSAndroid Build Coastguard Worker
113*7594170eSAndroid Build Coastguard Worker    if "manual" in ctx.rule.attr.tags and "apex_available_checked_manual_for_testing" not in ctx.rule.attr.tags:
114*7594170eSAndroid Build Coastguard Worker        include_self_in_transitive_deps = False
115*7594170eSAndroid Build Coastguard Worker    else:
116*7594170eSAndroid Build Coastguard Worker        apex_available_names = target[ApexAvailableInfo].apex_available_names
117*7594170eSAndroid Build Coastguard Worker        apex_name = ctx.attr._apex_name[BuildSettingInfo].value
118*7594170eSAndroid Build Coastguard Worker        base_apex_name = ctx.attr._base_apex_name[BuildSettingInfo].value
119*7594170eSAndroid Build Coastguard Worker        if not (
120*7594170eSAndroid Build Coastguard Worker            "//apex_available:anyapex" in apex_available_names or
121*7594170eSAndroid Build Coastguard Worker            base_apex_name in apex_available_names or
122*7594170eSAndroid Build Coastguard Worker            apex_name in apex_available_names
123*7594170eSAndroid Build Coastguard Worker        ):
124*7594170eSAndroid Build Coastguard Worker            # APEX deps validation stops when the dependency graph crosses the APEX boundary
125*7594170eSAndroid Build Coastguard Worker            # Record that this is a boundary target, so that we exclude can it later from validation
126*7594170eSAndroid Build Coastguard Worker            is_external = True
127*7594170eSAndroid Build Coastguard Worker            transitive_deps = []
128*7594170eSAndroid Build Coastguard Worker
129*7594170eSAndroid Build Coastguard Worker        if not target[ApexAvailableInfo].platform_available:
130*7594170eSAndroid Build Coastguard Worker            # Skip dependencies that are only available to APEXes; they are
131*7594170eSAndroid Build Coastguard Worker            # developed with updatability in mind and don't need manual approval.
132*7594170eSAndroid Build Coastguard Worker            include_self_in_transitive_deps = False
133*7594170eSAndroid Build Coastguard Worker
134*7594170eSAndroid Build Coastguard Worker    if ApexInfo in target:
135*7594170eSAndroid Build Coastguard Worker        include_self_in_transitive_deps = False
136*7594170eSAndroid Build Coastguard Worker
137*7594170eSAndroid Build Coastguard Worker    direct_deps = []
138*7594170eSAndroid Build Coastguard Worker    if include_self_in_transitive_deps:
139*7594170eSAndroid Build Coastguard Worker        direct_deps = [
140*7594170eSAndroid Build Coastguard Worker            ApexDepInfo(
141*7594170eSAndroid Build Coastguard Worker                label = ctx.label,
142*7594170eSAndroid Build Coastguard Worker                is_external = is_external,
143*7594170eSAndroid Build Coastguard Worker                min_sdk_version = get_min_sdk_version(ctx),
144*7594170eSAndroid Build Coastguard Worker            ),
145*7594170eSAndroid Build Coastguard Worker        ]
146*7594170eSAndroid Build Coastguard Worker
147*7594170eSAndroid Build Coastguard Worker    return ApexDepsInfo(
148*7594170eSAndroid Build Coastguard Worker        transitive_deps = depset(
149*7594170eSAndroid Build Coastguard Worker            direct = direct_deps,
150*7594170eSAndroid Build Coastguard Worker            transitive = transitive_deps,
151*7594170eSAndroid Build Coastguard Worker        ),
152*7594170eSAndroid Build Coastguard Worker    )
153*7594170eSAndroid Build Coastguard Worker
154*7594170eSAndroid Build Coastguard Workerapex_deps_validation_aspect = aspect(
155*7594170eSAndroid Build Coastguard Worker    doc = "apex_deps_validation_aspect walks the deps of an APEX and records" +
156*7594170eSAndroid Build Coastguard Worker          " its transitive dependencies so that they can be validated against" +
157*7594170eSAndroid Build Coastguard Worker          " allowed_deps.txt.",
158*7594170eSAndroid Build Coastguard Worker    implementation = _apex_dep_validation_aspect_impl,
159*7594170eSAndroid Build Coastguard Worker    attr_aspects = ["*"],
160*7594170eSAndroid Build Coastguard Worker    apply_to_generating_rules = True,
161*7594170eSAndroid Build Coastguard Worker    attrs = {
162*7594170eSAndroid Build Coastguard Worker        "_apex_name": attr.label(default = "//build/bazel/rules/apex:apex_name"),
163*7594170eSAndroid Build Coastguard Worker        "_base_apex_name": attr.label(default = "//build/bazel/rules/apex:base_apex_name"),
164*7594170eSAndroid Build Coastguard Worker        "_direct_deps": attr.label(default = "//build/bazel/rules/apex:apex_direct_deps"),
165*7594170eSAndroid Build Coastguard Worker    },
166*7594170eSAndroid Build Coastguard Worker    required_aspect_providers = [ApexAvailableInfo],
167*7594170eSAndroid Build Coastguard Worker    provides = [ApexDepsInfo],
168*7594170eSAndroid Build Coastguard Worker)
169*7594170eSAndroid Build Coastguard Worker
170*7594170eSAndroid Build Coastguard Workerdef _min_sdk_version_string(version):
171*7594170eSAndroid Build Coastguard Worker    if version.apex_inherit:
172*7594170eSAndroid Build Coastguard Worker        return "apex_inherit"
173*7594170eSAndroid Build Coastguard Worker    elif version.min_sdk_version == None:
174*7594170eSAndroid Build Coastguard Worker        return "(no version)"
175*7594170eSAndroid Build Coastguard Worker    return version.min_sdk_version
176*7594170eSAndroid Build Coastguard Worker
177*7594170eSAndroid Build Coastguard Workerdef _apex_dep_to_string(apex_dep_info):
178*7594170eSAndroid Build Coastguard Worker    return "{name}(minSdkVersion:{min_sdk_version})".format(
179*7594170eSAndroid Build Coastguard Worker        name = strip_bp2build_label_suffix(apex_dep_info.label.name),
180*7594170eSAndroid Build Coastguard Worker        min_sdk_version = _min_sdk_version_string(apex_dep_info.min_sdk_version),
181*7594170eSAndroid Build Coastguard Worker    )
182*7594170eSAndroid Build Coastguard Worker
183*7594170eSAndroid Build Coastguard Workerdef apex_dep_infos_to_allowlist_strings(apex_dep_infos):
184*7594170eSAndroid Build Coastguard Worker    """apex_dep_infos_to_allowlist_strings converts outputs a string that can be compared against allowed_deps.txt
185*7594170eSAndroid Build Coastguard Worker
186*7594170eSAndroid Build Coastguard Worker    Args:
187*7594170eSAndroid Build Coastguard Worker        apex_dep_infos (list[ApexDepInfo]): list of deps to convert
188*7594170eSAndroid Build Coastguard Worker    Returns:
189*7594170eSAndroid Build Coastguard Worker        a list of strings conforming to the format of allowed_deps.txt
190*7594170eSAndroid Build Coastguard Worker    """
191*7594170eSAndroid Build Coastguard Worker    return [
192*7594170eSAndroid Build Coastguard Worker        _apex_dep_to_string(d)
193*7594170eSAndroid Build Coastguard Worker        for d in apex_dep_infos
194*7594170eSAndroid Build Coastguard Worker        if not d.is_external
195*7594170eSAndroid Build Coastguard Worker    ]
196*7594170eSAndroid Build Coastguard Worker
197*7594170eSAndroid Build Coastguard Workerdef validate_apex_deps(ctx, transitive_deps, allowed_deps_manifest):
198*7594170eSAndroid Build Coastguard Worker    """validate_apex_deps generates actions to validate that all deps in transitive_deps exist in the allowed_deps file
199*7594170eSAndroid Build Coastguard Worker
200*7594170eSAndroid Build Coastguard Worker    Args:
201*7594170eSAndroid Build Coastguard Worker        ctx (rule context): a rule context
202*7594170eSAndroid Build Coastguard Worker        transitive_deps (depset[ApexDepsInfo]): list of transitive dependencies
203*7594170eSAndroid Build Coastguard Worker            of an APEX. This is most likely generated by collecting the output
204*7594170eSAndroid Build Coastguard Worker            of apex_deps_validation_aspect
205*7594170eSAndroid Build Coastguard Worker        allowed_deps_manifest (File): a file containing an allowlist of modules
206*7594170eSAndroid Build Coastguard Worker            that can be included in an APEX. This is expected to be in the format
207*7594170eSAndroid Build Coastguard Worker            of //packages/modules/common/build/allowed_deps.txt
208*7594170eSAndroid Build Coastguard Worker    Returns:
209*7594170eSAndroid Build Coastguard Worker        validation_marker (File): an empty file created if validation succeeds
210*7594170eSAndroid Build Coastguard Worker    """
211*7594170eSAndroid Build Coastguard Worker    apex_deps_file = ctx.actions.declare_file(ctx.label.name + ".current_deps")
212*7594170eSAndroid Build Coastguard Worker    ctx.actions.write(
213*7594170eSAndroid Build Coastguard Worker        apex_deps_file,
214*7594170eSAndroid Build Coastguard Worker        "\n".join(apex_dep_infos_to_allowlist_strings(transitive_deps.to_list())),
215*7594170eSAndroid Build Coastguard Worker    )
216*7594170eSAndroid Build Coastguard Worker    validation_marker = ctx.actions.declare_file(ctx.label.name + ".allowed_deps")
217*7594170eSAndroid Build Coastguard Worker    shell_command = """
218*7594170eSAndroid Build Coastguard Worker        export module_diff=$(
219*7594170eSAndroid Build Coastguard Worker            cat {allowed_deps_manifest} |
220*7594170eSAndroid Build Coastguard Worker            sed -e 's/^prebuilt_//g' |
221*7594170eSAndroid Build Coastguard Worker            sort |
222*7594170eSAndroid Build Coastguard Worker            comm -23 <(sort -u {apex_deps_file}) -
223*7594170eSAndroid Build Coastguard Worker        );
224*7594170eSAndroid Build Coastguard Worker        export diff_size=$(echo "$module_diff" | wc -w);
225*7594170eSAndroid Build Coastguard Worker        if [[ $diff_size -eq 0 ]]; then
226*7594170eSAndroid Build Coastguard Worker            touch {validation_marker};
227*7594170eSAndroid Build Coastguard Worker        else
228*7594170eSAndroid Build Coastguard Worker            echo -e "\n******************************";
229*7594170eSAndroid Build Coastguard Worker            echo "ERROR: go/apex-allowed-deps-error contains more information";
230*7594170eSAndroid Build Coastguard Worker            echo "******************************";
231*7594170eSAndroid Build Coastguard Worker            echo "Detected changes to allowed dependencies in updatable modules.";
232*7594170eSAndroid Build Coastguard Worker            echo "There are $diff_size dependencies of APEX {target_label} on modules not in {allowed_deps_manifest}:";
233*7594170eSAndroid Build Coastguard Worker            echo "$module_diff";
234*7594170eSAndroid Build Coastguard Worker            echo "To fix and update packages/modules/common/build/allowed_deps.txt, please run:";
235*7594170eSAndroid Build Coastguard Worker            echo -e "$ (croot && packages/modules/common/build/update-apex-allowed-deps.sh)\n";
236*7594170eSAndroid Build Coastguard Worker            echo "When submitting the generated CL, you must include the following information";
237*7594170eSAndroid Build Coastguard Worker            echo "in the commit message if you are adding a new dependency:";
238*7594170eSAndroid Build Coastguard Worker            echo "Apex-Size-Increase: Expected binary size increase for affected APEXes (or the size of the .jar / .so file of the new library)";
239*7594170eSAndroid Build Coastguard Worker            echo "Previous-Platform-Support: Are the maintainers of the new dependency committed to supporting previous platform releases?";
240*7594170eSAndroid Build Coastguard Worker            echo "Aosp-First: Is the new dependency being developed AOSP-first or internal?";
241*7594170eSAndroid Build Coastguard Worker            echo "Test-Info: What’s the testing strategy for the new dependency? Does it have its own tests, and are you adding integration tests? How/when are the tests run?";
242*7594170eSAndroid Build Coastguard Worker            echo "You do not need OWNERS approval to submit the change, but mainline-modularization@";
243*7594170eSAndroid Build Coastguard Worker            echo "will periodically review additions and may require changes.";
244*7594170eSAndroid Build Coastguard Worker            echo -e "******************************\n";
245*7594170eSAndroid Build Coastguard Worker            exit 1;
246*7594170eSAndroid Build Coastguard Worker        fi;
247*7594170eSAndroid Build Coastguard Worker    """.format(
248*7594170eSAndroid Build Coastguard Worker        allowed_deps_manifest = allowed_deps_manifest.path,
249*7594170eSAndroid Build Coastguard Worker        apex_deps_file = apex_deps_file.path,
250*7594170eSAndroid Build Coastguard Worker        validation_marker = validation_marker.path,
251*7594170eSAndroid Build Coastguard Worker        target_label = ctx.label,
252*7594170eSAndroid Build Coastguard Worker    )
253*7594170eSAndroid Build Coastguard Worker    ctx.actions.run_shell(
254*7594170eSAndroid Build Coastguard Worker        inputs = [allowed_deps_manifest, apex_deps_file],
255*7594170eSAndroid Build Coastguard Worker        outputs = [validation_marker],
256*7594170eSAndroid Build Coastguard Worker        command = shell_command,
257*7594170eSAndroid Build Coastguard Worker        mnemonic = "ApexDepValidation",
258*7594170eSAndroid Build Coastguard Worker        progress_message = "Validating APEX dependencies",
259*7594170eSAndroid Build Coastguard Worker    )
260*7594170eSAndroid Build Coastguard Worker
261*7594170eSAndroid Build Coastguard Worker    return validation_marker
262