• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (C) 2021 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("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
16load("//build/bazel/rules/abi:abi_dump.bzl", "AbiDiffInfo", "abi_dump")
17load(
18    ":cc_library_common.bzl",
19    "CcAndroidMkInfo",
20    "add_lists_defaulting_to_none",
21    "parse_sdk_version",
22    "sanitizer_deps",
23    "system_dynamic_deps_defaults",
24)
25load(":cc_library_static.bzl", "cc_library_static")
26load(":clang_tidy.bzl", "collect_deps_clang_tidy_info")
27load(
28    ":composed_transitions.bzl",
29    "lto_and_fdo_profile_incoming_transition",
30)
31load(
32    ":fdo_profile_transitions.bzl",
33    "FDO_PROFILE_ATTR_KEY",
34)
35load(":generate_toc.bzl", "CcTocInfo", "generate_toc")
36load(":lto_transitions.bzl", "lto_deps_transition")
37load(":stl.bzl", "stl_info_from_attr")
38load(":stripped_cc_common.bzl", "CcUnstrippedInfo", "stripped_shared_library")
39load(":versioned_cc_common.bzl", "versioned_shared_library")
40
41def cc_library_shared(
42        name,
43        suffix = "",
44        # Common arguments between shared_root and the shared library
45        features = [],
46        dynamic_deps = [],
47        implementation_dynamic_deps = [],
48        linkopts = [],
49        target_compatible_with = [],
50        # Ultimately _static arguments for shared_root production
51        srcs = [],
52        srcs_c = [],
53        srcs_as = [],
54        copts = [],
55        cppflags = [],
56        conlyflags = [],
57        asflags = [],
58        hdrs = [],
59        implementation_deps = [],
60        deps = [],
61        whole_archive_deps = [],
62        implementation_whole_archive_deps = [],
63        system_dynamic_deps = None,
64        runtime_deps = [],
65        export_includes = [],
66        export_absolute_includes = [],
67        export_system_includes = [],
68        local_includes = [],
69        absolute_includes = [],
70        rtti = False,
71        stl = "",
72        cpp_std = "",
73        c_std = "",
74        additional_linker_inputs = None,
75
76        # Purely _shared arguments
77        strip = {},
78
79        # TODO(b/202299295): Handle data attribute.
80        data = [],  # @unused
81        use_version_lib = False,
82        stubs_symbol_file = None,
83        inject_bssl_hash = False,
84        sdk_version = "",  # @unused
85        min_sdk_version = "",
86        abi_checker_enabled = None,
87        abi_checker_symbol_file = None,
88        abi_checker_exclude_symbol_versions = [],
89        abi_checker_exclude_symbol_tags = [],
90        abi_checker_check_all_apis = False,
91        abi_checker_diff_flags = [],
92        native_coverage = True,
93        tags = [],
94        fdo_profile = None,
95        tidy = None,
96        tidy_checks = None,
97        tidy_checks_as_errors = None,
98        tidy_flags = None,
99        tidy_disabled_srcs = None,
100        tidy_timeout_srcs = None,
101        tidy_gen_header_filter = None,
102        **kwargs):
103    "Bazel macro to correspond with the cc_library_shared Soong module."
104
105    # There exist modules named 'libtest_missing_symbol' and
106    # 'libtest_missing_symbol_root'. Ensure that that the target suffixes are
107    # sufficiently unique.
108    shared_root_name = name + "__internal_root"
109    unstripped_name = name + "_unstripped"
110    stripped_name = name + "_stripped"
111
112    if system_dynamic_deps == None:
113        system_dynamic_deps = system_dynamic_deps_defaults
114
115    if min_sdk_version:
116        features = features + parse_sdk_version(min_sdk_version) + ["-sdk_version_default"]
117
118    if fdo_profile != None:
119        # FIXME(b/261609769): This is a temporary workaround to add link flags
120        # that requires the path to fdo profile.
121        # This workaround is error-prone because it assumes all the fdo_profile
122        # targets are created in a specific way (e.g. fdo_profile target named foo
123        # uses an afdo profile file named foo.afdo in the same folder).
124        fdo_profile_path = fdo_profile + ".afdo"
125        linkopts = linkopts + [
126            "-funique-internal-linkage-names",
127            "-fprofile-sample-accurate",
128            "-fprofile-sample-use=$(location {})".format(fdo_profile_path),
129            "-Wl,-mllvm,-no-warn-sample-unused=true",
130        ]
131        if additional_linker_inputs != None:
132            additional_linker_inputs = additional_linker_inputs + [fdo_profile_path]
133        else:
134            additional_linker_inputs = [fdo_profile_path]
135
136    stl_info = stl_info_from_attr(stl, True)
137    linkopts = linkopts + stl_info.linkopts
138    copts = copts + stl_info.cppflags
139
140    extra_archive_deps = []
141    if not native_coverage:
142        features = features + ["-coverage"]
143    else:
144        features = features + select({
145            "//build/bazel/rules/cc:android_coverage_lib_flag": ["android_coverage_lib"],
146            "//conditions:default": [],
147        })
148
149        # TODO(b/233660582): deal with the cases where the default lib shouldn't be used
150        extra_archive_deps = select({
151            "//build/bazel/rules/cc:android_coverage_lib_flag": ["//system/extras/toolchain-extras:libprofile-clang-extras"],
152            "//conditions:default": [],
153        })
154
155    # The static library at the root of the shared library.
156    # This may be distinct from the static version of the library if e.g.
157    # the static-variant srcs are different than the shared-variant srcs.
158    cc_library_static(
159        name = shared_root_name,
160        hdrs = hdrs,
161        srcs = srcs,
162        srcs_c = srcs_c,
163        srcs_as = srcs_as,
164        copts = copts,
165        cppflags = cppflags,
166        conlyflags = conlyflags,
167        asflags = asflags,
168        export_includes = export_includes,
169        export_absolute_includes = export_absolute_includes,
170        export_system_includes = export_system_includes,
171        local_includes = local_includes,
172        absolute_includes = absolute_includes,
173        rtti = rtti,
174        stl = "none",
175        cpp_std = cpp_std,
176        c_std = c_std,
177        dynamic_deps = dynamic_deps,
178        implementation_deps = implementation_deps + stl_info.static_deps,
179        implementation_dynamic_deps = implementation_dynamic_deps + stl_info.shared_deps,
180        implementation_whole_archive_deps = implementation_whole_archive_deps,
181        system_dynamic_deps = system_dynamic_deps,
182        deps = deps,
183        whole_archive_deps = whole_archive_deps + extra_archive_deps,
184        features = features,
185        target_compatible_with = target_compatible_with,
186        tags = ["manual"],
187        native_coverage = native_coverage,
188        tidy = tidy,
189        tidy_checks = tidy_checks,
190        tidy_checks_as_errors = tidy_checks_as_errors,
191        tidy_flags = tidy_flags,
192        tidy_disabled_srcs = tidy_disabled_srcs,
193        tidy_timeout_srcs = tidy_timeout_srcs,
194        tidy_gen_header_filter = tidy_gen_header_filter,
195    )
196
197    sanitizer_deps_name = name + "_sanitizer_deps"
198    sanitizer_deps(
199        name = sanitizer_deps_name,
200        dep = shared_root_name,
201        tags = ["manual"],
202    )
203
204    # implementation_deps and deps are to be linked into the shared library via
205    # --no-whole-archive. In order to do so, they need to be dependencies of
206    # a "root" of the cc_shared_library, but may not be roots themselves.
207    # Below we define stub roots (which themselves have no srcs) in order to facilitate
208    # this.
209    imp_deps_stub = name + "_implementation_deps"
210    deps_stub = name + "_deps"
211    native.cc_library(
212        name = imp_deps_stub,
213        deps = (
214            implementation_deps +
215            implementation_whole_archive_deps +
216            stl_info.static_deps +
217            implementation_dynamic_deps +
218            system_dynamic_deps +
219            stl_info.shared_deps +
220            [sanitizer_deps_name]
221        ),
222        target_compatible_with = target_compatible_with,
223        tags = ["manual"],
224    )
225    native.cc_library(
226        name = deps_stub,
227        deps = deps + dynamic_deps,
228        target_compatible_with = target_compatible_with,
229        tags = ["manual"],
230    )
231
232    shared_dynamic_deps = add_lists_defaulting_to_none(
233        dynamic_deps,
234        system_dynamic_deps,
235        implementation_dynamic_deps,
236        stl_info.shared_deps,
237    )
238
239    soname = name + suffix + ".so"
240    soname_flag = "-Wl,-soname," + soname
241
242    native.cc_shared_library(
243        name = unstripped_name,
244        user_link_flags = linkopts + [soname_flag],
245        dynamic_deps = shared_dynamic_deps,
246        additional_linker_inputs = additional_linker_inputs,
247        deps = [shared_root_name, imp_deps_stub, deps_stub],
248        features = features,
249        target_compatible_with = target_compatible_with,
250        tags = ["manual"],
251        **kwargs
252    )
253
254    hashed_name = name + "_hashed"
255    _bssl_hash_injection(
256        name = hashed_name,
257        src = unstripped_name,
258        inject_bssl_hash = inject_bssl_hash,
259        tags = ["manual"],
260    )
261
262    versioned_name = name + "_versioned"
263    versioned_shared_library(
264        name = versioned_name,
265        src = hashed_name,
266        stamp_build_number = use_version_lib,
267        tags = ["manual"],
268    )
269
270    stripped_shared_library(
271        name = stripped_name,
272        src = versioned_name,
273        target_compatible_with = target_compatible_with,
274        tags = ["manual"],
275        **strip
276    )
277
278    # The logic here is based on the shouldCreateSourceAbiDumpForLibrary() in sabi.go
279    # abi_root is used to control if abi_dump aspects should be run on the static
280    # deps because there is no way to control the aspects directly from the rule.
281    abi_root = shared_root_name
282
283    # explicitly disabled
284    if abi_checker_enabled == False:
285        abi_root = None
286    elif abi_checker_enabled == True or stubs_symbol_file:
287        # The logic comes from here:
288        # https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/library.go;l=2288;drc=73feba33308bf9432aea43e069ed24a2f0312f1b
289        if not abi_checker_symbol_file and stubs_symbol_file:
290            abi_checker_symbol_file = stubs_symbol_file
291    else:
292        abi_root = None
293
294    abi_checker_explicitly_disabled = abi_checker_enabled == False
295
296    abi_dump_name = name + "_abi_dump"
297    abi_dump(
298        name = abi_dump_name,
299        shared = stripped_name,
300        root = abi_root,
301        soname = soname,
302        has_stubs = stubs_symbol_file != None,
303        enabled = abi_checker_enabled,
304        explicitly_disabled = abi_checker_explicitly_disabled,
305        symbol_file = abi_checker_symbol_file,
306        exclude_symbol_versions = abi_checker_exclude_symbol_versions,
307        exclude_symbol_tags = abi_checker_exclude_symbol_tags,
308        check_all_apis = abi_checker_check_all_apis,
309        diff_flags = abi_checker_diff_flags,
310        tags = ["manual"],
311    )
312
313    _cc_library_shared_proxy(
314        name = name,
315        shared = stripped_name,
316        shared_debuginfo = unstripped_name,
317        deps = [shared_root_name],
318        features = features,
319        output_file = soname,
320        target_compatible_with = target_compatible_with,
321        has_stubs = stubs_symbol_file != None,
322        runtime_deps = runtime_deps,
323        abi_dump = abi_dump_name,
324        fdo_profile = fdo_profile,
325        tags = tags,
326    )
327
328def _create_dynamic_library_linker_input_for_file(ctx, shared_info, output):
329    cc_toolchain = find_cpp_toolchain(ctx)
330    feature_configuration = cc_common.configure_features(
331        ctx = ctx,
332        cc_toolchain = cc_toolchain,
333    )
334
335    new_library_to_link = cc_common.create_library_to_link(
336        actions = ctx.actions,
337        dynamic_library = output,
338        feature_configuration = feature_configuration,
339        cc_toolchain = cc_toolchain,
340    )
341
342    new_linker_input = cc_common.create_linker_input(
343        owner = shared_info.linker_input.owner,
344        libraries = depset([new_library_to_link]),
345    )
346    return new_linker_input
347
348def _correct_cc_shared_library_linking(ctx, shared_info, new_output, static_root):
349    # we may have done some post-processing of the shared library
350    # replace the linker_input that has not been post-processed with the
351    # library that has been post-processed
352    new_linker_input = _create_dynamic_library_linker_input_for_file(ctx, shared_info, new_output)
353
354    # only export the static internal root, we include other libraries as roots
355    # that should be linked as alwayslink; however, if they remain as exports,
356    # they will be linked dynamically, not statically when they should be
357    static_root_label = str(static_root.label)
358    if static_root_label not in shared_info.exports:
359        fail("Expected %s in exports %s" % (static_root_label, shared_info.exports))
360    exports = [static_root_label]
361
362    return CcSharedLibraryInfo(
363        dynamic_deps = shared_info.dynamic_deps,
364        exports = exports,
365        link_once_static_libs = shared_info.link_once_static_libs,
366        linker_input = new_linker_input,
367        preloaded_deps = shared_info.preloaded_deps,
368    )
369
370CcStubLibrariesInfo = provider(
371    fields = {
372        "has_stubs": "If the shared library has stubs",
373    },
374)
375
376# A provider to propagate shared library output artifacts, primarily useful
377# for root level querying in Soong-Bazel mixed builds.
378# Ideally, it would be preferable to reuse the existing native
379# CcSharedLibraryInfo provider, but that provider requires that shared library
380# artifacts are wrapped in a linker input. Artifacts retrievable from this linker
381# input are symlinks to the original artifacts, which is problematic when
382# other dependencies expect a real file.
383CcSharedLibraryOutputInfo = provider(
384    fields = {
385        "output_file": "A single .so file, produced by this target.",
386    },
387)
388
389def _cc_library_shared_proxy_impl(ctx):
390    # Using a "deps" label_list instead of a single mandatory label attribute
391    # is a hack to support aspect propagation of graph_aspect of the native
392    # cc_shared_library. The aspect will only be applied and propagated along
393    # a label_list attribute named "deps".
394    if len(ctx.attr.deps) != 1:
395        fail("Exactly one 'deps' must be specified for cc_library_shared_proxy")
396    root_files = ctx.attr.deps[0][DefaultInfo].files.to_list()
397    shared_files = ctx.attr.shared[0][DefaultInfo].files.to_list()
398    shared_debuginfo = ctx.attr.shared_debuginfo[0][DefaultInfo].files.to_list()
399    if len(shared_files) != 1 or len(shared_debuginfo) != 1:
400        fail("Expected only one shared library file and one debuginfo file for it")
401
402    shared_lib = shared_files[0]
403    abi_diff_files = ctx.attr.abi_dump[AbiDiffInfo].diff_files.to_list()
404
405    # Copy the output instead of symlinking. This is because this output
406    # can be directly installed into a system image; this installation treats
407    # symlinks differently from real files (symlinks will be preserved relative
408    # to the image root).
409    ctx.actions.run_shell(
410        # We need to add the abi dump files to the inputs of this copy action even
411        # though they are not used, otherwise not all the abi dump files will be
412        # created. For example, for b build
413        # packages/modules/adb/pairing_connection:libadb_pairing_server, only
414        # libadb_pairing_server.so.lsdump will be created, libadb_pairing_auth.so.lsdump
415        # and libadb_pairing_connection.so.lsdump will not be. The reason is that
416        # even though libadb_pairing server depends on libadb_pairing_auth and
417        # libadb_pairing_connection, the abi dump files are not explicitly used
418        # by libadb_pairing_server, so bazel won't bother generating them.
419        inputs = depset(direct = [shared_lib] + abi_diff_files),
420        outputs = [ctx.outputs.output_file],
421        command = "cp -f %s %s" % (shared_lib.path, ctx.outputs.output_file.path),
422        mnemonic = "CopyFile",
423        progress_message = "Copying files",
424        use_default_shell_env = True,
425    )
426
427    toc_info = generate_toc(ctx, ctx.attr.name, ctx.outputs.output_file)
428
429    files = root_files + [ctx.outputs.output_file, toc_info.toc] + abi_diff_files
430
431    return [
432        DefaultInfo(
433            files = depset(direct = files),
434            runfiles = ctx.runfiles(files = [ctx.outputs.output_file]),
435        ),
436        _correct_cc_shared_library_linking(ctx, ctx.attr.shared[0][CcSharedLibraryInfo], ctx.outputs.output_file, ctx.attr.deps[0]),
437        toc_info,
438        # The _only_ linker_input is the statically linked root itself. We need to propagate this
439        # as cc_shared_library identifies which libraries can be linked dynamically based on the
440        # linker_inputs of the roots
441        ctx.attr.deps[0][CcInfo],
442        ctx.attr.deps[0][CcAndroidMkInfo],
443        CcStubLibrariesInfo(has_stubs = ctx.attr.has_stubs),
444        ctx.attr.shared[0][OutputGroupInfo],
445        CcSharedLibraryOutputInfo(output_file = ctx.outputs.output_file),
446        CcUnstrippedInfo(unstripped = shared_debuginfo[0]),
447        ctx.attr.abi_dump[AbiDiffInfo],
448        collect_deps_clang_tidy_info(ctx),
449    ]
450
451_cc_library_shared_proxy = rule(
452    implementation = _cc_library_shared_proxy_impl,
453    # Incoming transition to override outgoing transition from rdep
454    cfg = lto_and_fdo_profile_incoming_transition,
455    attrs = {
456        FDO_PROFILE_ATTR_KEY: attr.label(),
457        "shared": attr.label(
458            mandatory = True,
459            providers = [CcSharedLibraryInfo],
460            cfg = lto_deps_transition,
461        ),
462        "shared_debuginfo": attr.label(
463            mandatory = True,
464            cfg = lto_deps_transition,
465        ),
466        # "deps" should be a single element: the root target of the shared library.
467        # See _cc_library_shared_proxy_impl comment for explanation.
468        "deps": attr.label_list(
469            mandatory = True,
470            providers = [CcInfo],
471            cfg = lto_deps_transition,
472        ),
473        "output_file": attr.output(mandatory = True),
474        "has_stubs": attr.bool(default = False),
475        "runtime_deps": attr.label_list(
476            providers = [CcInfo],
477            doc = "Deps that should be installed along with this target. Read by the apex cc aspect.",
478        ),
479        "abi_dump": attr.label(providers = [AbiDiffInfo]),
480        "_allowlist_function_transition": attr.label(
481            default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
482        ),
483        "androidmk_static_deps": attr.label_list(
484            providers = [CcInfo],
485            doc = "All the whole archive deps of the lib. This is used to propagate" +
486                  " information to AndroidMk about LOCAL_STATIC_LIBRARIES.",
487        ),
488        "androidmk_whole_archive_deps": attr.label_list(
489            providers = [CcInfo],
490            doc = "All the whole archive deps of the lib. This is used to propagate" +
491                  " information to AndroidMk about LOCAL_WHOLE_STATIC_LIBRARIES.",
492        ),
493        "androidmk_dynamic_deps": attr.label_list(
494            providers = [CcInfo],
495            doc = "All the dynamic deps of the lib. This is used to propagate" +
496                  " information to AndroidMk about LOCAL_SHARED_LIBRARIES.",
497        ),
498        "_toc_script": attr.label(
499            cfg = "exec",
500            executable = True,
501            allow_single_file = True,
502            default = "//build/soong/scripts:toc.sh",
503        ),
504        "_readelf": attr.label(
505            cfg = "exec",
506            executable = True,
507            allow_single_file = True,
508            default = "//prebuilts/clang/host/linux-x86:llvm-readelf",
509        ),
510    },
511    provides = [CcAndroidMkInfo, CcInfo, CcTocInfo],
512    fragments = ["cpp"],
513    toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
514)
515
516def _bssl_hash_injection_impl(ctx):
517    if len(ctx.files.src) != 1:
518        fail("Expected only one shared library file")
519
520    hashed_file = ctx.files.src[0]
521    if ctx.attr.inject_bssl_hash:
522        hashed_file = ctx.actions.declare_file("lib" + ctx.attr.name + ".so")
523        args = ctx.actions.args()
524        args.add_all(["-in-object", ctx.files.src[0]])
525        args.add_all(["-o", hashed_file])
526
527        ctx.actions.run(
528            inputs = ctx.files.src,
529            outputs = [hashed_file],
530            executable = ctx.executable._bssl_inject_hash,
531            arguments = [args],
532            tools = [ctx.executable._bssl_inject_hash],
533            mnemonic = "BsslInjectHash",
534        )
535
536    return [
537        DefaultInfo(files = depset([hashed_file])),
538        ctx.attr.src[CcSharedLibraryInfo],
539        ctx.attr.src[OutputGroupInfo],
540    ]
541
542_bssl_hash_injection = rule(
543    implementation = _bssl_hash_injection_impl,
544    attrs = {
545        "src": attr.label(
546            mandatory = True,
547            # TODO(b/217908237): reenable allow_single_file
548            # allow_single_file = True,
549            providers = [CcSharedLibraryInfo],
550        ),
551        "inject_bssl_hash": attr.bool(
552            default = False,
553            doc = "Whether inject BSSL hash",
554        ),
555        "_bssl_inject_hash": attr.label(
556            cfg = "exec",
557            doc = "The BSSL hash injection tool.",
558            executable = True,
559            default = "//prebuilts/build-tools:linux-x86/bin/bssl_inject_hash",
560            allow_single_file = True,
561        ),
562    },
563)
564