blob: 5283b97a474e35c00e1dc54f325c1e010dc5ac7a [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.
"""Starlark implementation of cc_import.
We may change the implementation at any moment or even delete this file. Do not
rely on this. Pass the flag --experimental_starlark_cc_import
"""
load(":common/cc/cc_helper.bzl", "cc_helper")
load(":common/objc/semantics.bzl", "semantics")
CcInfo = _builtins.toplevel.CcInfo
cc_common = _builtins.toplevel.cc_common
CPP_LINK_STATIC_LIBRARY_ACTION_NAME = "c++-link-static-library"
def _to_list(element):
if element == None:
return []
else:
return [element]
def _to_depset(element):
if element == None:
return depset()
else:
return depset([element])
def _is_shared_library_extension_valid(shared_library_name):
if (shared_library_name.endswith(".so") or
shared_library_name.endswith(".dll") or
shared_library_name.endswith(".dylib")):
return True
# validate agains the regex "^.+\\.((so)|(dylib))(\\.\\d\\w*)+$",
# must match VERSIONED_SHARED_LIBRARY.
for ext in (".so.", ".dylib."):
name, _, version = shared_library_name.rpartition(ext)
if name and version:
version_parts = version.split(".")
for part in version_parts:
if not part[0].isdigit():
return False
for c in part[1:].elems():
if not (c.isalnum() or c == "_"):
return False
return True
return False
def _perform_error_checks(
system_provided,
shared_library_artifact,
interface_library_artifact):
# If the shared library will be provided by system during runtime, users are not supposed to
# specify shared_library.
if system_provided and shared_library_artifact != None:
fail("'shared_library' shouldn't be specified when 'system_provided' is true")
# If a shared library won't be provided by system during runtime and we are linking the shared
# library through interface library, the shared library must be specified.
if (not system_provided and shared_library_artifact == None and
interface_library_artifact != None):
fail("'shared_library' should be specified when 'system_provided' is false")
if (shared_library_artifact != None and
not _is_shared_library_extension_valid(shared_library_artifact.basename)):
fail("'shared_library' does not produce any cc_import shared_library files (expected .so, .dylib or .dll)")
def _create_archive_action(
ctx,
feature_configuration,
cc_toolchain,
output_file,
object_files):
archiver_path = cc_common.get_tool_for_action(
feature_configuration = feature_configuration,
action_name = CPP_LINK_STATIC_LIBRARY_ACTION_NAME,
)
archiver_variables = cc_common.create_link_variables(
feature_configuration = feature_configuration,
cc_toolchain = cc_toolchain,
output_file = output_file.path,
is_using_linker = False,
)
command_line = cc_common.get_memory_inefficient_command_line(
feature_configuration = feature_configuration,
action_name = CPP_LINK_STATIC_LIBRARY_ACTION_NAME,
variables = archiver_variables,
)
args = ctx.actions.args()
args.add_all(command_line)
args.add_all(object_files)
args.use_param_file("@%s", use_always = True)
env = cc_common.get_environment_variables(
feature_configuration = feature_configuration,
action_name = CPP_LINK_STATIC_LIBRARY_ACTION_NAME,
variables = archiver_variables,
)
# TODO(bazel-team): PWD=/proc/self/cwd env var is missing, but it is present when an analogous archiving
# action is created by cc_library
ctx.actions.run(
executable = archiver_path,
arguments = [args],
env = env,
inputs = depset(
direct = object_files,
transitive = [
cc_toolchain.all_files,
],
),
use_default_shell_env = True,
outputs = [output_file],
mnemonic = "CppArchive",
)
def _cc_import_impl(ctx):
cc_toolchain = cc_helper.find_cpp_toolchain(ctx)
feature_configuration = cc_common.configure_features(
ctx = ctx,
cc_toolchain = cc_toolchain,
requested_features = ctx.features,
unsupported_features = ctx.disabled_features,
)
_perform_error_checks(
ctx.attr.system_provided,
ctx.file.shared_library,
ctx.file.interface_library,
)
pic_static_library = ctx.file.pic_static_library or None
static_library = ctx.file.static_library or None
if ctx.files.pic_objects and not pic_static_library:
lib_name = "lib" + ctx.label.name + ".pic.a"
pic_static_library = ctx.actions.declare_file(lib_name)
_create_archive_action(ctx, feature_configuration, cc_toolchain, pic_static_library, ctx.files.pic_objects)
if ctx.files.objects and not static_library:
lib_name = "lib" + ctx.label.name + ".a"
static_library = ctx.actions.declare_file(lib_name)
_create_archive_action(ctx, feature_configuration, cc_toolchain, static_library, ctx.files.objects)
not_none_artifact_to_link = False
# Check if there is something to link, if not skip that part.
if static_library != None or pic_static_library != None or ctx.file.interface_library != None or ctx.file.shared_library != None:
not_none_artifact_to_link = True
linking_context = None
if not_none_artifact_to_link:
library_to_link = cc_common.create_library_to_link(
actions = ctx.actions,
feature_configuration = feature_configuration,
cc_toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo],
static_library = static_library,
pic_static_library = pic_static_library,
interface_library = ctx.file.interface_library,
dynamic_library = ctx.file.shared_library,
pic_objects = ctx.files.pic_objects,
objects = ctx.files.objects,
alwayslink = ctx.attr.alwayslink,
)
linker_input = cc_common.create_linker_input(
libraries = depset([library_to_link]),
user_link_flags = depset(ctx.attr.linkopts),
owner = ctx.label,
)
linking_context = cc_common.create_linking_context(
linker_inputs = depset([linker_input]),
)
(compilation_context, compilation_outputs) = cc_common.compile(
actions = ctx.actions,
feature_configuration = feature_configuration,
cc_toolchain = cc_toolchain,
public_hdrs = ctx.files.hdrs,
includes = ctx.attr.includes,
name = ctx.label.name,
grep_includes = ctx.attr._grep_includes.files_to_run.executable,
)
this_cc_info = CcInfo(compilation_context = compilation_context, linking_context = linking_context)
cc_infos = [this_cc_info]
for dep in ctx.attr.deps:
cc_infos.append(dep[CcInfo])
merged_cc_info = cc_common.merge_cc_infos(direct_cc_infos = [this_cc_info], cc_infos = cc_infos)
return [merged_cc_info]
cc_import = rule(
implementation = _cc_import_impl,
attrs = {
"hdrs": attr.label_list(
allow_files = True,
flags = ["ORDER_INDEPENDENT", "DIRECT_COMPILE_TIME_INPUT"],
),
"static_library": attr.label(allow_single_file = [".a", ".lib"]),
"pic_static_library": attr.label(allow_single_file = [".pic.a", ".pic.lib"]),
"shared_library": attr.label(allow_single_file = True),
"interface_library": attr.label(
allow_single_file = [".ifso", ".tbd", ".lib", ".so", ".dylib"],
),
"pic_objects": attr.label_list(
allow_files = [".o", ".pic.o"],
),
"objects": attr.label_list(
allow_files = [".o", ".nopic.o"],
),
"system_provided": attr.bool(default = False),
"alwayslink": attr.bool(default = False),
"linkopts": attr.string_list(),
"includes": attr.string_list(),
"deps": attr.label_list(),
"data": attr.label_list(
allow_files = True,
flags = ["SKIP_CONSTRAINTS_OVERRIDE"],
),
"_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 = "@" + semantics.get_repo() + "//tools/cpp:current_cc_toolchain"),
},
toolchains = ["@" + semantics.get_repo() + "//tools/cpp:toolchain_type"], # copybara-use-repo-external-label
fragments = ["cpp"],
incompatible_use_toolchain_transition = True,
)