1# Copyright (C) 2021 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("//build/bazel/rules:metadata.bzl", "MetadataFileInfo") 17load("//build/bazel/rules/cc:cc_library_common.bzl", "parse_apex_sdk_version") 18load("//build/bazel/rules/cc:cc_library_shared.bzl", "CcSharedLibraryOutputInfo", "CcStubLibrariesInfo") 19load("//build/bazel/rules/cc:cc_stub_library.bzl", "CcStubLibrarySharedInfo") 20load("//build/bazel/rules/cc:stripped_cc_common.bzl", "CcUnstrippedInfo") 21load("//build/bazel/rules/license:license_aspect.bzl", "license_aspect") 22 23ApexCcInfo = provider( 24 "Info needed to use CC targets in APEXes", 25 fields = { 26 "provides_native_libs": "Labels of native shared libs that this apex provides.", 27 "requires_native_libs": "Labels of native shared libs that this apex requires.", 28 "transitive_shared_libs": "File references to transitive .so libs produced by the CC targets and should be included in the APEX.", 29 }, 30) 31 32ApexCcMkInfo = provider( 33 "AndroidMk data about CC targets in APEXes", 34 fields = { 35 "make_modules_to_install": "List of module names that should be installed into the system, along with this APEX", 36 }, 37) 38 39# Special libraries that are installed to the bootstrap subdirectory. Bionic 40# libraries are assumed to be provided by the system, and installed automatically 41# as a symlink to the runtime APEX. 42# 43# This list is from https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/cc.go;l=1439-1452;drc=9c667416ded33b93a44c5f1894ea23cae6699a17 44# 45# NOTE: Keep this list in sync with the Soong list. 46# 47# See cc/binary.go#install for more information. 48def _installed_to_bootstrap(label): 49 label = str(label) 50 51 # hwasan 52 if label == "@//prebuilts/clang/host/linux-x86:libclang_rt.hwasan": 53 return True 54 55 # bionic libs 56 if label in [ 57 "@//bionic/libc:libc", 58 "@//bionic/libc:libc_hwasan", # For completeness, but no one should be depending on this. 59 "@//bionic/libm:libm", 60 "@//bionic/libdl:libdl", 61 "@//bionic/libdl_android:libdl_android", 62 "@//bionic/linker:linker", 63 ]: 64 return True 65 66 return False 67 68def has_cc_stubs(target): 69 """ 70 Return True if this target provides stubs. 71 72 There is no need to check versions of stubs any more, see aosp/1609533. 73 74 These stable ABI libraries are intentionally omitted from APEXes as they are 75 provided from another APEX or the platform. By omitting them from APEXes, we 76 ensure that there are no multiple copies of such libraries on a device. 77 78 Args: 79 target: The target to check for stubs on. 80 Returns: 81 If the target has cc stubs 82 """ 83 if CcStubLibrarySharedInfo in target: 84 # This is a stub lib (direct or transitive). 85 return True 86 87 if CcStubLibrariesInfo in target and target[CcStubLibrariesInfo].has_stubs: 88 # Direct deps of the apex. The apex would depend on the source lib, not stub lib, 89 # so check for CcStubLibrariesInfo.has_stubs. 90 return True 91 92 return False 93 94# Check if this target is specified as a direct dependency of the APEX, 95# as opposed to a transitive dependency, as the transitivity impacts 96# the files that go into an APEX. 97def is_apex_direct_dep(label, ctx): 98 apex_direct_deps = ctx.attr._apex_direct_deps[BuildSettingInfo].value 99 return str(label) in apex_direct_deps 100 101MinSdkVersionInfo = provider( 102 "MinSdkVersionInfo provides metadata about the min_sdk_version attribute of a target", 103 fields = { 104 "apex_inherit": "true if min_sdk_version: \"apex_inherit\" is present on the module", 105 "min_sdk_version": "value of min_sdk_version", 106 }, 107) 108 109def get_min_sdk_version(ctx): 110 """get_min_sdk_version returns the min_sdk_version for the existing target 111 112 Args: 113 ctx (rule context): a rule context 114 Returns: 115 MinSdkVersionInfo 116 """ 117 min_sdk_version = None 118 apex_inherit = False 119 if hasattr(ctx.rule.attr, "min_sdk_version"): 120 if ctx.rule.attr.min_sdk_version == "apex_inherit": 121 apex_inherit = True 122 elif ctx.rule.attr.min_sdk_version: 123 min_sdk_version = parse_apex_sdk_version(ctx.rule.attr.min_sdk_version) 124 else: 125 # min_sdk_version in cc targets are represented as features 126 for f in ctx.rule.attr.features: 127 if f.startswith("sdk_version_"): 128 # e.g. sdk_version_29 or sdk_version_10000 or sdk_version_apex_inherit 129 sdk_version = f.removeprefix("sdk_version_") 130 if sdk_version == "apex_inherit": 131 apex_inherit = True 132 elif min_sdk_version == None: 133 min_sdk_version = int(sdk_version) 134 else: 135 fail( 136 "found more than one sdk_version feature on {target}; features = {features}", 137 target = ctx.label, 138 features = ctx.rule.attr.features, 139 ) 140 return MinSdkVersionInfo( 141 min_sdk_version = min_sdk_version, 142 apex_inherit = apex_inherit, 143 ) 144 145def _validate_min_sdk_version(ctx): 146 dep_min_version = get_min_sdk_version(ctx).min_sdk_version 147 apex_min_version = parse_apex_sdk_version(ctx.attr._min_sdk_version[BuildSettingInfo].value) 148 if dep_min_version and apex_min_version < dep_min_version: 149 fail("The apex %s's min_sdk_version %s cannot be lower than the dep's min_sdk_version %s" % 150 (ctx.attr._apex_name[BuildSettingInfo].value, apex_min_version, dep_min_version)) 151 152def _apex_cc_aspect_impl(target, ctx): 153 # Ensure that dependencies are compatible with this apex's min_sdk_level 154 if not ctx.attr.testonly: 155 _validate_min_sdk_version(ctx) 156 157 # Whether this dep is a direct dep of an APEX or makes a difference in dependency 158 # traversal, and aggregation of libs that are required from the platform/other APEXes, 159 # and libs that this APEX will provide to others. 160 is_direct_dep = is_apex_direct_dep(target.label, ctx) 161 162 provides = [] 163 requires = [] 164 make_modules_to_install = [] 165 166 # The APEX manifest records the stub-providing libs (ABI-stable) in its 167 # direct and transitive deps. 168 # 169 # If a stub-providing lib is in the direct deps of an apex, then the apex 170 # provides the symbols. 171 # 172 # If a stub-providing lib is in the transitive deps of an apex, then the 173 # apex requires the symbols from the platform or other apexes. 174 if has_cc_stubs(target): 175 if is_direct_dep: 176 # Mark this target as "stub-providing" exports of this APEX, 177 # which the system and other APEXes can depend on, and propagate 178 # this list. 179 provides.append(target.label) 180 else: 181 # If this is not a direct dep and the build is in not unbundled mode, 182 # and stubs are available, don't propagate the libraries. 183 184 # Mark this target as required from the system either via 185 # the system partition, or another APEX, and propagate this list. 186 if CcStubLibrarySharedInfo not in target: 187 fail("Analysis of target: %s in apex: %s failed. This target does not provide CcStubLibrarySharedInfo. \ 188This apex should likely use stubs of the target instead." % (target, ctx.attr._apex_name[BuildSettingInfo].value)) 189 source_library_label = target[CcStubLibrarySharedInfo].source_library_label 190 191 # If a stub library is in the "provides" of the apex, it doesn't need to be in the "requires" 192 if not is_apex_direct_dep(source_library_label, ctx): 193 requires.append(source_library_label) 194 if not ctx.attr._unbundled_build[BuildSettingInfo].value and not _installed_to_bootstrap(source_library_label): 195 # It's sufficient to pass the make module name, not the fully qualified bazel label. 196 make_modules_to_install.append(source_library_label.name) 197 198 return [ 199 ApexCcInfo( 200 transitive_shared_libs = depset(), 201 requires_native_libs = depset(direct = requires), 202 provides_native_libs = depset(direct = provides), 203 ), 204 ApexCcMkInfo( 205 make_modules_to_install = depset(direct = make_modules_to_install), 206 ), 207 ] 208 209 shared_object_files = [] 210 211 # Transitive deps containing shared libraries to be propagated the apex. 212 transitive_deps = [] 213 rules_propagate_src = [ 214 "_bssl_hash_injection", 215 "stripped_shared_library", 216 "versioned_shared_library", 217 "stripped_binary", 218 "versioned_binary", 219 ] 220 221 # Exclude the stripped and unstripped so files 222 if ctx.rule.kind == "_cc_library_shared_proxy": 223 shared_object_files.append(struct( 224 stripped = target[CcSharedLibraryOutputInfo].output_file, 225 unstripped = target[CcUnstrippedInfo].unstripped, 226 metadata_file = target[MetadataFileInfo].metadata_file, 227 generating_rule_owner = target[CcSharedLibraryOutputInfo].output_file.owner, 228 )) 229 if hasattr(ctx.rule.attr, "shared"): 230 transitive_deps.append(ctx.rule.attr.shared[0]) 231 elif ctx.rule.kind == "cc_prebuilt_library_shared": 232 files = target[DefaultInfo].files.to_list() 233 if len(files) != 1: 234 fail("expected only 1 file in %s[DefaultInfo].files, but got %d" % (target.label, len(files))) 235 shared_object_files.append(struct( 236 # TODO: This file needs to actually be stripped. 237 stripped = files[0], 238 unstripped = files[0], 239 metadata_file = None, 240 # Normally the generating_rule_owner is the owner of the stripped 241 # output file, but the owner of files[0] has slashes in its name, 242 # and the APEX's make_module_name must not contain a slash. 243 generating_rule_owner = target.label, 244 )) 245 elif ctx.rule.kind in ["cc_shared_library", "cc_binary"]: 246 # Propagate along the dynamic_deps edges for binaries and shared libs 247 if hasattr(ctx.rule.attr, "dynamic_deps"): 248 for dep in ctx.rule.attr.dynamic_deps: 249 transitive_deps.append(dep) 250 elif ctx.rule.kind in rules_propagate_src and hasattr(ctx.rule.attr, "src"): 251 # Propagate along the src edge 252 if ctx.rule.kind == "stripped_binary": 253 transitive_deps.append(ctx.rule.attr.src[0]) 254 else: 255 transitive_deps.append(ctx.rule.attr.src) 256 257 # We only collect runtime dependencies from binaries and shared libraries, 258 # we _explicitly_ omit static libraries (kind = _cc_library_combiner) 259 if ctx.rule.kind in ["stripped_binary", "_cc_library_shared_proxy"] and hasattr(ctx.rule.attr, "runtime_deps"): 260 for dep in ctx.rule.attr.runtime_deps: 261 unstripped = None 262 if CcUnstrippedInfo in dep: 263 unstripped = dep[CcUnstrippedInfo].unstripped 264 for output_file in dep[DefaultInfo].files.to_list(): 265 if output_file.extension == "so": 266 shared_object_files.append(struct( 267 stripped = output_file, 268 unstripped = unstripped, 269 metadata_file = dep[MetadataFileInfo].metadata_file, 270 generating_rule_owner = output_file.owner, 271 )) 272 transitive_deps.append(dep) 273 274 return [ 275 ApexCcInfo( 276 transitive_shared_libs = depset( 277 shared_object_files, 278 transitive = [info[ApexCcInfo].transitive_shared_libs for info in transitive_deps], 279 ), 280 requires_native_libs = depset( 281 [], 282 transitive = [info[ApexCcInfo].requires_native_libs for info in transitive_deps], 283 ), 284 provides_native_libs = depset( 285 provides, 286 transitive = [info[ApexCcInfo].provides_native_libs for info in transitive_deps], 287 ), 288 ), 289 ApexCcMkInfo( 290 make_modules_to_install = depset( 291 [], 292 transitive = [info[ApexCcMkInfo].make_modules_to_install for info in transitive_deps], 293 ), 294 ), 295 ] 296 297# The list of attributes in a cc dep graph where this aspect will traverse on. 298CC_ATTR_ASPECTS = [ 299 "dynamic_deps", 300 "deps", 301 "shared", 302 "src", 303 "runtime_deps", 304 "static_deps", 305 "whole_archive_deps", 306] 307 308# This aspect is intended to be applied on a apex.native_shared_libs attribute 309apex_cc_aspect = aspect( 310 implementation = _apex_cc_aspect_impl, 311 provides = [ApexCcInfo, ApexCcMkInfo], 312 attrs = { 313 # This is propagated from the apex 314 "testonly": attr.bool(default = False), 315 "_apex_direct_deps": attr.label(default = "//build/bazel/rules/apex:apex_direct_deps"), 316 "_apex_name": attr.label(default = "//build/bazel/rules/apex:apex_name"), 317 "_min_sdk_version": attr.label(default = "//build/bazel/rules/apex:min_sdk_version"), 318 "_unbundled_build": attr.label(default = "//build/bazel/product_config:unbundled_build"), 319 }, 320 attr_aspects = CC_ATTR_ASPECTS, 321 requires = [license_aspect], 322 # TODO: Have this aspect also propagate along attributes of native_shared_libs? 323) 324