blob: fbd78adba9b103630c4421bce26869001c24295d [file] [log] [blame]
# Protocol Buffers - Google's data interchange format
# Copyright 2024 Google Inc. All rights reserved.
#
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file or at
# https://developers.google.com/open-source/licenses/bsd
#
"""Supporting C++ compilation of generated code"""
load("@proto_bazel_features//:features.bzl", "bazel_features")
load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain")
load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
def get_feature_configuration(ctx, has_sources, extra_requested_features = []):
"""Returns C++ feature configuration for compiling and linking generated C++ files.
Args:
ctx: (RuleCtx) rule context.
has_sources: (bool) Has the proto_library sources.
extra_requested_features: (list[str]) Additionally requested features.
Returns:
(FeatureConfiguration) C++ feature configuration
"""
cc_toolchain = find_cc_toolchain(ctx)
requested_features = ctx.features + extra_requested_features
# TODO: Remove LAYERING_CHECK once we have verified that there are direct
# dependencies for all generated #includes.
unsupported_features = ctx.disabled_features + ["parse_headers", "layering_check"]
if has_sources:
requested_features.append("header_modules")
else:
unsupported_features.append("header_modules")
return cc_common.configure_features(
ctx = ctx,
cc_toolchain = cc_toolchain,
requested_features = requested_features,
unsupported_features = unsupported_features,
)
def _get_libraries_from_linking_outputs(linking_outputs, feature_configuration):
library_to_link = linking_outputs.library_to_link
if not library_to_link:
return []
outputs = []
if library_to_link.static_library:
outputs.append(library_to_link.static_library)
if library_to_link.pic_static_library:
outputs.append(library_to_link.pic_static_library)
# On Windows, dynamic library is not built by default, so don't add them to files_to_build.
if not cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "targets_windows"):
if library_to_link.resolved_symlink_dynamic_library:
outputs.append(library_to_link.resolved_symlink_dynamic_library)
elif library_to_link.dynamic_library:
outputs.append(library_to_link.dynamic_library)
if library_to_link.resolved_symlink_interface_library:
outputs.append(library_to_link.resolved_symlink_interface_library)
elif library_to_link.interface_library:
outputs.append(library_to_link.interface_library)
return outputs
def cc_proto_compile_and_link(ctx, deps, sources, headers, disallow_dynamic_library = None, feature_configuration = None, alwayslink = False, **kwargs):
"""Creates C++ compilation and linking actions for C++ proto sources.
Args:
ctx: rule context
deps: (list[CcInfo]) List of libraries to be added as dependencies to compilation and linking
actions.
sources:(list[File]) List of C++ sources files.
headers: list(File] List of C++ headers files.
disallow_dynamic_library: (bool) Are dynamic libraries disallowed.
feature_configuration: (FeatureConfiguration) feature configuration to use.
alwayslink: (bool) Should the library be always linked.
**kwargs: Additional arguments passed to the compilation. See cc_common.compile.
Returns:
(CcInfo, list[File], list[File])
- CcInfo provider with compilation context and linking context
- A list of linked libraries related to this proto
- A list of temporary files generated durind compilation
"""
cc_toolchain = find_cc_toolchain(ctx)
feature_configuration = feature_configuration or get_feature_configuration(ctx, bool(sources))
if disallow_dynamic_library == None:
# TODO: Configure output artifact with action_config
# once proto compile action is configurable from the crosstool.
disallow_dynamic_library = not cc_common.is_enabled(
feature_name = "supports_dynamic_linker",
feature_configuration = feature_configuration,
)
(compilation_context, compilation_outputs) = cc_common.compile(
actions = ctx.actions,
feature_configuration = feature_configuration,
cc_toolchain = cc_toolchain,
srcs = sources,
public_hdrs = headers,
compilation_contexts = [dep[CcInfo].compilation_context for dep in deps if CcInfo in dep],
name = ctx.label.name,
# Don't instrument the generated C++ files even when --collect_code_coverage is set.
# If we actually start generating coverage instrumentation for .proto files based on coverage
# data from the generated C++ files, this will have to be removed. Currently, the work done
# to instrument those files and execute the instrumentation is all for nothing, and it can
# be quite a bit of extra computation even when that's not made worse by performance bugs,
# as in b/64963386.
# code_coverage_enabled = False (cc_common.compile disables code_coverage by default)
**kwargs
)
if sources:
linking_context, linking_outputs = cc_common.create_linking_context_from_compilation_outputs(
actions = ctx.actions,
feature_configuration = feature_configuration,
cc_toolchain = cc_toolchain,
compilation_outputs = compilation_outputs,
linking_contexts = [dep[CcInfo].linking_context for dep in deps if CcInfo in dep],
name = ctx.label.name,
disallow_dynamic_library = disallow_dynamic_library,
alwayslink = alwayslink,
)
libraries = _get_libraries_from_linking_outputs(linking_outputs, feature_configuration)
else:
linking_context = cc_common.merge_linking_contexts(
linking_contexts = [dep[CcInfo].linking_context for dep in deps if CcInfo in dep],
)
libraries = []
debug_context = None
temps = []
if bazel_features.cc.protobuf_on_allowlist:
debug_context = cc_common.merge_debug_context(
[cc_common.create_debug_context(compilation_outputs)] +
[dep[CcInfo].debug_context() for dep in deps if CcInfo in dep],
)
temps = compilation_outputs.temps()
return CcInfo(
compilation_context = compilation_context,
linking_context = linking_context,
debug_context = debug_context,
), libraries, temps