# 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("@soong_injection//apex_toolchain:constants.bzl", "apex_available_baseline") load("//build/bazel/rules:common.bzl", "get_dep_targets", "strip_bp2build_label_suffix") load("//build/bazel/rules:prebuilt_file.bzl", "PrebuiltFileInfo") load("//build/bazel/rules/apex:cc.bzl", "CC_ATTR_ASPECTS") load("//build/bazel/rules/cc:cc_library_static.bzl", "CcStaticLibraryInfo") load("//build/bazel/rules/cc:cc_stub_library.bzl", "CcStubLibrarySharedInfo") 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", }, ) # 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" # 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" if base_apex_name in apex_available_tags or apex_name in apex_available_tags: return True target_name = strip_bp2build_label_suffix(target.label.name) baselines = [ apex_available_baseline.get(base_apex_name, []), apex_available_baseline.get(apex_name, []), apex_available_baseline.get("//apex_available:anyapex", []), ] if any([target_name in b for b in baselines]): return True return False _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 = { "testonly": attr.bool(default = False), # propagated from the apex "_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"), }, )