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