blob: 0849573bf0e62e5aeb08f07b56197be800d0013f [file] [log] [blame]
# Copyright 2020 The Bazel Authors. All rights reserved.
#
# 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.
"""cc_library Starlark implementation replacing native"""
load(":common/cc/cc_helper.bzl", "cc_helper")
load(":blaze/common/toplevel_aliases.bzl", "CcInfo", "cc_common", "cc_internal")
load(":common/cc/semantics.bzl", "semantics")
def _check_src_extension(file):
extension = "." + file.extension
if cc_helper.matches_extension(extension, ALLOWED_SRC_FILES) or cc_helper.is_shared_library_extension_valid(file.path):
return True
return False
def _check_srcs_extensions(ctx):
for src in ctx.attr.srcs:
if DefaultInfo in src:
files = src[DefaultInfo].files.to_list()
if len(files) == 1 and files[0].is_source:
if not _check_src_extension(files[0]) and not files[0].is_directory:
fail("source file '{}' is misplaced here".format(str(src.label)), attr = "srcs")
else:
at_least_one_good = False
for file in files:
if _check_src_extension(file) or file.is_directory:
at_least_one_good = True
break
if not at_least_one_good:
fail("'{}' does not produce any cc_library srcs files".format(str(src.label)), attr = "srcs")
def _cc_library_impl(ctx):
_check_srcs_extensions(ctx)
cpp_config = ctx.fragments.cpp
if (not cpp_config.experimental_cc_implementation_deps() and
len(ctx.attr.implementation_deps) > 0):
fail("requires --experimental_cc_implementation_deps", attr = "implementation_deps")
common = cc_internal.create_common(ctx = ctx)
common.report_invalid_options(ctx = ctx)
cc_toolchain = common.toolchain
cc_internal.init_make_variables(ctx = ctx, cc_toolchain = cc_toolchain)
feature_configuration = cc_common.configure_features(
ctx = ctx,
cc_toolchain = cc_toolchain,
requested_features = ctx.features,
unsupported_features = ctx.disabled_features,
)
precompiled_files = cc_helper.build_precompiled_files(ctx = ctx)
_check_no_repeated_srcs(ctx)
compilation_contexts = cc_helper.get_compilation_contexts_from_deps(ctx.attr.deps)
if not _is_stl(ctx.attr.tags):
compilation_contexts.append(ctx.attr._stl[CcInfo].compilation_context)
implementation_compilation_contexts = cc_helper.get_compilation_contexts_from_deps(ctx.attr.implementation_deps)
(compilation_context, srcs_compilation_outputs) = cc_common.compile(
actions = ctx.actions,
name = ctx.label.name,
cc_toolchain = cc_toolchain,
feature_configuration = feature_configuration,
user_compile_flags = common.copts,
defines = common.defines,
local_defines = common.local_defines,
loose_includes = common.loose_include_dirs,
system_includes = common.system_include_dirs,
copts_filter = common.copts_filter,
srcs = common.srcs,
private_hdrs = common.private_hdrs,
public_hdrs = common.public_hdrs,
code_coverage_enabled = cc_helper.is_code_coverage_enabled(ctx),
compilation_contexts = compilation_contexts,
implementation_compilation_contexts = implementation_compilation_contexts,
hdrs_checking_mode = cc_internal.determine_hdrs_checking_mode(ctx = ctx, semantics = semantics.get_semantics()),
grep_includes = ctx.executable._grep_includes,
textual_hdrs = ctx.files.textual_hdrs,
include_prefix = ctx.attr.include_prefix,
strip_include_prefix = ctx.attr.strip_include_prefix,
)
precompiled_objects = cc_common.create_compilation_outputs(
# TODO(bazel-team): Perhaps this should be objects, leaving as it is in the original
# Java code for now. Changing it might cause breakages.
objects = depset(precompiled_files[1]),
pic_objects = depset(precompiled_files[1]),
)
compilation_outputs = cc_common.merge_compilation_outputs(
compilation_outputs = [precompiled_objects, srcs_compilation_outputs],
)
supports_dynamic_linker = cc_common.is_enabled(
feature_configuration = feature_configuration,
feature_name = "supports_dynamic_linker",
)
create_dynamic_library = (not ctx.attr.linkstatic and
supports_dynamic_linker and
(not cc_helper.is_compilation_outputs_empty(compilation_outputs) or
cc_common.is_enabled(
feature_configuration = feature_configuration,
feature_name = "header_module_codegen",
)))
output_group_builder = {}
has_compilation_outputs = not cc_helper.is_compilation_outputs_empty(compilation_outputs)
linking_context = CcInfo().linking_context
empty_archive_linking_context = CcInfo().linking_context
is_google = True
linking_contexts = cc_helper.get_linking_contexts_from_deps(ctx.attr.deps)
linking_contexts.extend(cc_helper.get_linking_contexts_from_deps(ctx.attr.implementation_deps))
if ctx.file.linkstamp != None:
linkstamps = []
linkstamps.append(cc_internal.create_linkstamp(
actions = ctx.actions,
linkstamp = ctx.file.linkstamp,
compilation_context = compilation_context,
))
linkstamps_linker_input = cc_common.create_linker_input(
owner = ctx.label,
linkstamps = depset(linkstamps),
)
linkstamps_linking_context = cc_common.create_linking_context(
linker_inputs = depset([linkstamps_linker_input]),
)
linking_contexts.append(linkstamps_linking_context)
linking_outputs = None
if has_compilation_outputs:
(
linking_context,
linking_outputs,
) = cc_common.create_linking_context_from_compilation_outputs(
actions = ctx.actions,
name = ctx.label.name,
compilation_outputs = compilation_outputs,
cc_toolchain = cc_toolchain,
feature_configuration = feature_configuration,
additional_inputs = _filter_linker_scripts(ctx.files.deps),
linking_contexts = linking_contexts,
grep_includes = ctx.executable._grep_includes,
user_link_flags = common.linkopts,
alwayslink = ctx.attr.alwayslink,
disallow_dynamic_library = not create_dynamic_library,
)
elif is_google:
filename = "lib" + ctx.label.name
if cc_common.is_enabled(
feature_configuration = feature_configuration,
feature_name = "targets_windows",
):
filename += ".lib"
else:
filename += ".a"
archive = ctx.actions.declare_file(filename)
ctx.actions.write(archive, "")
library = cc_common.create_library_to_link(
actions = ctx.actions,
feature_configuration = feature_configuration,
cc_toolchain = cc_toolchain,
static_library = archive,
)
if len(precompiled_files) == 0:
empty_archive_linking_context = _build_linking_context_from_library(ctx, [library])
linking_outputs = struct(
library_to_link = library,
)
_add_linker_artifacts_output_groups(ctx, output_group_builder, linking_outputs)
precompiled_libraries = _convert_precompiled_libraries_to_library_to_link(
ctx,
cc_toolchain,
feature_configuration,
ctx.fragments.cpp.force_pic(),
precompiled_files,
)
if not cc_helper.is_compilation_outputs_empty(compilation_outputs):
_check_if_link_outputs_colliding_with_precompiled_files(
ctx,
linking_outputs,
precompiled_libraries,
)
precompiled_linking_context = _build_linking_context_from_library(ctx, precompiled_libraries)
contexts_to_merge = []
contexts_to_merge.append(linking_context)
if has_compilation_outputs:
contexts_to_merge.append(linking_context)
else:
user_link_flags = common.linkopts
linker_scripts = _filter_linker_scripts(ctx.files.deps)
if len(common.linkopts) > 0 or len(linker_scripts) > 0:
linker_input = cc_common.create_linker_input(
owner = ctx.label,
user_link_flags = depset(user_link_flags),
additional_inputs = depset(linker_scripts),
)
contexts_to_merge.append(cc_common.create_linking_context(linker_inputs = depset([linker_input])))
contexts_to_merge.extend(linking_contexts)
contexts_to_merge.append(precompiled_linking_context)
contexts_to_merge.append(empty_archive_linking_context)
linking_context = cc_common.merge_linking_contexts(
linking_contexts = contexts_to_merge,
)
libraries_to_link = _create_libraries_to_link_list(
linking_outputs.library_to_link,
precompiled_libraries,
)
cc_native_library_info = cc_internal.collect_native_cc_libraries(
deps = ctx.attr.deps,
libraries_to_link = libraries_to_link,
)
files_builder = []
if linking_outputs.library_to_link != None:
artifacts_to_build = linking_outputs.library_to_link
if artifacts_to_build.static_library != None:
files_builder.append(artifacts_to_build.static_library)
if artifacts_to_build.pic_static_library != None:
files_builder.append(artifacts_to_build.pic_static_library)
if not cc_common.is_enabled(
feature_configuration = feature_configuration,
feature_name = "targets_windows",
):
if artifacts_to_build.resolved_symlink_dynamic_library != None:
files_builder.append(artifacts_to_build.resolved_symlink_dynamic_library)
elif artifacts_to_build.dynamic_library != None:
files_builder.append(artifacts_to_build.dynamic_library)
if artifacts_to_build.resolved_symlink_interface_library != None:
files_builder.append(artifacts_to_build.resolved_symlink_interface_library)
elif artifacts_to_build.interface_library != None:
files_builder.append(artifacts_to_build.interface_library)
instrumented_object_files = []
instrumented_object_files.extend(compilation_outputs.objects)
instrumented_object_files.extend(compilation_outputs.pic_objects)
instrumented_files_info = common.instrumented_files_info(
files = instrumented_object_files,
with_base_line_coverage = True,
)
runfiles = ctx.runfiles()
for data_dep in ctx.attr.data:
runfiles = runfiles.merge(ctx.runfiles(transitive_files = data_dep[DefaultInfo].files))
runfiles = runfiles.merge(data_dep[DefaultInfo].data_runfiles)
for src in ctx.attr.srcs:
runfiles = runfiles.merge(src[DefaultInfo].default_runfiles)
for dep in ctx.attr.deps:
runfiles = runfiles.merge(dep[DefaultInfo].default_runfiles)
default_runfiles = ctx.runfiles(files = cc_helper.get_dynamic_libraries_for_runtime(linking_context, True))
default_runfiles = runfiles.merge(default_runfiles)
data_runfiles = ctx.runfiles(files = cc_helper.get_dynamic_libraries_for_runtime(linking_context, False))
data_runfiles = runfiles.merge(data_runfiles)
current_output_groups = cc_helper.build_output_groups_for_emitting_compile_providers(
compilation_outputs,
compilation_context,
ctx.fragments.cpp,
cc_toolchain,
feature_configuration,
ctx,
generate_hidden_top_level_group = True,
)
providers = []
providers.append(DefaultInfo(
files = depset(files_builder),
default_runfiles = default_runfiles,
data_runfiles = data_runfiles,
))
debug_context = cc_helper.merge_cc_debug_contexts(compilation_outputs, cc_helper.get_providers(ctx.attr.deps, CcInfo))
cc_info = CcInfo(
compilation_context = compilation_context,
linking_context = linking_context,
debug_context = debug_context,
cc_native_library_info = cc_native_library_info,
)
merged_output_groups = cc_helper.merge_output_groups(
[current_output_groups, output_group_builder],
)
providers.append(cc_info)
providers.append(OutputGroupInfo(**merged_output_groups))
providers.append(instrumented_files_info)
if ctx.fragments.cpp.enable_legacy_cc_provider():
# buildifier: disable=rule-impl-return
return struct(
cc = cc_internal.create_cc_provider(cc_info = cc_info),
providers = providers,
)
else:
return providers
def _add_linker_artifacts_output_groups(ctx, output_group_builder, linking_outputs):
archive_file = []
dynamic_library = []
lib = linking_outputs.library_to_link
if lib == None:
return
if lib.static_library != None:
archive_file.append(lib.static_library)
if lib.pic_static_library != None:
archive_file.append(lib.pic_static_library)
if lib.resolved_symlink_dynamic_library != None:
dynamic_library.append(lib.resolved_symlink_dynamic_library)
elif lib.dynamic_library != None:
dynamic_library.append(lib.dynamic_library)
if lib.resolved_symlink_interface_library != None:
dynamic_library.append(lib.resolved_symlink_interface_library)
elif lib.interface_library != None:
dynamic_library.append(lib.interface_library)
output_group_builder["archive"] = depset(archive_file)
output_group_builder["dynamic_library"] = depset(dynamic_library)
def _convert_precompiled_libraries_to_library_to_link(
ctx,
cc_toolchain,
feature_configuration,
force_pic,
precompiled_files):
static_libraries = _build_map_identifier_to_artifact(precompiled_files[2])
pic_static_libraries = _build_map_identifier_to_artifact(precompiled_files[3])
alwayslink_static_libraries = _build_map_identifier_to_artifact(precompiled_files[4])
alwayslink_pic_static_libraries = _build_map_identifier_to_artifact(precompiled_files[5])
dynamic_libraries = _build_map_identifier_to_artifact(precompiled_files[6])
libraries = []
identifiers_used = {}
static_libraries_it = []
static_libraries_it.extend(static_libraries.items())
static_libraries_it.extend(alwayslink_static_libraries.items())
for identifier, v in static_libraries_it:
static_library = None
pic_static_library = None
dynamic_library = None
interface_library = None
has_pic = identifier in pic_static_libraries
has_always_pic = identifier in alwayslink_pic_static_libraries
if has_pic or has_always_pic:
if has_pic:
pic_static_library = pic_static_libraries[identifier]
else:
pic_static_library = alwayslink_pic_static_libraries[identifier]
if not force_pic or not (has_pic or has_always_pic):
static_library = v
if identifier in dynamic_libraries:
dynamic_library = dynamic_libraries[identifier]
identifiers_used[identifier] = True
library = cc_common.create_library_to_link(
actions = ctx.actions,
feature_configuration = feature_configuration,
cc_toolchain = cc_toolchain,
static_library = static_library,
pic_static_library = pic_static_library,
dynamic_library = dynamic_library,
alwayslink = identifier in alwayslink_static_libraries,
)
libraries.append(library)
pic_static_libraries_it = []
pic_static_libraries_it.extend(pic_static_libraries.items())
pic_static_libraries_it.extend(alwayslink_pic_static_libraries.items())
for identifier, v in pic_static_libraries_it:
if identifier in identifiers_used:
continue
pic_static_library = v
if identifier in dynamic_libraries:
dynamic_library = dynamic_libraries[identifier]
identifiers_used[identifier] = True
library = cc_common.create_library_to_link(
actions = ctx.actions,
feature_configuration = feature_configuration,
cc_toolchain = cc_toolchain,
pic_static_library = pic_static_library,
alwayslink = identifier in alwayslink_static_libraries,
)
libraries.append(library)
for identifier, v in dynamic_libraries.items():
if identifier in identifiers_used:
continue
dynamic_library = dynamic_libraries[identifier]
library = cc_common.create_library_to_link(
actions = ctx.actions,
feature_configuration = feature_configuration,
cc_toolchain = cc_toolchain,
dynamic_library = dynamic_library,
)
libraries.append(library)
return libraries
def _build_map_identifier_to_artifact(artifacts):
libraries = {}
for artifact in artifacts:
identifier = _identifier_of_artifact(artifact)
if identifier in libraries:
fail(
"Trying to link twice a library with the same identifier '{}',".format(identifier) +
"files: {} and {}".format(
artifact.short_path,
libraries[identifier].short_path,
),
attr = "srcs",
)
libraries[identifier] = artifact
return libraries
def _identifier_of_artifact(artifact):
name = artifact.short_path
for pic_suffix in [".pic.a", ".nopic.a", ".pic.lo"]:
if name.endswith(pic_suffix):
return name[:len(name) - len(pic_suffix)]
return name[:len(name) - len(artifact.extension) - 1]
def _identifier_of_library(library):
if library.static_library != None:
return _identifier_of_artifact(library.static_library)
if library.pic_static_library != None:
return _identifier_of_artifact(library.pic_static_library)
if library.dynamic_library != None:
return _identifier_of_artifact(library.dynamic_library)
if library.interface_library != None:
return _identifier_of_artifact(library.interface_libary)
return None
def _build_linking_context_from_library(ctx, libraries):
if len(libraries) == 0:
return CcInfo().linking_context
linker_input = cc_common.create_linker_input(
owner = ctx.label,
libraries = depset(libraries),
)
linking_context = cc_common.create_linking_context(
linker_inputs = depset([linker_input]),
)
return linking_context
def _create_libraries_to_link_list(current_library, precompiled_libraries):
libraries = []
libraries.extend(precompiled_libraries)
if current_library != None:
libraries.append(current_library)
return libraries
def _filter_linker_scripts(files):
linker_scripts = []
for file in files:
extension = "." + file.extension
if extension in LINKER_SCRIPT:
linker_scripts.append(file)
return linker_scripts
def _check_if_link_outputs_colliding_with_precompiled_files(ctx, linking_outputs, precompiled_libraries):
identifier = _identifier_of_library(linking_outputs.library_to_link)
for precompiled_library in precompiled_libraries:
precompiled_library_identifier = _identifier_of_library(precompiled_library)
if precompiled_library_identifier == identifier:
fail("Can't put library with identifier '{}' into the srcs of a cc_library with".format(identifier) +
" the same name ({}) which also contains other code or objects to link".format(
ctx.label.name,
))
def _check_no_repeated_srcs(ctx):
seen = {}
for target in ctx.attr.srcs:
if DefaultInfo in target:
for file in target.files.to_list():
extension = "." + file.extension
if extension not in cc_helper.extensions.CC_HEADER:
if extension in cc_helper.extensions.CC_AND_OBJC:
if file.path in seen:
if seen[file.path] != target.label:
fail("Artifact '{}' is duplicated (through ".format(file.path) +
"'{}' and '{}')".format(str(seen[file.path]), str(target.label)))
seen[file.path] = target.label
ALLOWED_SRC_FILES = []
ALLOWED_SRC_FILES.extend(cc_helper.extensions.CC_SOURCE)
ALLOWED_SRC_FILES.extend(cc_helper.extensions.C_SOURCE)
ALLOWED_SRC_FILES.extend(cc_helper.extensions.CC_HEADER)
ALLOWED_SRC_FILES.extend(cc_helper.extensions.ASSESMBLER_WITH_C_PREPROCESSOR)
ALLOWED_SRC_FILES.extend(cc_helper.extensions.ASSEMBLER)
ALLOWED_SRC_FILES.extend(cc_helper.extensions.ARCHIVE)
ALLOWED_SRC_FILES.extend(cc_helper.extensions.PIC_ARCHIVE)
ALLOWED_SRC_FILES.extend(cc_helper.extensions.ALWAYSLINK_LIBRARY)
ALLOWED_SRC_FILES.extend(cc_helper.extensions.ALWAYSLINK_PIC_LIBRARY)
ALLOWED_SRC_FILES.extend(cc_helper.extensions.SHARED_LIBRARY)
SRCS_FOR_COMPILATION = []
SRCS_FOR_COMPILATION.extend(cc_helper.extensions.CC_SOURCE)
SRCS_FOR_COMPILATION.extend(cc_helper.extensions.C_SOURCE)
SRCS_FOR_COMPILATION.extend(cc_helper.extensions.ASSESMBLER_WITH_C_PREPROCESSOR)
SRCS_FOR_COMPILATION.extend(cc_helper.extensions.ASSEMBLER)
ALLOWED_SRC_FILES.extend(cc_helper.extensions.OBJECT_FILE)
ALLOWED_SRC_FILES.extend(cc_helper.extensions.PIC_OBJECT_FILE)
LINKER_SCRIPT = [".ld", ".lds", ".ldscript"]
PREPROCESSED_C = [".i"]
DEPS_ALLOWED_RULES = [
"genrule",
"cc_library",
"cc_inc_library",
"cc_embed_data",
"go_library",
"objc_library",
"cc_import",
"cc_proto_library",
]
def _is_stl(tags):
return "__CC_STL__" in tags
def _get_stl(tags):
stl = Label("@//third_party/stl")
if not _is_stl(tags):
return stl
return None
attrs = {
"srcs": attr.label_list(
allow_files = True,
flags = ["DIRECT_COMPILE_TIME_INPUT"],
),
"alwayslink": attr.bool(default = False),
"linkstatic": attr.bool(default = False),
"implementation_deps": attr.label_list(providers = [CcInfo], allow_files = False),
"hdrs": attr.label_list(
allow_files = True,
flags = ["ORDER_INDEPENDENT", "DIRECT_COMPILE_TIME_INPUT"],
),
"strip_include_prefix": attr.string(),
"include_prefix": attr.string(),
"textual_hdrs": attr.label_list(
allow_files = True,
flags = ["ORDER_INDEPENDENT", "DIRECT_COMPILE_TIME_INPUT"],
),
"linkstamp": attr.label(allow_single_file = True),
"win_def_file": attr.label(allow_files = [".def"]),
"linkopts": attr.string_list(),
"nocopts": attr.string(),
"hdrs_check": attr.string(default = cc_internal.default_hdrs_check_computed_default()),
"includes": attr.string_list(),
"defines": attr.string_list(),
"copts": attr.string_list(),
"_default_copts": attr.string_list(default = cc_internal.default_copts_computed_default()),
"local_defines": attr.string_list(),
"deps": attr.label_list(
providers = [CcInfo],
flags = ["SKIP_ANALYSIS_TIME_FILETYPE_CHECK"],
allow_files = LINKER_SCRIPT + PREPROCESSED_C,
allow_rules = DEPS_ALLOWED_RULES,
),
"data": attr.label_list(
allow_files = True,
flags = ["SKIP_CONSTRAINTS_OVERRIDE"],
),
"_stl": attr.label(default = _get_stl),
"_grep_includes": attr.label(
allow_files = True,
executable = True,
cfg = "exec",
default = Label("@" + semantics.get_repo() + "//tools/cpp:grep-includes"),
),
"_cc_toolchain": attr.label(default = "@//tools/cpp:current_cc_toolchain"),
}
attrs.update(semantics.get_licenses_attr())
attrs.update(semantics.get_distribs_attr())
attrs.update(semantics.get_loose_mode_in_hdrs_check_allowed_attr())
cc_library = rule(
implementation = _cc_library_impl,
attrs = attrs,
toolchains = ["@//tools/cpp:toolchain_type"],
fragments = ["cpp"] + semantics.additional_fragments(),
incompatible_use_toolchain_transition = True,
exec_groups = {
"cpp_link": exec_group(copy_from_rule = True),
},
compile_one_filetype = [".cc", ".h", ".c"],
)