blob: 83b21c45c448b1de5fe6ad2bddb763c32f9f840e [file] [edit]
# 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("//cc:find_cc_toolchain.bzl", "find_cc_toolchain")
load("//cc/common:cc_common.bzl", "cc_common")
load("//cc/common:cc_helper.bzl", "cc_helper")
load("//cc/common:cc_info.bzl", "CcInfo")
load("//cc/common:semantics.bzl", "semantics")
load(":function_providing_rule.bzl", "wrap_starlark_function")
def _cc_library_impl(ctx):
semantics.validate(ctx, "cc_library")
cc_helper.check_srcs_extensions(ctx, ALLOWED_SRC_FILES, "cc_library", True)
semantics.check_cc_shared_library_tags(ctx)
cc_toolchain = find_cc_toolchain(ctx)
cc_helper.report_invalid_options(cc_toolchain, ctx.fragments.cpp)
feature_configuration = cc_common.configure_features(
ctx = ctx,
cc_toolchain = cc_toolchain,
requested_features = ctx.features,
unsupported_features = ctx.disabled_features,
)
cc_helper.check_cpp_modules(ctx, feature_configuration)
precompiled_files = cc_helper.build_precompiled_files(ctx = ctx)
semantics.validate_attributes(ctx)
_check_no_repeated_srcs(ctx)
semantics.check_can_use_implementation_deps(ctx)
interface_deps = ctx.attr.deps + semantics.get_cc_runtimes(ctx, True)
runtimes_copts = semantics.get_cc_runtimes_copts(ctx)
compilation_contexts = cc_helper.get_compilation_contexts_from_deps(interface_deps)
implementation_compilation_contexts = cc_helper.get_compilation_contexts_from_deps(ctx.attr.implementation_deps)
additional_make_variable_substitutions = cc_helper.get_toolchain_global_make_variables(cc_toolchain, feature_configuration)
additional_make_variable_substitutions.update(cc_helper.get_cc_flags_make_variable(ctx, feature_configuration, cc_toolchain))
(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 = runtimes_copts + cc_helper.get_copts(ctx, feature_configuration, additional_make_variable_substitutions, attr = "copts"),
conly_flags = cc_helper.get_copts(ctx, feature_configuration, additional_make_variable_substitutions, attr = "conlyopts"),
cxx_flags = cc_helper.get_copts(ctx, feature_configuration, additional_make_variable_substitutions, attr = "cxxopts"),
defines = cc_helper.defines(ctx, additional_make_variable_substitutions),
local_defines = cc_helper.local_defines(ctx, additional_make_variable_substitutions) + cc_helper.get_local_defines_for_runfiles_lookup(ctx, ctx.attr.deps + ctx.attr.implementation_deps),
includes = cc_helper.include_dirs(ctx, additional_make_variable_substitutions),
local_includes = cc_helper.include_dirs(ctx, additional_make_variable_substitutions, attr = "local_includes"),
purpose = "cc_library-compile",
srcs = cc_helper.get_srcs(ctx),
module_interfaces = cc_helper.get_cpp_module_interfaces(ctx),
private_hdrs = cc_helper.get_private_hdrs(ctx),
public_hdrs = cc_helper.get_public_hdrs(ctx),
code_coverage_enabled = cc_helper.is_code_coverage_enabled(ctx),
compilation_contexts = compilation_contexts,
implementation_compilation_contexts = implementation_compilation_contexts,
textual_hdrs = ctx.files.textual_hdrs,
include_prefix = ctx.attr.include_prefix,
strip_include_prefix = ctx.attr.strip_include_prefix,
additional_inputs = ctx.files.additional_compiler_inputs,
)
precompiled_objects = cc_common.create_compilation_outputs(
objects = depset(precompiled_files[0]),
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
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 + semantics.get_cc_runtimes(ctx, True),
),
)
if ctx.file.linkstamp != None:
linkstamps = []
linkstamps.append(cc_common.create_linkstamp(
linkstamp = ctx.file.linkstamp,
headers = compilation_context.headers,
))
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)
if has_compilation_outputs:
dll_name_suffix = ""
additional_inputs = _filter_linker_scripts(ctx.files.deps) + ctx.files.additional_linker_inputs
link_variables = {}
is_windows_enabled = cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "targets_windows")
if is_windows_enabled:
dll_name_suffix = cc_helper.dll_hash_suffix(ctx, feature_configuration, ctx.fragments.cpp)
generated_def_file = None
generated_def_file = cc_helper.generate_def_file(
ctx,
ctx.file._def_parser,
compilation_outputs.objects,
ctx.label.name + dll_name_suffix,
cc_toolchain,
feature_configuration,
)
if generated_def_file != None:
output_group_builder["def_file"] = depset([generated_def_file])
win_def_file = cc_helper.get_windows_def_file_for_linking(ctx, ctx.file.win_def_file, generated_def_file, feature_configuration)
link_variables["def_file_path"] = win_def_file.path
additional_inputs.append(win_def_file)
(
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 = additional_inputs,
linking_contexts = linking_contexts,
user_link_flags = cc_helper.linkopts(ctx, additional_make_variable_substitutions, cc_toolchain),
alwayslink = ctx.attr.alwayslink,
disallow_dynamic_library = not create_dynamic_library,
linked_dll_name_suffix = dll_name_suffix,
variables_extension = link_variables,
)
else:
linking_outputs = struct(library_to_link = None)
_add_linker_artifacts_output_groups(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,
ctx.attr.alwayslink,
)
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 = cc_helper.build_linking_context_from_libraries(ctx, precompiled_libraries)
contexts_to_merge = [precompiled_linking_context, empty_archive_linking_context]
if has_compilation_outputs:
contexts_to_merge.append(linking_context)
else:
user_link_flags = cc_helper.linkopts(ctx, additional_make_variable_substitutions, cc_toolchain)
linker_scripts = _filter_linker_scripts(ctx.files.deps)
additional_linker_inputs = ctx.files.additional_linker_inputs
linker_input = cc_common.create_linker_input(
owner = ctx.label,
user_link_flags = user_link_flags,
additional_inputs = depset(linker_scripts + additional_linker_inputs),
)
contexts_to_merge.append(cc_common.create_linking_context(linker_inputs = depset([linker_input])))
contexts_to_merge.extend(linking_contexts)
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,
)
linking_context_for_runfiles = cc_helper.build_linking_context_from_libraries(ctx, libraries_to_link)
cc_native_library_info = cc_helper.collect_native_cc_libraries(
deps = ctx.attr.deps,
libraries = 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)
if hasattr(compilation_outputs, "gcno_files"):
gcno_files = compilation_outputs.gcno_files()
else:
gcno_files = compilation_outputs._gcno_files
if hasattr(compilation_outputs, "pic_gcno_files"):
pic_gcno_files = compilation_outputs.pic_gcno_files()
else:
pic_gcno_files = compilation_outputs._pic_gcno_files
instrumented_files_info = cc_helper.create_cc_instrumented_files_info(
ctx = ctx,
cc_config = ctx.fragments.cpp,
cc_toolchain = cc_toolchain,
feature_configuration = feature_configuration,
metadata_files = gcno_files + pic_gcno_files,
)
runfiles_list = []
for data_dep in ctx.attr.data:
if data_dep[DefaultInfo].data_runfiles.files:
runfiles_list.append(data_dep[DefaultInfo].data_runfiles)
else:
# This branch ensures interop with custom Starlark rules following
# https://bazel.build/extending/rules#runfiles_features_to_avoid
runfiles_list.append(ctx.runfiles(transitive_files = data_dep[DefaultInfo].files))
runfiles_list.append(data_dep[DefaultInfo].default_runfiles)
for src in ctx.attr.srcs:
runfiles_list.append(src[DefaultInfo].default_runfiles)
for dep in ctx.attr.deps:
runfiles_list.append(dep[DefaultInfo].default_runfiles)
for dep in ctx.attr.implementation_deps:
runfiles_list.append(dep[DefaultInfo].default_runfiles)
runfiles = ctx.runfiles().merge_all(runfiles_list)
default_runfiles = ctx.runfiles(files = cc_helper.get_dynamic_libraries_for_runtime(linking_context_for_runfiles, True))
default_runfiles = runfiles.merge(default_runfiles)
data_runfiles = ctx.runfiles(files = cc_helper.get_dynamic_libraries_for_runtime(linking_context_for_runfiles, 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 + ctx.attr.implementation_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)
return providers
def _add_linker_artifacts_output_groups(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)
elif 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,
alwayslink):
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
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 = alwayslink or 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 = alwayslink or 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_library)
return None
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[DefaultInfo].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.ASSEMBLER_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.ASSEMBLER_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"]
cc_library_impl = _cc_library_impl
cc_library_impl_wrapper = wrap_starlark_function(_cc_library_impl)