• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Protocol Buffers - Google's data interchange format
2# Copyright 2024 Google Inc.  All rights reserved.
3#
4# Use of this source code is governed by a BSD-style
5# license that can be found in the LICENSE file or at
6# https://developers.google.com/open-source/licenses/bsd
7#
8"""Supporting C++ compilation of generated code"""
9
10load("@proto_bazel_features//:features.bzl", "bazel_features")
11load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain")
12load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
13load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
14
15def get_feature_configuration(ctx, has_sources, extra_requested_features = []):
16    """Returns C++ feature configuration for compiling and linking generated C++ files.
17
18    Args:
19        ctx: (RuleCtx) rule context.
20        has_sources: (bool) Has the proto_library sources.
21        extra_requested_features: (list[str]) Additionally requested features.
22    Returns:
23      (FeatureConfiguration) C++ feature configuration
24    """
25    cc_toolchain = find_cc_toolchain(ctx)
26    requested_features = ctx.features + extra_requested_features
27
28    # TODO: Remove LAYERING_CHECK once we have verified that there are direct
29    # dependencies for all generated #includes.
30    unsupported_features = ctx.disabled_features + ["parse_headers", "layering_check"]
31    if has_sources:
32        requested_features.append("header_modules")
33    else:
34        unsupported_features.append("header_modules")
35    return cc_common.configure_features(
36        ctx = ctx,
37        cc_toolchain = cc_toolchain,
38        requested_features = requested_features,
39        unsupported_features = unsupported_features,
40    )
41
42def _get_libraries_from_linking_outputs(linking_outputs, feature_configuration):
43    library_to_link = linking_outputs.library_to_link
44    if not library_to_link:
45        return []
46    outputs = []
47    if library_to_link.static_library:
48        outputs.append(library_to_link.static_library)
49    if library_to_link.pic_static_library:
50        outputs.append(library_to_link.pic_static_library)
51
52    # On Windows, dynamic library is not built by default, so don't add them to files_to_build.
53    if not cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "targets_windows"):
54        if library_to_link.resolved_symlink_dynamic_library:
55            outputs.append(library_to_link.resolved_symlink_dynamic_library)
56        elif library_to_link.dynamic_library:
57            outputs.append(library_to_link.dynamic_library)
58        if library_to_link.resolved_symlink_interface_library:
59            outputs.append(library_to_link.resolved_symlink_interface_library)
60        elif library_to_link.interface_library:
61            outputs.append(library_to_link.interface_library)
62    return outputs
63
64def cc_proto_compile_and_link(ctx, deps, sources, headers, disallow_dynamic_library = None, feature_configuration = None, alwayslink = False, **kwargs):
65    """Creates C++ compilation and linking actions for C++ proto sources.
66
67    Args:
68        ctx: rule context
69        deps: (list[CcInfo]) List of libraries to be added as dependencies to compilation and linking
70            actions.
71        sources:(list[File]) List of C++ sources files.
72        headers: list(File] List of C++ headers files.
73        disallow_dynamic_library: (bool) Are dynamic libraries disallowed.
74        feature_configuration: (FeatureConfiguration) feature configuration to use.
75        alwayslink: (bool) Should the library be always linked.
76        **kwargs: Additional arguments passed to the compilation. See cc_common.compile.
77
78    Returns:
79        (CcInfo, list[File], list[File])
80        - CcInfo provider with compilation context and linking context
81        - A list of linked libraries related to this proto
82        - A list of temporary files generated durind compilation
83    """
84    cc_toolchain = find_cc_toolchain(ctx)
85    feature_configuration = feature_configuration or get_feature_configuration(ctx, bool(sources))
86    if disallow_dynamic_library == None:
87        # TODO: Configure output artifact with action_config
88        # once proto compile action is configurable from the crosstool.
89        disallow_dynamic_library = not cc_common.is_enabled(
90            feature_name = "supports_dynamic_linker",
91            feature_configuration = feature_configuration,
92        )
93
94    (compilation_context, compilation_outputs) = cc_common.compile(
95        actions = ctx.actions,
96        feature_configuration = feature_configuration,
97        cc_toolchain = cc_toolchain,
98        srcs = sources,
99        public_hdrs = headers,
100        compilation_contexts = [dep[CcInfo].compilation_context for dep in deps if CcInfo in dep],
101        name = ctx.label.name,
102        # Don't instrument the generated C++ files even when --collect_code_coverage is set.
103        # If we actually start generating coverage instrumentation for .proto files based on coverage
104        # data from the generated C++ files, this will have to be removed. Currently, the work done
105        # to instrument those files and execute the instrumentation is all for nothing, and it can
106        # be quite a bit of extra computation even when that's not made worse by performance bugs,
107        # as in b/64963386.
108        # code_coverage_enabled = False (cc_common.compile disables code_coverage by default)
109        **kwargs
110    )
111
112    if sources:
113        linking_context, linking_outputs = cc_common.create_linking_context_from_compilation_outputs(
114            actions = ctx.actions,
115            feature_configuration = feature_configuration,
116            cc_toolchain = cc_toolchain,
117            compilation_outputs = compilation_outputs,
118            linking_contexts = [dep[CcInfo].linking_context for dep in deps if CcInfo in dep],
119            name = ctx.label.name,
120            disallow_dynamic_library = disallow_dynamic_library,
121            alwayslink = alwayslink,
122        )
123        libraries = _get_libraries_from_linking_outputs(linking_outputs, feature_configuration)
124    else:
125        linking_context = cc_common.merge_linking_contexts(
126            linking_contexts = [dep[CcInfo].linking_context for dep in deps if CcInfo in dep],
127        )
128        libraries = []
129
130    debug_context = None
131    temps = []
132    if bazel_features.cc.protobuf_on_allowlist:
133        debug_context = cc_common.merge_debug_context(
134            [cc_common.create_debug_context(compilation_outputs)] +
135            [dep[CcInfo].debug_context() for dep in deps if CcInfo in dep],
136        )
137        temps = compilation_outputs.temps()
138
139    return CcInfo(
140        compilation_context = compilation_context,
141        linking_context = linking_context,
142        debug_context = debug_context,
143    ), libraries, temps
144