| """ |
| Copyright (C) 2022 The Android Open Source Project |
| |
| 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. |
| """ |
| |
| load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") |
| load("//build/bazel/rules:common.bzl", "get_dep_targets") |
| load("//build/bazel/rules/apex:cc.bzl", "CC_ATTR_ASPECTS") |
| load("//build/bazel/rules:prebuilt_file.bzl", "PrebuiltFileInfo") |
| load("//build/bazel/rules/cc:cc_stub_library.bzl", "CcStubLibrarySharedInfo") |
| load("//build/bazel/rules/cc:cc_library_static.bzl", "CcStaticLibraryInfo") |
| |
| ApexAvailableInfo = provider( |
| "ApexAvailableInfo collects APEX availability metadata.", |
| fields = { |
| "apex_available_names": "names of APEXs that this target is available to", |
| "platform_available": "whether this target is available for the platform", |
| "transitive_invalid_targets": "list of targets that had an invalid apex_available attribute", |
| "transitive_unvalidated_targets": "list of targets that were skipped in the apex_available_validation function", |
| }, |
| ) |
| |
| # Denylist of APEX names that are validated with apex_available. |
| # |
| # Certain apexes are not checked because their dependencies aren't converting |
| # apex_available to tags properly in the bp2build converters yet. See associated |
| # bugs for more information. |
| _unchecked_apexes = [ |
| # TODO(b/216741746, b/239093645): support aidl and hidl apex_available props. |
| "com.android.neuralnetworks", |
| "com.android.media.swcodec", |
| ] |
| |
| # Validates if a target is made available as a transitive dependency of an APEX. The return |
| # value is tri-state: True, False, string. Strings are used when a target is _not checked_ |
| # and the string itself contains the reason. |
| def _validate_apex_available(target, ctx, *, apex_available_tags, apex_name, base_apex_name): |
| # testonly apexes aren't checked. |
| if ctx.attr.testonly: |
| return "testonly" |
| |
| # Macro-internal manual targets aren't checked. |
| if "manual" in ctx.rule.attr.tags and "apex_available_checked_manual_for_testing" not in ctx.rule.attr.tags: |
| return "manual" |
| |
| # prebuilt_file targets don't specify apex_available, and aren't checked. |
| if PrebuiltFileInfo in target: |
| return "prebuilt" |
| |
| # stubs are APIs, and don't specify apex_available, and aren't checked. |
| if CcStubLibrarySharedInfo in target: |
| return "stubs" |
| |
| if "//apex_available:anyapex" in apex_available_tags: |
| return "//apex_available:anyapex" |
| |
| if apex_name in _unchecked_apexes: |
| # Skipped unchecked APEXes. |
| return "unchecked_apex" |
| |
| # https://cs.android.com/android/platform/superproject/+/master:build/soong/apex/apex.go;l=2910;drc=862c0d68fff500d7fe59bc2fcfc9c7d75596e5b5 |
| # Bp2build-generated cc_library_static target from stubs-providing lib |
| # doesn't have apex_available tag. |
| # If its shared variant is directly in the apex, skip validation |
| # Otherwise, it will be invalidated. |
| direct_deps = ctx.attr._direct_deps[BuildSettingInfo].value |
| if CcStaticLibraryInfo in target and str(target.label).removesuffix("_bp2build_cc_library_static") in direct_deps: |
| return "has shared variant directly included" |
| |
| elif base_apex_name not in apex_available_tags and apex_name not in apex_available_tags: |
| return False |
| |
| # All good! |
| return True |
| |
| _IGNORED_ATTRS = [ |
| "certificate", |
| "key", |
| "android_manifest", |
| "applicable_licenses", |
| "androidmk_static_deps", |
| "androidmk_whole_archive_deps", |
| "androidmk_dynamic_deps", |
| "androidmk_deps", |
| ] |
| |
| def _apex_available_aspect_impl(target, ctx): |
| apex_available_tags = [ |
| t.removeprefix("apex_available=") |
| for t in ctx.rule.attr.tags |
| if t.startswith("apex_available=") |
| ] |
| platform_available = ( |
| "//apex_available:platform" in apex_available_tags or |
| len(apex_available_tags) == 0 |
| ) |
| apex_name = ctx.attr._apex_name[BuildSettingInfo].value |
| |
| dep_targets = get_dep_targets( |
| ctx.rule.attr, |
| predicate = lambda target: ApexAvailableInfo in target, |
| ) |
| transitive_unvalidated_targets = [] |
| transitive_invalid_targets = [] |
| for attr, attr_targets in dep_targets.items(): |
| for t in attr_targets: |
| info = t[ApexAvailableInfo] |
| transitive_unvalidated_targets.append(info.transitive_unvalidated_targets) |
| if attr in CC_ATTR_ASPECTS: |
| transitive_invalid_targets.append(info.transitive_invalid_targets) |
| if attr not in _IGNORED_ATTRS: |
| if info.platform_available != None: |
| platform_available = platform_available and info.platform_available |
| |
| if "manual" in ctx.rule.attr.tags and "apex_available_checked_manual_for_testing" not in ctx.rule.attr.tags: |
| platform_available = None |
| |
| if CcStubLibrarySharedInfo in target: |
| # stub libraries libraries are always available to platform |
| # https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/cc.go;l=3670;drc=89ff729d1d65fb0ce2945ec6b8c4777a9d78dcab |
| platform_available = True |
| |
| skipped_reason = _validate_apex_available( |
| target, |
| ctx, |
| apex_available_tags = apex_available_tags, |
| apex_name = apex_name, |
| base_apex_name = ctx.attr._base_apex_name[BuildSettingInfo].value, |
| ) |
| |
| return [ |
| ApexAvailableInfo( |
| platform_available = platform_available, |
| apex_available_names = apex_available_tags, |
| transitive_unvalidated_targets = depset( |
| direct = [(ctx.label, skipped_reason)] if type(skipped_reason) == type("") else None, |
| transitive = transitive_unvalidated_targets, |
| ), |
| transitive_invalid_targets = depset( |
| direct = [(target, tuple(apex_available_tags))] if skipped_reason == False else None, |
| transitive = transitive_invalid_targets, |
| ), |
| ), |
| ] |
| |
| apex_available_aspect = aspect( |
| implementation = _apex_available_aspect_impl, |
| provides = [ApexAvailableInfo], |
| attr_aspects = ["*"], |
| attrs = { |
| "_apex_name": attr.label(default = "//build/bazel/rules/apex:apex_name"), |
| "_base_apex_name": attr.label(default = "//build/bazel/rules/apex:base_apex_name"), |
| "_direct_deps": attr.label(default = "//build/bazel/rules/apex:apex_direct_deps"), |
| "testonly": attr.bool(default = False), # propagated from the apex |
| }, |
| ) |