# 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") 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