1""" 2Copyright (C) 2021 The Android Open Source Project 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15""" 16 17load( 18 ":cc_library_common.bzl", 19 "add_lists_defaulting_to_none", 20 "disable_crt_link", 21 "parse_sdk_version", 22 "system_dynamic_deps_defaults", 23) 24load(":cc_library_static.bzl", "cc_library_static") 25load(":cc_stub_library.bzl", "CcStubInfo", "cc_stub_gen") 26load(":generate_toc.bzl", "shared_library_toc", _CcTocInfo = "CcTocInfo") 27load(":stl.bzl", "shared_stl_deps") 28load(":stripped_cc_common.bzl", "stripped_shared_library") 29load(":versioned_cc_common.bzl", "versioned_shared_library") 30load("@rules_cc//examples:experimental_cc_shared_library.bzl", "cc_shared_library", _CcSharedLibraryInfo = "CcSharedLibraryInfo") 31load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cpp_toolchain") 32 33CcTocInfo = _CcTocInfo 34CcSharedLibraryInfo = _CcSharedLibraryInfo 35 36def cc_library_shared( 37 name, 38 # Common arguments between shared_root and the shared library 39 features = [], 40 dynamic_deps = [], 41 implementation_dynamic_deps = [], 42 linkopts = [], 43 target_compatible_with = [], 44 # Ultimately _static arguments for shared_root production 45 srcs = [], 46 srcs_c = [], 47 srcs_as = [], 48 copts = [], 49 cppflags = [], 50 conlyflags = [], 51 asflags = [], 52 hdrs = [], 53 implementation_deps = [], 54 deps = [], 55 whole_archive_deps = [], 56 system_dynamic_deps = None, 57 export_includes = [], 58 export_absolute_includes = [], 59 export_system_includes = [], 60 local_includes = [], 61 absolute_includes = [], 62 rtti = False, 63 use_libcrt = True, # FIXME: Unused below? 64 stl = "", 65 cpp_std = "", 66 c_std = "", 67 link_crt = True, 68 additional_linker_inputs = None, 69 70 # Purely _shared arguments 71 strip = {}, 72 soname = "", 73 74 # TODO(b/202299295): Handle data attribute. 75 data = [], 76 use_version_lib = False, 77 stubs_symbol_file = None, 78 stubs_versions = [], 79 inject_bssl_hash = False, 80 sdk_version = "", 81 min_sdk_version = "", 82 **kwargs): 83 "Bazel macro to correspond with the cc_library_shared Soong module." 84 85 shared_root_name = name + "_root" 86 unstripped_name = name + "_unstripped" 87 stripped_name = name + "_stripped" 88 toc_name = name + "_toc" 89 90 if system_dynamic_deps == None: 91 system_dynamic_deps = system_dynamic_deps_defaults 92 93 # Force crtbegin and crtend linking unless explicitly disabled (i.e. bionic 94 # libraries do this) 95 if link_crt == False: 96 features = disable_crt_link(features) 97 98 if min_sdk_version: 99 features = features + [ 100 "sdk_version_" + parse_sdk_version(min_sdk_version), 101 "-sdk_version_default", 102 ] 103 104 # The static library at the root of the shared library. 105 # This may be distinct from the static version of the library if e.g. 106 # the static-variant srcs are different than the shared-variant srcs. 107 cc_library_static( 108 name = shared_root_name, 109 hdrs = hdrs, 110 srcs = srcs, 111 srcs_c = srcs_c, 112 srcs_as = srcs_as, 113 copts = copts, 114 cppflags = cppflags, 115 conlyflags = conlyflags, 116 asflags = asflags, 117 export_includes = export_includes, 118 export_absolute_includes = export_absolute_includes, 119 export_system_includes = export_system_includes, 120 local_includes = local_includes, 121 absolute_includes = absolute_includes, 122 rtti = rtti, 123 stl = stl, 124 cpp_std = cpp_std, 125 c_std = c_std, 126 dynamic_deps = dynamic_deps, 127 implementation_deps = implementation_deps, 128 implementation_dynamic_deps = implementation_dynamic_deps, 129 system_dynamic_deps = system_dynamic_deps, 130 deps = deps + whole_archive_deps, 131 features = features, 132 use_version_lib = use_version_lib, 133 target_compatible_with = target_compatible_with, 134 ) 135 136 stl_static, stl_shared = shared_stl_deps(stl) 137 138 # implementation_deps and deps are to be linked into the shared library via 139 # --no-whole-archive. In order to do so, they need to be dependencies of 140 # a "root" of the cc_shared_library, but may not be roots themselves. 141 # Below we define stub roots (which themselves have no srcs) in order to facilitate 142 # this. 143 imp_deps_stub = name + "_implementation_deps" 144 deps_stub = name + "_deps" 145 native.cc_library( 146 name = imp_deps_stub, 147 deps = implementation_deps + stl_static, 148 target_compatible_with = target_compatible_with, 149 ) 150 native.cc_library( 151 name = deps_stub, 152 deps = deps, 153 target_compatible_with = target_compatible_with, 154 ) 155 156 shared_dynamic_deps = add_lists_defaulting_to_none( 157 dynamic_deps, 158 system_dynamic_deps, 159 implementation_dynamic_deps, 160 stl_shared, 161 ) 162 163 if len(soname) == 0: 164 soname = name + ".so" 165 soname_flag = "-Wl,-soname," + soname 166 167 cc_shared_library( 168 name = unstripped_name, 169 user_link_flags = linkopts + [soname_flag], 170 # b/184806113: Note this is a workaround so users don't have to 171 # declare all transitive static deps used by this target. It'd be great 172 # if a shared library could declare a transitive exported static dep 173 # instead of needing to declare each target transitively. 174 static_deps = ["//:__subpackages__"] + [shared_root_name, imp_deps_stub, deps_stub], 175 dynamic_deps = shared_dynamic_deps, 176 additional_linker_inputs = additional_linker_inputs, 177 roots = [shared_root_name, imp_deps_stub, deps_stub] + whole_archive_deps, 178 features = features, 179 target_compatible_with = target_compatible_with, 180 **kwargs 181 ) 182 183 hashed_name = name + "_hashed" 184 _bssl_hash_injection( 185 name = hashed_name, 186 src = unstripped_name, 187 inject_bssl_hash = inject_bssl_hash, 188 ) 189 190 versioned_name = name + "_versioned" 191 versioned_shared_library( 192 name = versioned_name, 193 src = hashed_name, 194 stamp_build_number = use_version_lib, 195 ) 196 197 stripped_shared_library( 198 name = stripped_name, 199 src = versioned_name, 200 target_compatible_with = target_compatible_with, 201 **strip 202 ) 203 204 shared_library_toc( 205 name = toc_name, 206 src = stripped_name, 207 target_compatible_with = target_compatible_with, 208 ) 209 210 # Emit the stub version of this library (e.g. for libraries that are 211 # provided by the NDK) 212 stub_shared_libraries = [] 213 if stubs_symbol_file and len(stubs_versions) > 0: 214 # TODO(b/193663198): This unconditionally creates stubs for every version, but 215 # that's not always true depending on whether this module is available 216 # on the host, ramdisk, vendor ramdisk. We currently don't have 217 # information about the image variant yet, so we'll create stub targets 218 # for all shared libraries with the stubs property for now. 219 # 220 # See: https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/library.go;l=2316-2377;drc=3d3b35c94ed2a3432b2e5e7e969a3a788a7a80b5 221 for version in stubs_versions: 222 stubs_library_name = "_".join([name, version, "stubs"]) 223 cc_stub_library_shared( 224 name = stubs_library_name, 225 stubs_symbol_file = stubs_symbol_file, 226 version = version, 227 target_compatible_with = target_compatible_with, 228 features = features, 229 ) 230 stub_shared_libraries.append(stubs_library_name) 231 232 _cc_library_shared_proxy( 233 name = name, 234 shared = stripped_name, 235 root = shared_root_name, 236 table_of_contents = toc_name, 237 output_file = soname, 238 target_compatible_with = target_compatible_with, 239 stub_shared_libraries = stub_shared_libraries, 240 ) 241 242# cc_stub_library_shared creates a cc_library_shared target, but using stub C source files generated 243# from a library's .map.txt files and ndkstubgen. The top level target returns the same 244# providers as a cc_library_shared, with the addition of a CcStubInfo 245# containing metadata files and versions of the stub library. 246def cc_stub_library_shared(name, stubs_symbol_file, version, target_compatible_with, features): 247 # Call ndkstubgen to generate the stub.c source file from a .map.txt file. These 248 # are accessible in the CcStubInfo provider of this target. 249 cc_stub_gen( 250 name = name + "_files", 251 symbol_file = stubs_symbol_file, 252 version = version, 253 target_compatible_with = target_compatible_with, 254 ) 255 256 # The static library at the root of the stub shared library. 257 cc_library_static( 258 name = name + "_root", 259 srcs_c = [name + "_files"], # compile the stub.c file 260 features = disable_crt_link(features) + \ 261 [ 262 # Enable the stub library compile flags 263 "stub_library", 264 # Disable all include-related features to avoid including any headers 265 # that may cause conflicting type errors with the symbols in the 266 # generated stubs source code. 267 # e.g. 268 # double acos(double); // in header 269 # void acos() {} // in the generated source code 270 # See https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/library.go;l=942-946;drc=d8a72d7dc91b2122b7b10b47b80cf2f7c65f9049 271 "-toolchain_include_directories", 272 "-includes", 273 "-include_paths", 274 ], 275 target_compatible_with = target_compatible_with, 276 stl = "none", 277 system_dynamic_deps = [], 278 ) 279 280 # Create a .so for the stub library. This library is self contained, has 281 # no deps, and doesn't link against crt. 282 cc_shared_library( 283 name = name + "_so", 284 roots = [name + "_root"], 285 features = disable_crt_link(features), 286 target_compatible_with = target_compatible_with, 287 ) 288 289 # Create a target with CcSharedLibraryInfo and CcStubInfo providers. 290 _cc_stub_library_shared( 291 name = name, 292 stub_target = name + "_files", 293 library_target = name + "_so", 294 ) 295 296def _cc_stub_library_shared_impl(ctx): 297 return [ 298 ctx.attr.library_target[DefaultInfo], 299 ctx.attr.library_target[CcSharedLibraryInfo], 300 ctx.attr.stub_target[CcStubInfo], 301 ] 302 303_cc_stub_library_shared = rule( 304 implementation = _cc_stub_library_shared_impl, 305 doc = "Top level rule to merge CcStubInfo and CcSharedLibraryInfo into a single target", 306 attrs = { 307 "stub_target": attr.label(mandatory = True), 308 "library_target": attr.label(mandatory = True), 309 }, 310) 311 312def _swap_shared_linker_input(ctx, shared_info, new_output): 313 old_library_to_link = shared_info.linker_input.libraries[0] 314 315 cc_toolchain = find_cpp_toolchain(ctx) 316 feature_configuration = cc_common.configure_features( 317 ctx = ctx, 318 cc_toolchain = cc_toolchain, 319 ) 320 321 new_library_to_link = cc_common.create_library_to_link( 322 actions = ctx.actions, 323 dynamic_library = new_output, 324 feature_configuration = feature_configuration, 325 cc_toolchain = cc_toolchain, 326 ) 327 328 new_linker_input = cc_common.create_linker_input( 329 owner = shared_info.linker_input.owner, 330 libraries = depset([new_library_to_link]), 331 ) 332 333 return CcSharedLibraryInfo( 334 dynamic_deps = shared_info.dynamic_deps, 335 exports = shared_info.exports, 336 link_once_static_libs = shared_info.link_once_static_libs, 337 linker_input = new_linker_input, 338 preloaded_deps = shared_info.preloaded_deps, 339 ) 340 341CcStubLibrariesInfo = provider( 342 fields = { 343 "infos": "A list of dict, where each dict contains the CcStubInfo, CcSharedLibraryInfo and DefaultInfo of a version of a stub library.", 344 }, 345) 346 347def _cc_library_shared_proxy_impl(ctx): 348 root_files = ctx.attr.root[DefaultInfo].files.to_list() 349 shared_files = ctx.attr.shared[DefaultInfo].files.to_list() 350 351 if len(shared_files) != 1: 352 fail("Expected only one shared library file") 353 354 shared_lib = shared_files[0] 355 356 ctx.actions.symlink( 357 output = ctx.outputs.output_file, 358 target_file = shared_lib, 359 ) 360 361 files = root_files + [ctx.outputs.output_file, ctx.files.table_of_contents[0]] 362 363 stub_library_infos = [] 364 for stub_library in ctx.attr.stub_shared_libraries: 365 providers = { 366 "CcStubInfo": stub_library[CcStubInfo], 367 "CcSharedLibraryInfo": stub_library[CcSharedLibraryInfo], 368 "DefaultInfo": stub_library[DefaultInfo], 369 } 370 stub_library_infos.append(providers) 371 372 return [ 373 DefaultInfo( 374 files = depset(direct = files), 375 runfiles = ctx.runfiles(files = [ctx.outputs.output_file]), 376 ), 377 _swap_shared_linker_input(ctx, ctx.attr.shared[CcSharedLibraryInfo], ctx.outputs.output_file), 378 ctx.attr.table_of_contents[CcTocInfo], 379 # Propagate only includes from the root. Do not re-propagate linker inputs. 380 CcInfo(compilation_context = ctx.attr.root[CcInfo].compilation_context), 381 CcStubLibrariesInfo(infos = stub_library_infos), 382 ] 383 384_cc_library_shared_proxy = rule( 385 implementation = _cc_library_shared_proxy_impl, 386 attrs = { 387 "shared": attr.label(mandatory = True, providers = [CcSharedLibraryInfo]), 388 "root": attr.label(mandatory = True, providers = [CcInfo]), 389 "output_file": attr.output(mandatory = True), 390 "table_of_contents": attr.label( 391 mandatory = True, 392 # TODO(b/217908237): reenable allow_single_file 393 # allow_single_file = True, 394 providers = [CcTocInfo], 395 ), 396 "stub_shared_libraries": attr.label_list(providers = [CcStubInfo, CcSharedLibraryInfo]), 397 }, 398 fragments = ["cpp"], 399 toolchains = ["@bazel_tools//tools/cpp:toolchain_type"], 400) 401 402def _bssl_hash_injection_impl(ctx): 403 if len(ctx.files.src) != 1: 404 fail("Expected only one shared library file") 405 406 hashed_file = ctx.files.src[0] 407 if ctx.attr.inject_bssl_hash: 408 hashed_file = ctx.actions.declare_file("lib" + ctx.attr.name + ".so") 409 args = ctx.actions.args() 410 args.add_all(["-sha256"]) 411 args.add_all(["-in-object", ctx.files.src[0]]) 412 args.add_all(["-o", hashed_file]) 413 414 ctx.actions.run( 415 inputs = ctx.files.src, 416 outputs = [hashed_file], 417 executable = ctx.executable._bssl_inject_hash, 418 arguments = [args], 419 tools = [ctx.executable._bssl_inject_hash], 420 mnemonic = "BsslInjectHash", 421 ) 422 423 return [ 424 DefaultInfo(files = depset([hashed_file])), 425 ctx.attr.src[CcSharedLibraryInfo], 426 ] 427 428_bssl_hash_injection = rule( 429 implementation = _bssl_hash_injection_impl, 430 attrs = { 431 "src": attr.label( 432 mandatory = True, 433 # TODO(b/217908237): reenable allow_single_file 434 # allow_single_file = True, 435 providers = [CcSharedLibraryInfo], 436 ), 437 "inject_bssl_hash": attr.bool( 438 default = False, 439 doc = "Whether inject BSSL hash", 440 ), 441 "_bssl_inject_hash": attr.label( 442 cfg = "exec", 443 doc = "The BSSL hash injection tool.", 444 executable = True, 445 default = "//prebuilts/build-tools:linux-x86/bin/bssl_inject_hash", 446 allow_single_file = True, 447 ), 448 }, 449) 450