unplugged-system/build/bazel/rules/cc/cc_library_shared.bzl

564 lines
21 KiB
Python

# Copyright (C) 2021 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
load("//build/bazel/rules/abi:abi_dump.bzl", "AbiDiffInfo", "abi_dump")
load(
":cc_library_common.bzl",
"CcAndroidMkInfo",
"add_lists_defaulting_to_none",
"parse_sdk_version",
"sanitizer_deps",
"system_dynamic_deps_defaults",
)
load(":cc_library_static.bzl", "cc_library_static")
load(":clang_tidy.bzl", "collect_deps_clang_tidy_info")
load(
":composed_transitions.bzl",
"lto_and_fdo_profile_incoming_transition",
)
load(
":fdo_profile_transitions.bzl",
"FDO_PROFILE_ATTR_KEY",
)
load(":generate_toc.bzl", "CcTocInfo", "generate_toc")
load(":lto_transitions.bzl", "lto_deps_transition")
load(":stl.bzl", "stl_info_from_attr")
load(":stripped_cc_common.bzl", "CcUnstrippedInfo", "stripped_shared_library")
load(":versioned_cc_common.bzl", "versioned_shared_library")
def cc_library_shared(
name,
suffix = "",
# Common arguments between shared_root and the shared library
features = [],
dynamic_deps = [],
implementation_dynamic_deps = [],
linkopts = [],
target_compatible_with = [],
# Ultimately _static arguments for shared_root production
srcs = [],
srcs_c = [],
srcs_as = [],
copts = [],
cppflags = [],
conlyflags = [],
asflags = [],
hdrs = [],
implementation_deps = [],
deps = [],
whole_archive_deps = [],
implementation_whole_archive_deps = [],
system_dynamic_deps = None,
runtime_deps = [],
export_includes = [],
export_absolute_includes = [],
export_system_includes = [],
local_includes = [],
absolute_includes = [],
rtti = False,
stl = "",
cpp_std = "",
c_std = "",
additional_linker_inputs = None,
# Purely _shared arguments
strip = {},
# TODO(b/202299295): Handle data attribute.
data = [], # @unused
use_version_lib = False,
stubs_symbol_file = None,
inject_bssl_hash = False,
sdk_version = "", # @unused
min_sdk_version = "",
abi_checker_enabled = None,
abi_checker_symbol_file = None,
abi_checker_exclude_symbol_versions = [],
abi_checker_exclude_symbol_tags = [],
abi_checker_check_all_apis = False,
abi_checker_diff_flags = [],
native_coverage = True,
tags = [],
fdo_profile = None,
tidy = None,
tidy_checks = None,
tidy_checks_as_errors = None,
tidy_flags = None,
tidy_disabled_srcs = None,
tidy_timeout_srcs = None,
tidy_gen_header_filter = None,
**kwargs):
"Bazel macro to correspond with the cc_library_shared Soong module."
# There exist modules named 'libtest_missing_symbol' and
# 'libtest_missing_symbol_root'. Ensure that that the target suffixes are
# sufficiently unique.
shared_root_name = name + "__internal_root"
unstripped_name = name + "_unstripped"
stripped_name = name + "_stripped"
if system_dynamic_deps == None:
system_dynamic_deps = system_dynamic_deps_defaults
if min_sdk_version:
features = features + parse_sdk_version(min_sdk_version) + ["-sdk_version_default"]
if fdo_profile != None:
# FIXME(b/261609769): This is a temporary workaround to add link flags
# that requires the path to fdo profile.
# This workaround is error-prone because it assumes all the fdo_profile
# targets are created in a specific way (e.g. fdo_profile target named foo
# uses an afdo profile file named foo.afdo in the same folder).
fdo_profile_path = fdo_profile + ".afdo"
linkopts = linkopts + [
"-funique-internal-linkage-names",
"-fprofile-sample-accurate",
"-fprofile-sample-use=$(location {})".format(fdo_profile_path),
"-Wl,-mllvm,-no-warn-sample-unused=true",
]
if additional_linker_inputs != None:
additional_linker_inputs = additional_linker_inputs + [fdo_profile_path]
else:
additional_linker_inputs = [fdo_profile_path]
stl_info = stl_info_from_attr(stl, True)
linkopts = linkopts + stl_info.linkopts
copts = copts + stl_info.cppflags
extra_archive_deps = []
if not native_coverage:
features = features + ["-coverage"]
else:
features = features + select({
"//build/bazel/rules/cc:android_coverage_lib_flag": ["android_coverage_lib"],
"//conditions:default": [],
})
# TODO(b/233660582): deal with the cases where the default lib shouldn't be used
extra_archive_deps = select({
"//build/bazel/rules/cc:android_coverage_lib_flag": ["//system/extras/toolchain-extras:libprofile-clang-extras"],
"//conditions:default": [],
})
# The static library at the root of the shared library.
# This may be distinct from the static version of the library if e.g.
# the static-variant srcs are different than the shared-variant srcs.
cc_library_static(
name = shared_root_name,
hdrs = hdrs,
srcs = srcs,
srcs_c = srcs_c,
srcs_as = srcs_as,
copts = copts,
cppflags = cppflags,
conlyflags = conlyflags,
asflags = asflags,
export_includes = export_includes,
export_absolute_includes = export_absolute_includes,
export_system_includes = export_system_includes,
local_includes = local_includes,
absolute_includes = absolute_includes,
rtti = rtti,
stl = "none",
cpp_std = cpp_std,
c_std = c_std,
dynamic_deps = dynamic_deps,
implementation_deps = implementation_deps + stl_info.static_deps,
implementation_dynamic_deps = implementation_dynamic_deps + stl_info.shared_deps,
implementation_whole_archive_deps = implementation_whole_archive_deps,
system_dynamic_deps = system_dynamic_deps,
deps = deps,
whole_archive_deps = whole_archive_deps + extra_archive_deps,
features = features,
target_compatible_with = target_compatible_with,
tags = ["manual"],
native_coverage = native_coverage,
tidy = tidy,
tidy_checks = tidy_checks,
tidy_checks_as_errors = tidy_checks_as_errors,
tidy_flags = tidy_flags,
tidy_disabled_srcs = tidy_disabled_srcs,
tidy_timeout_srcs = tidy_timeout_srcs,
tidy_gen_header_filter = tidy_gen_header_filter,
)
sanitizer_deps_name = name + "_sanitizer_deps"
sanitizer_deps(
name = sanitizer_deps_name,
dep = shared_root_name,
tags = ["manual"],
)
# implementation_deps and deps are to be linked into the shared library via
# --no-whole-archive. In order to do so, they need to be dependencies of
# a "root" of the cc_shared_library, but may not be roots themselves.
# Below we define stub roots (which themselves have no srcs) in order to facilitate
# this.
imp_deps_stub = name + "_implementation_deps"
deps_stub = name + "_deps"
native.cc_library(
name = imp_deps_stub,
deps = (
implementation_deps +
implementation_whole_archive_deps +
stl_info.static_deps +
implementation_dynamic_deps +
system_dynamic_deps +
stl_info.shared_deps +
[sanitizer_deps_name]
),
target_compatible_with = target_compatible_with,
tags = ["manual"],
)
native.cc_library(
name = deps_stub,
deps = deps + dynamic_deps,
target_compatible_with = target_compatible_with,
tags = ["manual"],
)
shared_dynamic_deps = add_lists_defaulting_to_none(
dynamic_deps,
system_dynamic_deps,
implementation_dynamic_deps,
stl_info.shared_deps,
)
soname = name + suffix + ".so"
soname_flag = "-Wl,-soname," + soname
native.cc_shared_library(
name = unstripped_name,
user_link_flags = linkopts + [soname_flag],
dynamic_deps = shared_dynamic_deps,
additional_linker_inputs = additional_linker_inputs,
deps = [shared_root_name, imp_deps_stub, deps_stub],
features = features,
target_compatible_with = target_compatible_with,
tags = ["manual"],
**kwargs
)
hashed_name = name + "_hashed"
_bssl_hash_injection(
name = hashed_name,
src = unstripped_name,
inject_bssl_hash = inject_bssl_hash,
tags = ["manual"],
)
versioned_name = name + "_versioned"
versioned_shared_library(
name = versioned_name,
src = hashed_name,
stamp_build_number = use_version_lib,
tags = ["manual"],
)
stripped_shared_library(
name = stripped_name,
src = versioned_name,
target_compatible_with = target_compatible_with,
tags = ["manual"],
**strip
)
# The logic here is based on the shouldCreateSourceAbiDumpForLibrary() in sabi.go
# abi_root is used to control if abi_dump aspects should be run on the static
# deps because there is no way to control the aspects directly from the rule.
abi_root = shared_root_name
# explicitly disabled
if abi_checker_enabled == False:
abi_root = None
elif abi_checker_enabled == True or stubs_symbol_file:
# The logic comes from here:
# https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/library.go;l=2288;drc=73feba33308bf9432aea43e069ed24a2f0312f1b
if not abi_checker_symbol_file and stubs_symbol_file:
abi_checker_symbol_file = stubs_symbol_file
else:
abi_root = None
abi_checker_explicitly_disabled = abi_checker_enabled == False
abi_dump_name = name + "_abi_dump"
abi_dump(
name = abi_dump_name,
shared = stripped_name,
root = abi_root,
soname = soname,
has_stubs = stubs_symbol_file != None,
enabled = abi_checker_enabled,
explicitly_disabled = abi_checker_explicitly_disabled,
symbol_file = abi_checker_symbol_file,
exclude_symbol_versions = abi_checker_exclude_symbol_versions,
exclude_symbol_tags = abi_checker_exclude_symbol_tags,
check_all_apis = abi_checker_check_all_apis,
diff_flags = abi_checker_diff_flags,
tags = ["manual"],
)
_cc_library_shared_proxy(
name = name,
shared = stripped_name,
shared_debuginfo = unstripped_name,
deps = [shared_root_name],
features = features,
output_file = soname,
target_compatible_with = target_compatible_with,
has_stubs = stubs_symbol_file != None,
runtime_deps = runtime_deps,
abi_dump = abi_dump_name,
fdo_profile = fdo_profile,
tags = tags,
)
def _create_dynamic_library_linker_input_for_file(ctx, shared_info, output):
cc_toolchain = find_cpp_toolchain(ctx)
feature_configuration = cc_common.configure_features(
ctx = ctx,
cc_toolchain = cc_toolchain,
)
new_library_to_link = cc_common.create_library_to_link(
actions = ctx.actions,
dynamic_library = output,
feature_configuration = feature_configuration,
cc_toolchain = cc_toolchain,
)
new_linker_input = cc_common.create_linker_input(
owner = shared_info.linker_input.owner,
libraries = depset([new_library_to_link]),
)
return new_linker_input
def _correct_cc_shared_library_linking(ctx, shared_info, new_output, static_root):
# we may have done some post-processing of the shared library
# replace the linker_input that has not been post-processed with the
# library that has been post-processed
new_linker_input = _create_dynamic_library_linker_input_for_file(ctx, shared_info, new_output)
# only export the static internal root, we include other libraries as roots
# that should be linked as alwayslink; however, if they remain as exports,
# they will be linked dynamically, not statically when they should be
static_root_label = str(static_root.label)
if static_root_label not in shared_info.exports:
fail("Expected %s in exports %s" % (static_root_label, shared_info.exports))
exports = [static_root_label]
return CcSharedLibraryInfo(
dynamic_deps = shared_info.dynamic_deps,
exports = exports,
link_once_static_libs = shared_info.link_once_static_libs,
linker_input = new_linker_input,
preloaded_deps = shared_info.preloaded_deps,
)
CcStubLibrariesInfo = provider(
fields = {
"has_stubs": "If the shared library has stubs",
},
)
# A provider to propagate shared library output artifacts, primarily useful
# for root level querying in Soong-Bazel mixed builds.
# Ideally, it would be preferable to reuse the existing native
# CcSharedLibraryInfo provider, but that provider requires that shared library
# artifacts are wrapped in a linker input. Artifacts retrievable from this linker
# input are symlinks to the original artifacts, which is problematic when
# other dependencies expect a real file.
CcSharedLibraryOutputInfo = provider(
fields = {
"output_file": "A single .so file, produced by this target.",
},
)
def _cc_library_shared_proxy_impl(ctx):
# Using a "deps" label_list instead of a single mandatory label attribute
# is a hack to support aspect propagation of graph_aspect of the native
# cc_shared_library. The aspect will only be applied and propagated along
# a label_list attribute named "deps".
if len(ctx.attr.deps) != 1:
fail("Exactly one 'deps' must be specified for cc_library_shared_proxy")
root_files = ctx.attr.deps[0][DefaultInfo].files.to_list()
shared_files = ctx.attr.shared[0][DefaultInfo].files.to_list()
shared_debuginfo = ctx.attr.shared_debuginfo[0][DefaultInfo].files.to_list()
if len(shared_files) != 1 or len(shared_debuginfo) != 1:
fail("Expected only one shared library file and one debuginfo file for it")
shared_lib = shared_files[0]
abi_diff_files = ctx.attr.abi_dump[AbiDiffInfo].diff_files.to_list()
# Copy the output instead of symlinking. This is because this output
# can be directly installed into a system image; this installation treats
# symlinks differently from real files (symlinks will be preserved relative
# to the image root).
ctx.actions.run_shell(
# We need to add the abi dump files to the inputs of this copy action even
# though they are not used, otherwise not all the abi dump files will be
# created. For example, for b build
# packages/modules/adb/pairing_connection:libadb_pairing_server, only
# libadb_pairing_server.so.lsdump will be created, libadb_pairing_auth.so.lsdump
# and libadb_pairing_connection.so.lsdump will not be. The reason is that
# even though libadb_pairing server depends on libadb_pairing_auth and
# libadb_pairing_connection, the abi dump files are not explicitly used
# by libadb_pairing_server, so bazel won't bother generating them.
inputs = depset(direct = [shared_lib] + abi_diff_files),
outputs = [ctx.outputs.output_file],
command = "cp -f %s %s" % (shared_lib.path, ctx.outputs.output_file.path),
mnemonic = "CopyFile",
progress_message = "Copying files",
use_default_shell_env = True,
)
toc_info = generate_toc(ctx, ctx.attr.name, ctx.outputs.output_file)
files = root_files + [ctx.outputs.output_file, toc_info.toc] + abi_diff_files
return [
DefaultInfo(
files = depset(direct = files),
runfiles = ctx.runfiles(files = [ctx.outputs.output_file]),
),
_correct_cc_shared_library_linking(ctx, ctx.attr.shared[0][CcSharedLibraryInfo], ctx.outputs.output_file, ctx.attr.deps[0]),
toc_info,
# The _only_ linker_input is the statically linked root itself. We need to propagate this
# as cc_shared_library identifies which libraries can be linked dynamically based on the
# linker_inputs of the roots
ctx.attr.deps[0][CcInfo],
ctx.attr.deps[0][CcAndroidMkInfo],
CcStubLibrariesInfo(has_stubs = ctx.attr.has_stubs),
ctx.attr.shared[0][OutputGroupInfo],
CcSharedLibraryOutputInfo(output_file = ctx.outputs.output_file),
CcUnstrippedInfo(unstripped = shared_debuginfo[0]),
ctx.attr.abi_dump[AbiDiffInfo],
collect_deps_clang_tidy_info(ctx),
]
_cc_library_shared_proxy = rule(
implementation = _cc_library_shared_proxy_impl,
# Incoming transition to override outgoing transition from rdep
cfg = lto_and_fdo_profile_incoming_transition,
attrs = {
FDO_PROFILE_ATTR_KEY: attr.label(),
"shared": attr.label(
mandatory = True,
providers = [CcSharedLibraryInfo],
cfg = lto_deps_transition,
),
"shared_debuginfo": attr.label(
mandatory = True,
cfg = lto_deps_transition,
),
# "deps" should be a single element: the root target of the shared library.
# See _cc_library_shared_proxy_impl comment for explanation.
"deps": attr.label_list(
mandatory = True,
providers = [CcInfo],
cfg = lto_deps_transition,
),
"output_file": attr.output(mandatory = True),
"has_stubs": attr.bool(default = False),
"runtime_deps": attr.label_list(
providers = [CcInfo],
doc = "Deps that should be installed along with this target. Read by the apex cc aspect.",
),
"abi_dump": attr.label(providers = [AbiDiffInfo]),
"_allowlist_function_transition": attr.label(
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
),
"androidmk_static_deps": attr.label_list(
providers = [CcInfo],
doc = "All the whole archive deps of the lib. This is used to propagate" +
" information to AndroidMk about LOCAL_STATIC_LIBRARIES.",
),
"androidmk_whole_archive_deps": attr.label_list(
providers = [CcInfo],
doc = "All the whole archive deps of the lib. This is used to propagate" +
" information to AndroidMk about LOCAL_WHOLE_STATIC_LIBRARIES.",
),
"androidmk_dynamic_deps": attr.label_list(
providers = [CcInfo],
doc = "All the dynamic deps of the lib. This is used to propagate" +
" information to AndroidMk about LOCAL_SHARED_LIBRARIES.",
),
"_toc_script": attr.label(
cfg = "exec",
executable = True,
allow_single_file = True,
default = "//build/soong/scripts:toc.sh",
),
"_readelf": attr.label(
cfg = "exec",
executable = True,
allow_single_file = True,
default = "//prebuilts/clang/host/linux-x86:llvm-readelf",
),
},
provides = [CcAndroidMkInfo, CcInfo, CcTocInfo],
fragments = ["cpp"],
toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
)
def _bssl_hash_injection_impl(ctx):
if len(ctx.files.src) != 1:
fail("Expected only one shared library file")
hashed_file = ctx.files.src[0]
if ctx.attr.inject_bssl_hash:
hashed_file = ctx.actions.declare_file("lib" + ctx.attr.name + ".so")
args = ctx.actions.args()
args.add_all(["-in-object", ctx.files.src[0]])
args.add_all(["-o", hashed_file])
ctx.actions.run(
inputs = ctx.files.src,
outputs = [hashed_file],
executable = ctx.executable._bssl_inject_hash,
arguments = [args],
tools = [ctx.executable._bssl_inject_hash],
mnemonic = "BsslInjectHash",
)
return [
DefaultInfo(files = depset([hashed_file])),
ctx.attr.src[CcSharedLibraryInfo],
ctx.attr.src[OutputGroupInfo],
]
_bssl_hash_injection = rule(
implementation = _bssl_hash_injection_impl,
attrs = {
"src": attr.label(
mandatory = True,
# TODO(b/217908237): reenable allow_single_file
# allow_single_file = True,
providers = [CcSharedLibraryInfo],
),
"inject_bssl_hash": attr.bool(
default = False,
doc = "Whether inject BSSL hash",
),
"_bssl_inject_hash": attr.label(
cfg = "exec",
doc = "The BSSL hash injection tool.",
executable = True,
default = "//prebuilts/build-tools:linux-x86/bin/bssl_inject_hash",
allow_single_file = True,
),
},
)