xref: /aosp_15_r20/build/bazel/rules/cc/cc_stub_library.bzl (revision 7594170e27e0732bc44b93d1440d87a54b6ffe7c)
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