1# Copyright (C) 2022 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("//build/bazel/platforms:platform_utils.bzl", "platforms") 16load("//build/bazel/rules/apis:api_surface.bzl", "MODULE_LIB_API", "PUBLIC_API") 17load("//build/bazel/rules/common:api.bzl", "api") 18load(":cc_library_headers.bzl", "cc_library_headers") 19load(":cc_library_shared.bzl", "CcStubLibrariesInfo") 20load(":cc_library_static.bzl", "cc_library_static") 21load(":composed_transitions.bzl", "drop_lto_sanitizer_and_fdo_profile_incoming_transition") 22load(":fdo_profile_transitions.bzl", "FDO_PROFILE_ATTR_KEY") 23load(":generate_toc.bzl", "CcTocInfo", "generate_toc") 24 25# This file contains the implementation for the cc_stub_library rule. 26# 27# TODO(b/207812332): 28# - ndk_api_coverage_parser: https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/coverage.go;l=248-262;drc=master 29 30CcStubInfo = provider( 31 fields = { 32 "stub_map": "The .map file containing library symbols for the specific API version.", 33 "version": "The API version of this library.", 34 "abi_symbol_list": "A plain-text list of all symbols of this library for the specific API version.", 35 }, 36) 37 38def _stub_gen_additional_args(ctx): 39 if ctx.attr.api_surface == PUBLIC_API: 40 return [] 41 42 # TODO: Support LLNDK 43 # Module-lib api, i.e. apex use case 44 apex_stub_args = ["--systemapi", "--apex"] 45 46 # If this is not an ndk library, add --no-ndk 47 if not ctx.attr.included_in_ndk: 48 # https://cs.android.com/android/_/android/platform/build/soong/+/main:cc/library.go;l=1318-1323;drc=d9b7f17f372a196efc82112c29efb86abf91e266;bpv=1;bpt=0 49 apex_stub_args.append("--no-ndk") 50 return apex_stub_args 51 52def _cc_stub_gen_impl(ctx): 53 # The name of this target. 54 name = ctx.attr.name 55 56 # All declared outputs of ndkstubgen. 57 out_stub_c = ctx.actions.declare_file("/".join([name, "stub.c"])) 58 out_stub_map = ctx.actions.declare_file("/".join([name, "stub.map"])) 59 out_abi_symbol_list = ctx.actions.declare_file("/".join([name, "abi_symbol_list.txt"])) 60 61 outputs = [out_stub_c, out_stub_map, out_abi_symbol_list] 62 63 ndkstubgen_args = ctx.actions.args() 64 ndkstubgen_args.add_all(["--arch", platforms.get_target_arch(ctx.attr._platform_utils)]) 65 ndkstubgen_args.add_all(["--api", ctx.attr.version]) 66 ndkstubgen_args.add_all(["--api-map", ctx.file._api_levels_file]) 67 68 ndkstubgen_args.add_all(_stub_gen_additional_args(ctx)) 69 ndkstubgen_args.add(ctx.file.symbol_file) 70 ndkstubgen_args.add_all(outputs) 71 ctx.actions.run( 72 executable = ctx.executable._ndkstubgen, 73 inputs = [ 74 ctx.file.symbol_file, 75 ctx.file._api_levels_file, 76 ], 77 outputs = outputs, 78 arguments = [ndkstubgen_args], 79 ) 80 81 return [ 82 # DefaultInfo.files contains the .stub.c file only so that this target 83 # can be used directly in the srcs of a cc_library. 84 DefaultInfo(files = depset([out_stub_c])), 85 CcStubInfo( 86 stub_map = out_stub_map, 87 abi_symbol_list = out_abi_symbol_list, 88 version = ctx.attr.version, 89 ), 90 OutputGroupInfo( 91 stub_map = [out_stub_map], 92 ), 93 ] 94 95cc_stub_gen = rule( 96 implementation = _cc_stub_gen_impl, 97 attrs = { 98 FDO_PROFILE_ATTR_KEY: attr.label(), 99 # Public attributes 100 "symbol_file": attr.label(mandatory = True, allow_single_file = [".map.txt"]), 101 "version": attr.string(mandatory = True, default = "current"), 102 "source_library_label": attr.label(mandatory = True), 103 "api_surface": attr.string(mandatory = True, values = [PUBLIC_API, MODULE_LIB_API]), 104 "included_in_ndk": attr.bool( 105 mandatory = False, 106 default = False, 107 doc = """ 108Set to true if the source library is part of the NDK (e.g. libc, liblog). This property is a no-op unless api_surface = module-libapi. 109When generating the stubs for this API surface, this property will be used to gate apis 1101. If True, every un-annotated api, i.e. public api will be present in stubs 1112. If False, un-annonated apis will be missing in stubs. Only #systemapi and #apex annotated apis will be present 112 113Another way to interpret this 114- For (1: True) module-libapi is a superset of publicapi and (#systemapi/#apex symbols) 115- For (2: False), module-libapi is just (#systemapi/#apex symbols) 116""", 117 ), 118 # Private attributes 119 "_api_levels_file": attr.label(default = "@soong_injection//api_levels:api_levels.json", allow_single_file = True), 120 "_ndkstubgen": attr.label(default = "//build/soong/cc/ndkstubgen", executable = True, cfg = "exec"), 121 "_platform_utils": attr.label(default = Label("//build/bazel/platforms:platform_utils")), 122 }, 123) 124 125CcStubLibrarySharedInfo = provider( 126 fields = { 127 "source_library_label": "The source library label of the cc_stub_library_shared", 128 }, 129) 130 131# cc_stub_library_shared creates a cc_library_shared target, but using stub C source files generated 132# from a library's .map.txt files and ndkstubgen. The top level target returns the same 133# providers as a cc_library_shared, with the addition of a CcStubInfo 134# containing metadata files and versions of the stub library. 135def cc_stub_library_shared(name, stubs_symbol_file, version, export_includes, soname, source_library_label, deps, target_compatible_with, features, tags, api_surface, included_in_ndk = False): 136 # Call ndkstubgen to generate the stub.c source file from a .map.txt file. These 137 # are accessible in the CcStubInfo provider of this target. 138 cc_stub_gen( 139 name = name + "_files", 140 symbol_file = stubs_symbol_file, 141 version = version, 142 source_library_label = source_library_label, 143 target_compatible_with = target_compatible_with, 144 api_surface = api_surface, 145 included_in_ndk = included_in_ndk, 146 tags = ["manual"], 147 ) 148 149 # Disable coverage for stub libraries. 150 features = features + ["-coverage", "-link_crt"] 151 152 # The static library at the root of the stub shared library. 153 cc_library_static( 154 name = name + "_root", 155 srcs_c = [name + "_files"], # compile the stub.c file 156 copts = ["-fno-builtin"], # ignore conflicts with builtin function signatures 157 features = [ 158 # Don't link the C runtime 159 "-link_crt", 160 # Enable the stub library compile flags 161 "stub_library", 162 # Disable all include-related features to avoid including any headers 163 # that may cause conflicting type errors with the symbols in the 164 # generated stubs source code. 165 # e.g. 166 # double acos(double); // in header 167 # void acos() {} // in the generated source code 168 # See https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/library.go;l=942-946;drc=d8a72d7dc91b2122b7b10b47b80cf2f7c65f9049 169 "-toolchain_include_directories", 170 "-includes", 171 "-include_paths", 172 ], 173 target_compatible_with = target_compatible_with, 174 stl = "none", 175 system_dynamic_deps = [], 176 tags = ["manual"], 177 export_includes = export_includes, 178 # deps is used to export includes that specified using "header_libs" in Android.bp, e.g. "libc_headers". 179 deps = deps, 180 ) 181 182 # Create a .so for the stub library. This library is self contained, has 183 # no deps, and doesn't link against crt. 184 if len(soname) == 0: 185 fail("For stub libraries 'soname' is mandatory and must be same as the soname of its source library.") 186 soname_flag = "-Wl,-soname," + soname 187 stub_map = name + "_stub_map" 188 native.filegroup( 189 name = stub_map, 190 srcs = [name + "_files"], 191 output_group = "stub_map", 192 tags = ["manual"], 193 ) 194 version_script_flag = "-Wl,--version-script,$(location %s)" % stub_map 195 native.cc_shared_library( 196 name = name + "_so", 197 additional_linker_inputs = [stub_map], 198 user_link_flags = [soname_flag, version_script_flag], 199 roots = [name + "_root"], 200 features = features + ["-link_crt"], 201 target_compatible_with = target_compatible_with, 202 tags = ["manual"], 203 ) 204 205 # Create a target with CcSharedLibraryInfo and CcStubInfo providers. 206 _cc_stub_library_shared( 207 name = name, 208 stub_target = name + "_files", 209 library_target = name + "_so", 210 root = name + "_root", 211 source_library_label = source_library_label, 212 version = version, 213 tags = tags, 214 ) 215 216def _cc_stub_library_shared_impl(ctx): 217 source_library_label = Label(ctx.attr.source_library_label) 218 api_level = str(api.parse_api_level_from_version(ctx.attr.version)) 219 version_macro_name = "__" + source_library_label.name.upper() + "_API__=" + api_level 220 compilation_context = cc_common.create_compilation_context( 221 defines = depset([version_macro_name]), 222 ) 223 224 cc_info = cc_common.merge_cc_infos(cc_infos = [ 225 ctx.attr.root[CcInfo], 226 CcInfo(compilation_context = compilation_context), 227 ]) 228 229 library_target_so_files = ctx.attr.library_target.files.to_list() 230 if len(library_target_so_files) != 1: 231 fail("expected single .so output file from library_target (%s); got %s" % ( 232 ctx.attr.library_target.label, 233 library_target_so_files, 234 )) 235 toc_info = generate_toc(ctx, ctx.attr.name, library_target_so_files[0]) 236 237 return [ 238 ctx.attr.library_target[DefaultInfo], 239 ctx.attr.library_target[CcSharedLibraryInfo], 240 ctx.attr.stub_target[CcStubInfo], 241 toc_info, 242 cc_info, 243 CcStubLibrariesInfo(has_stubs = True), 244 OutputGroupInfo(rule_impl_debug_files = depset()), 245 CcStubLibrarySharedInfo(source_library_label = source_library_label), 246 ] 247 248_cc_stub_library_shared = rule( 249 implementation = _cc_stub_library_shared_impl, 250 doc = "Top level rule to merge CcStubInfo and CcSharedLibraryInfo into a single target", 251 # Incoming transition to reset //command_line_option:fdo_profile to None 252 # to converge the configurations of the stub targets 253 # This also resets any lto transitions. 254 cfg = drop_lto_sanitizer_and_fdo_profile_incoming_transition, 255 attrs = { 256 FDO_PROFILE_ATTR_KEY: attr.label(), 257 "stub_target": attr.label( 258 providers = [CcStubInfo], 259 mandatory = True, 260 ), 261 "library_target": attr.label( 262 providers = [CcSharedLibraryInfo], 263 mandatory = True, 264 ), 265 "root": attr.label( 266 providers = [CcInfo], 267 mandatory = True, 268 ), 269 "source_library_label": attr.string(mandatory = True), 270 "version": attr.string(mandatory = True), 271 "_allowlist_function_transition": attr.label( 272 default = "@bazel_tools//tools/allowlists/function_transition_allowlist", 273 ), 274 "_toc_script": attr.label( 275 cfg = "exec", 276 executable = True, 277 allow_single_file = True, 278 default = "//build/soong/scripts:toc.sh", 279 ), 280 "_readelf": attr.label( 281 cfg = "exec", 282 executable = True, 283 allow_single_file = True, 284 default = "//prebuilts/clang/host/linux-x86:llvm-readelf", 285 ), 286 }, 287 provides = [ 288 CcSharedLibraryInfo, 289 CcTocInfo, 290 CcInfo, 291 CcStubInfo, 292 CcStubLibrariesInfo, 293 CcStubLibrarySharedInfo, 294 ], 295) 296 297def cc_stub_suite( 298 name, 299 source_library_label, 300 versions, 301 symbol_file, 302 export_includes = [], 303 soname = "", 304 deps = [], 305 data = [], # @unused 306 target_compatible_with = [], 307 features = [], 308 tags = ["manual"], 309 api_surface = PUBLIC_API, 310 included_in_ndk = False): 311 # Implicitly add "current" to versions. This copies the behavior from Soong (aosp/1641782) 312 if "current" not in versions: 313 versions.append("current") 314 315 for version in versions: 316 cc_stub_library_shared( 317 # Use - as the seperator of name and version. "current" might be the version of some libraries. 318 name = name + "-" + version, 319 version = version, 320 stubs_symbol_file = symbol_file, 321 export_includes = export_includes, 322 soname = soname, 323 source_library_label = str(native.package_relative_label(source_library_label)), 324 deps = deps, 325 target_compatible_with = target_compatible_with, 326 features = features, 327 tags = tags, 328 api_surface = api_surface, 329 included_in_ndk = included_in_ndk, 330 ) 331 332 # Create a header library target for this API surface (ModuleLibApi) 333 # The external @api_surfaces repository will contain an alias to this header library. 334 cc_library_headers( 335 name = "%s_%s_headers" % (name, MODULE_LIB_API), 336 export_includes = export_includes, 337 deps = deps, # Necessary for exporting headers that might exist in a different directory (e.g. libEGL) 338 ) 339 340 native.alias( 341 # Use _ as the seperator of name and version in alias. So there is no 342 # duplicated name if "current" is one of the versions of a library. 343 name = name + "_current", 344 actual = name + "-" + "current", 345 tags = tags, 346 ) 347