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//lib:paths.bzl", "paths") 16load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") 17load("//build/bazel/rules/abi:abi_dump.bzl", "AbiDiffInfo", "abi_dump") 18load( 19 ":cc_library_common.bzl", 20 "CcAndroidMkInfo", 21 "add_lists_defaulting_to_none", 22 "check_valid_ldlibs", 23 "parse_sdk_version", 24 "system_dynamic_deps_defaults", 25) 26load(":cc_library_static.bzl", "cc_library_static") 27load(":clang_tidy.bzl", "collect_deps_clang_tidy_info") 28load( 29 ":composed_transitions.bzl", 30 "lto_and_fdo_profile_incoming_transition", 31 "lto_and_sanitizer_deps_transition", 32) 33load( 34 ":fdo_profile_transitions.bzl", 35 "FDO_PROFILE_ATTR_KEY", 36) 37load(":generate_toc.bzl", "CcTocInfo", "generate_toc") 38load(":stl.bzl", "stl_info_from_attr") 39load(":stripped_cc_common.bzl", "CcUnstrippedInfo", "stripped_shared_library") 40load(":versioned_cc_common.bzl", "versioned_shared_library") 41 42def cc_library_shared( 43 name, 44 stem = "", 45 suffix = "", 46 # Common arguments between shared_root and the shared library 47 features = [], 48 dynamic_deps = [], 49 implementation_dynamic_deps = [], 50 linkopts = [], 51 target_compatible_with = [], 52 # Ultimately _static arguments for shared_root production 53 srcs = [], 54 srcs_c = [], 55 srcs_as = [], 56 copts = [], 57 cppflags = [], 58 conlyflags = [], 59 asflags = [], 60 hdrs = [], 61 implementation_deps = [], 62 deps = [], 63 whole_archive_deps = [], 64 implementation_whole_archive_deps = [], 65 system_dynamic_deps = None, 66 runtime_deps = [], 67 export_includes = [], 68 export_absolute_includes = [], 69 export_system_includes = [], 70 local_includes = [], 71 absolute_includes = [], 72 rtti = False, 73 stl = "", 74 cpp_std = "", 75 c_std = "", 76 additional_linker_inputs = None, 77 additional_compiler_inputs = [], 78 79 # Purely _shared arguments 80 strip = {}, 81 82 # TODO(b/202299295): Handle data attribute. 83 data = [], # @unused 84 use_version_lib = False, 85 stubs_symbol_file = None, 86 inject_bssl_hash = False, 87 sdk_version = "", # @unused 88 min_sdk_version = "", 89 abi_checker_enabled = None, 90 abi_checker_symbol_file = None, 91 abi_checker_exclude_symbol_versions = [], 92 abi_checker_exclude_symbol_tags = [], 93 abi_checker_check_all_apis = False, 94 abi_checker_diff_flags = [], 95 native_coverage = True, 96 tags = [], 97 fdo_profile = None, 98 tidy = None, 99 tidy_checks = None, 100 tidy_checks_as_errors = None, 101 tidy_flags = None, 102 tidy_disabled_srcs = None, 103 tidy_timeout_srcs = None, 104 tidy_gen_header_filter = None, 105 **kwargs): 106 "Bazel macro to correspond with the cc_library_shared Soong module." 107 108 # There exist modules named 'libtest_missing_symbol' and 109 # 'libtest_missing_symbol_root'. Ensure that that the target suffixes are 110 # sufficiently unique. 111 shared_root_name = name + "__internal_root" 112 unstripped_name = name + "_unstripped" 113 stripped_name = name + "_stripped" 114 115 if system_dynamic_deps == None: 116 system_dynamic_deps = system_dynamic_deps_defaults 117 118 if min_sdk_version: 119 features = features + parse_sdk_version(min_sdk_version) + ["-sdk_version_default"] 120 121 if fdo_profile != None: 122 # FIXME(b/261609769): This is a temporary workaround to add link flags 123 # that requires the path to fdo profile. 124 # This workaround is error-prone because it assumes all the fdo_profile 125 # targets are created in a specific way (e.g. fdo_profile target named foo 126 # uses an afdo profile file named foo.afdo in the same folder). 127 fdo_profile_file = fdo_profile + "_file" 128 linkopts = linkopts + [ 129 "-funique-internal-linkage-names", 130 "-fprofile-sample-accurate", 131 # profile-sample-use is needed to ensure symbol ordering 132 "-fprofile-sample-use=$(location {})".format(fdo_profile_file), 133 "-Wl,-mllvm,-no-warn-sample-unused=true", 134 ] 135 if additional_linker_inputs != None: 136 additional_linker_inputs = additional_linker_inputs + [fdo_profile_file] 137 else: 138 additional_linker_inputs = [fdo_profile_file] 139 140 stl_info = stl_info_from_attr(stl, True) 141 linkopts = linkopts + stl_info.linkopts 142 copts = copts + stl_info.cppflags 143 144 if not native_coverage: 145 features = features + ["-coverage"] 146 else: 147 features = features + select({ 148 "//build/bazel/rules/cc:android_coverage_lib_flag": ["android_coverage_lib"], 149 "//build/bazel/rules/cc:android_coverage_lib_flag_cfi": ["android_coverage_lib"], 150 "//conditions:default": [], 151 }) 152 153 # TODO(b/233660582): deal with the cases where the default lib shouldn't be used 154 deps = deps + select({ 155 "//build/bazel/rules/cc:android_coverage_lib_flag": ["//system/extras/toolchain-extras:libprofile-clang-extras"], 156 "//build/bazel/rules/cc:android_coverage_lib_flag_cfi": ["//system/extras/toolchain-extras:libprofile-clang-extras_cfi_support"], 157 "//conditions:default": [], 158 }) 159 160 # The static library at the root of the shared library. 161 # This may be distinct from the static version of the library if e.g. 162 # the static-variant srcs are different than the shared-variant srcs. 163 cc_library_static( 164 name = shared_root_name, 165 shared_linking = True, 166 hdrs = hdrs, 167 srcs = srcs, 168 srcs_c = srcs_c, 169 srcs_as = srcs_as, 170 copts = copts, 171 cppflags = cppflags, 172 conlyflags = conlyflags, 173 asflags = asflags, 174 export_includes = export_includes, 175 export_absolute_includes = export_absolute_includes, 176 export_system_includes = export_system_includes, 177 local_includes = local_includes, 178 absolute_includes = absolute_includes, 179 rtti = rtti, 180 stl = "none", 181 cpp_std = cpp_std, 182 c_std = c_std, 183 dynamic_deps = dynamic_deps, 184 implementation_deps = implementation_deps + stl_info.static_deps, 185 implementation_dynamic_deps = implementation_dynamic_deps + stl_info.shared_deps, 186 implementation_whole_archive_deps = implementation_whole_archive_deps, 187 system_dynamic_deps = system_dynamic_deps, 188 deps = deps, 189 whole_archive_deps = whole_archive_deps, 190 features = features, 191 target_compatible_with = target_compatible_with, 192 tags = ["manual"], 193 native_coverage = native_coverage, 194 tidy = tidy, 195 tidy_checks = tidy_checks, 196 tidy_checks_as_errors = tidy_checks_as_errors, 197 tidy_flags = tidy_flags, 198 tidy_disabled_srcs = tidy_disabled_srcs, 199 tidy_timeout_srcs = tidy_timeout_srcs, 200 tidy_gen_header_filter = tidy_gen_header_filter, 201 additional_compiler_inputs = additional_compiler_inputs, 202 ) 203 204 # dynamic deps are to be linked into the shared library via 205 # --no-whole-archive. In order to do so, they need to be dependencies of a 206 # "root" of the cc_shared_library, but may not be roots themselves. Below 207 # we define stub roots (which themselves have no srcs) in order to 208 # facilitate this. 209 imp_deps_stub = name + "_implementation_deps" 210 native.cc_library( 211 name = imp_deps_stub, 212 deps = implementation_dynamic_deps + 213 system_dynamic_deps + 214 stl_info.shared_deps + 215 dynamic_deps, 216 target_compatible_with = target_compatible_with, 217 tags = ["manual"], 218 ) 219 220 shared_dynamic_deps = add_lists_defaulting_to_none( 221 dynamic_deps, 222 system_dynamic_deps, 223 implementation_dynamic_deps, 224 stl_info.shared_deps, 225 ) 226 227 filename_stem = stem or name 228 soname = filename_stem + suffix + ".so" 229 soname_flag = "-Wl,-soname," + soname 230 231 native.cc_shared_library( 232 name = unstripped_name, 233 user_link_flags = linkopts + [soname_flag], 234 dynamic_deps = shared_dynamic_deps, 235 additional_linker_inputs = additional_linker_inputs, 236 deps = [shared_root_name] + whole_archive_deps + [imp_deps_stub], 237 features = features, 238 target_compatible_with = target_compatible_with, 239 tags = ["manual"], 240 **kwargs 241 ) 242 243 hashed_name = name + "_hashed" 244 _bssl_hash_injection( 245 name = hashed_name, 246 src = unstripped_name, 247 inject_bssl_hash = inject_bssl_hash, 248 tags = ["manual"], 249 ) 250 251 versioned_name = name + "_versioned" 252 versioned_shared_library( 253 name = versioned_name, 254 src = hashed_name, 255 stamp_build_number = use_version_lib, 256 tags = ["manual"], 257 ) 258 259 stripped_shared_library( 260 name = stripped_name, 261 src = versioned_name, 262 target_compatible_with = target_compatible_with, 263 tags = ["manual"], 264 **strip 265 ) 266 267 # The logic here is based on the shouldCreateSourceAbiDumpForLibrary() in sabi.go 268 # abi_root is used to control if abi_dump aspects should be run on the static 269 # deps because there is no way to control the aspects directly from the rule. 270 abi_root = shared_root_name 271 272 # explicitly disabled 273 if abi_checker_enabled == False: 274 abi_root = None 275 elif abi_checker_enabled == True or stubs_symbol_file: 276 # The logic comes from here: 277 # https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/library.go;l=2288;drc=73feba33308bf9432aea43e069ed24a2f0312f1b 278 if not abi_checker_symbol_file and stubs_symbol_file: 279 abi_checker_symbol_file = stubs_symbol_file 280 else: 281 abi_root = None 282 283 abi_checker_explicitly_disabled = abi_checker_enabled == False 284 285 abi_dump_name = name + "_abi_dump" 286 abi_dump( 287 name = abi_dump_name, 288 shared = stripped_name, 289 root = abi_root, 290 soname = soname, 291 has_stubs = stubs_symbol_file != None, 292 enabled = abi_checker_enabled, 293 explicitly_disabled = abi_checker_explicitly_disabled, 294 symbol_file = abi_checker_symbol_file, 295 exclude_symbol_versions = abi_checker_exclude_symbol_versions, 296 exclude_symbol_tags = abi_checker_exclude_symbol_tags, 297 check_all_apis = abi_checker_check_all_apis, 298 diff_flags = abi_checker_diff_flags, 299 tags = ["manual"], 300 ) 301 302 _cc_library_shared_proxy( 303 name = name, 304 shared = stripped_name, 305 shared_debuginfo = unstripped_name, 306 deps = [shared_root_name], 307 features = features, 308 output_file = paths.join(name, soname), # Prevent name collision by generating in a directory unique to the target 309 target_compatible_with = target_compatible_with, 310 has_stubs = stubs_symbol_file != None, 311 runtime_deps = runtime_deps, 312 abi_dump = abi_dump_name, 313 fdo_profile = fdo_profile, 314 linkopts = linkopts, 315 package_name = native.package_name(), 316 tags = tags, 317 ) 318 319def _create_dynamic_library_linker_input_for_file(ctx, shared_info, output): 320 cc_toolchain = find_cpp_toolchain(ctx) 321 feature_configuration = cc_common.configure_features( 322 ctx = ctx, 323 cc_toolchain = cc_toolchain, 324 ) 325 326 new_library_to_link = cc_common.create_library_to_link( 327 actions = ctx.actions, 328 dynamic_library = output, 329 feature_configuration = feature_configuration, 330 cc_toolchain = cc_toolchain, 331 ) 332 333 new_linker_input = cc_common.create_linker_input( 334 owner = shared_info.linker_input.owner, 335 libraries = depset([new_library_to_link]), 336 ) 337 return new_linker_input 338 339def _correct_cc_shared_library_linking(ctx, shared_info, new_output): 340 # we may have done some post-processing of the shared library 341 # replace the linker_input that has not been post-processed with the 342 # library that has been post-processed 343 new_linker_input = _create_dynamic_library_linker_input_for_file(ctx, shared_info, new_output) 344 345 return CcSharedLibraryInfo( 346 dynamic_deps = shared_info.dynamic_deps, 347 exports = shared_info.exports, 348 link_once_static_libs = shared_info.link_once_static_libs, 349 linker_input = new_linker_input, 350 ) 351 352CcStubLibrariesInfo = provider( 353 fields = { 354 "has_stubs": "If the shared library has stubs", 355 }, 356) 357 358# A provider to propagate shared library output artifacts, primarily useful 359# for root level querying in Soong-Bazel mixed builds. 360# Ideally, it would be preferable to reuse the existing native 361# CcSharedLibraryInfo provider, but that provider requires that shared library 362# artifacts are wrapped in a linker input. Artifacts retrievable from this linker 363# input are symlinks to the original artifacts, which is problematic when 364# other dependencies expect a real file. 365CcSharedLibraryOutputInfo = provider( 366 fields = { 367 "output_file": "A single .so file, produced by this target.", 368 }, 369) 370 371def _cc_library_shared_proxy_impl(ctx): 372 check_valid_ldlibs(ctx, ctx.attr.linkopts) 373 374 # Using a "deps" label_list instead of a single mandatory label attribute 375 # is a hack to support aspect propagation of graph_aspect of the native 376 # cc_shared_library. The aspect will only be applied and propagated along 377 # a label_list attribute named "deps". 378 if len(ctx.attr.deps) != 1: 379 fail("Exactly one 'deps' must be specified for cc_library_shared_proxy") 380 root_files = ctx.attr.deps[0][DefaultInfo].files.to_list() 381 shared_files = ctx.attr.shared[0][DefaultInfo].files.to_list() 382 shared_debuginfo = ctx.attr.shared_debuginfo[0][DefaultInfo].files.to_list() 383 if len(shared_files) != 1 or len(shared_debuginfo) != 1: 384 fail("Expected only one shared library file and one debuginfo file for it") 385 386 shared_lib = shared_files[0] 387 abi_diff_files = ctx.attr.abi_dump[0][AbiDiffInfo].diff_files.to_list() 388 389 # Copy the output instead of symlinking. This is because this output 390 # can be directly installed into a system image; this installation treats 391 # symlinks differently from real files (symlinks will be preserved relative 392 # to the image root). 393 ctx.actions.run_shell( 394 # We need to add the abi dump files to the inputs of this copy action even 395 # though they are not used, otherwise not all the abi dump files will be 396 # created. For example, for b build 397 # packages/modules/adb/pairing_connection:libadb_pairing_server, only 398 # libadb_pairing_server.so.lsdump will be created, libadb_pairing_auth.so.lsdump 399 # and libadb_pairing_connection.so.lsdump will not be. The reason is that 400 # even though libadb_pairing server depends on libadb_pairing_auth and 401 # libadb_pairing_connection, the abi dump files are not explicitly used 402 # by libadb_pairing_server, so bazel won't bother generating them. 403 inputs = depset(direct = [shared_lib] + abi_diff_files), 404 outputs = [ctx.outputs.output_file], 405 command = "cp -f %s %s" % (shared_lib.path, ctx.outputs.output_file.path), 406 mnemonic = "CopyFile", 407 progress_message = "Copying files", 408 use_default_shell_env = True, 409 ) 410 411 toc_info = generate_toc(ctx, ctx.attr.name, ctx.outputs.output_file) 412 413 files = root_files + [ctx.outputs.output_file, toc_info.toc] + abi_diff_files 414 415 return [ 416 DefaultInfo( 417 files = depset(direct = files), 418 runfiles = ctx.runfiles(files = [ctx.outputs.output_file]), 419 ), 420 _correct_cc_shared_library_linking(ctx, ctx.attr.shared[0][CcSharedLibraryInfo], ctx.outputs.output_file), 421 toc_info, 422 # The _only_ linker_input is the statically linked root itself. We need to propagate this 423 # as cc_shared_library identifies which libraries can be linked dynamically based on the 424 # linker_inputs of the roots 425 ctx.attr.deps[0][CcInfo], 426 ctx.attr.deps[0][CcAndroidMkInfo], 427 CcStubLibrariesInfo(has_stubs = ctx.attr.has_stubs), 428 ctx.attr.shared[0][OutputGroupInfo], 429 CcSharedLibraryOutputInfo(output_file = ctx.outputs.output_file), 430 CcUnstrippedInfo(unstripped = shared_debuginfo[0]), 431 ctx.attr.abi_dump[0][AbiDiffInfo], 432 collect_deps_clang_tidy_info(ctx), 433 cc_common.CcSharedLibraryHintInfo( 434 # cc_shared_library doesn't need to traverse any attrs of a cc_shared_library dep 435 attributes = [], 436 ), 437 ] 438 439_cc_library_shared_proxy = rule( 440 implementation = _cc_library_shared_proxy_impl, 441 # Incoming transition to override outgoing transition from rdep 442 cfg = lto_and_fdo_profile_incoming_transition, 443 attrs = { 444 FDO_PROFILE_ATTR_KEY: attr.label(), 445 "shared": attr.label( 446 mandatory = True, 447 providers = [CcSharedLibraryInfo], 448 cfg = lto_and_sanitizer_deps_transition, 449 ), 450 "shared_debuginfo": attr.label( 451 mandatory = True, 452 cfg = lto_and_sanitizer_deps_transition, 453 ), 454 # "deps" should be a single element: the root target of the shared library. 455 # See _cc_library_shared_proxy_impl comment for explanation. 456 "deps": attr.label_list( 457 mandatory = True, 458 providers = [CcInfo], 459 cfg = lto_and_sanitizer_deps_transition, 460 ), 461 "output_file": attr.output(mandatory = True), 462 "has_stubs": attr.bool(default = False), 463 "runtime_deps": attr.label_list( 464 providers = [CcInfo], 465 doc = "Deps that should be installed along with this target. Read by the apex cc aspect.", 466 ), 467 "abi_dump": attr.label( 468 providers = [AbiDiffInfo], 469 cfg = lto_and_sanitizer_deps_transition, 470 ), 471 "package_name": attr.string( 472 mandatory = True, 473 doc = "Just the path to the target package. Used by transitions.", 474 ), 475 "_allowlist_function_transition": attr.label( 476 default = "@bazel_tools//tools/allowlists/function_transition_allowlist", 477 ), 478 "androidmk_static_deps": attr.label_list( 479 providers = [CcInfo], 480 doc = "All the whole archive deps of the lib. This is used to propagate" + 481 " information to AndroidMk about LOCAL_STATIC_LIBRARIES.", 482 ), 483 "androidmk_whole_archive_deps": attr.label_list( 484 providers = [CcInfo], 485 doc = "All the whole archive deps of the lib. This is used to propagate" + 486 " information to AndroidMk about LOCAL_WHOLE_STATIC_LIBRARIES.", 487 ), 488 "androidmk_dynamic_deps": attr.label_list( 489 providers = [CcInfo], 490 doc = "All the dynamic deps of the lib. This is used to propagate" + 491 " information to AndroidMk about LOCAL_SHARED_LIBRARIES.", 492 ), 493 "_toc_script": attr.label( 494 cfg = "exec", 495 executable = True, 496 allow_single_file = True, 497 default = "//build/soong/scripts:toc.sh", 498 ), 499 "_readelf": attr.label( 500 cfg = "exec", 501 executable = True, 502 allow_single_file = True, 503 default = "//prebuilts/clang/host/linux-x86:llvm-readelf", 504 ), 505 "linkopts": attr.string_list(default = []), # Used for validation 506 "_android_constraint": attr.label( 507 default = Label("//build/bazel_common_rules/platforms/os:android"), 508 ), 509 "_darwin_constraint": attr.label( 510 default = Label("//build/bazel_common_rules/platforms/os:darwin"), 511 ), 512 "_linux_constraint": attr.label( 513 default = Label("//build/bazel_common_rules/platforms/os:linux"), 514 ), 515 "_windows_constraint": attr.label( 516 default = Label("//build/bazel_common_rules/platforms/os:windows"), 517 ), 518 }, 519 provides = [CcAndroidMkInfo, CcInfo, CcTocInfo, cc_common.CcSharedLibraryHintInfo], 520 fragments = ["cpp"], 521 toolchains = ["@bazel_tools//tools/cpp:toolchain_type"], 522) 523 524def _bssl_hash_injection_impl(ctx): 525 hashed_file = ctx.file.src 526 if ctx.attr.inject_bssl_hash: 527 hashed_file = ctx.actions.declare_file("lib" + ctx.attr.name + ".so") 528 args = ctx.actions.args() 529 args.add_all(["-in-object", ctx.files.src[0]]) 530 args.add_all(["-o", hashed_file]) 531 532 ctx.actions.run( 533 inputs = ctx.files.src, 534 outputs = [hashed_file], 535 executable = ctx.executable._bssl_inject_hash, 536 arguments = [args], 537 tools = [ctx.executable._bssl_inject_hash], 538 mnemonic = "BsslInjectHash", 539 ) 540 541 return [ 542 DefaultInfo(files = depset([hashed_file])), 543 ctx.attr.src[CcSharedLibraryInfo], 544 ctx.attr.src[OutputGroupInfo], 545 ] 546 547_bssl_hash_injection = rule( 548 implementation = _bssl_hash_injection_impl, 549 attrs = { 550 "src": attr.label( 551 mandatory = True, 552 allow_single_file = True, 553 providers = [CcSharedLibraryInfo], 554 ), 555 "inject_bssl_hash": attr.bool( 556 default = False, 557 doc = "Whether inject BSSL hash", 558 ), 559 "_bssl_inject_hash": attr.label( 560 cfg = "exec", 561 doc = "The BSSL hash injection tool.", 562 executable = True, 563 default = "//external/boringssl:bssl_inject_hash", 564 allow_single_file = True, 565 ), 566 }, 567) 568