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