• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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