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