| # 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 | 
 | # | 
 | """Bazel's implementation of cc_proto_library""" | 
 |  | 
 | load("@rules_cc//cc:find_cc_toolchain.bzl", "use_cc_toolchain") | 
 | load("@rules_cc//cc/common:cc_info.bzl", "CcInfo") | 
 | load("//bazel/common:proto_common.bzl", "proto_common") | 
 | load("//bazel/common:proto_info.bzl", "ProtoInfo") | 
 | load("//bazel/private:cc_proto_support.bzl", "cc_proto_compile_and_link") | 
 | load("//bazel/private:toolchain_helpers.bzl", "toolchains") | 
 |  | 
 | _CC_PROTO_TOOLCHAIN = Label("//bazel/private:cc_toolchain_type") | 
 |  | 
 | _ProtoCcFilesInfo = provider(fields = ["files"], doc = "Provide cc proto files.") | 
 | _ProtoCcHeaderInfo = provider(fields = ["headers"], doc = "Provide cc proto headers.") | 
 |  | 
 | def _get_output_files(actions, proto_info, suffixes): | 
 |     result = [] | 
 |     for suffix in suffixes: | 
 |         result.extend(proto_common.declare_generated_files( | 
 |             actions = actions, | 
 |             proto_info = proto_info, | 
 |             extension = suffix, | 
 |         )) | 
 |     return result | 
 |  | 
 | # TODO: Make this code actually work. | 
 | def _get_strip_include_prefix(ctx, proto_info): | 
 |     proto_root = proto_info.proto_source_root | 
 |     if proto_root == "." or proto_root == ctx.label.workspace_root: | 
 |         return "" | 
 |     strip_include_prefix = "" | 
 |     if proto_root.startswith(ctx.bin_dir.path): | 
 |         proto_root = proto_root[len(ctx.bin_dir.path) + 1:] | 
 |     elif proto_root.startswith(ctx.genfiles_dir.path): | 
 |         proto_root = proto_root[len(ctx.genfiles_dir.path) + 1:] | 
 |  | 
 |     if proto_root.startswith(ctx.label.workspace_root): | 
 |         proto_root = proto_root[len(ctx.label.workspace_root):] | 
 |  | 
 |     strip_include_prefix = "//" + proto_root | 
 |     return strip_include_prefix | 
 |  | 
 | def _aspect_impl(target, ctx): | 
 |     proto_info = target[ProtoInfo] | 
 |     proto_configuration = ctx.fragments.proto | 
 |  | 
 |     sources = [] | 
 |     headers = [] | 
 |     textual_hdrs = [] | 
 |  | 
 |     proto_toolchain = toolchains.find_toolchain(ctx, "_aspect_cc_proto_toolchain", _CC_PROTO_TOOLCHAIN) | 
 |     should_generate_code = proto_common.experimental_should_generate_code(proto_info, proto_toolchain, "cc_proto_library", target.label) | 
 |  | 
 |     if should_generate_code: | 
 |         if len(proto_info.direct_sources) != 0: | 
 |             # Bazel 7 didn't expose cc_proto_library_source_suffixes used by Kythe | 
 |             # gradually falling back to .pb.cc | 
 |             if type(proto_configuration.cc_proto_library_source_suffixes) == "builtin_function_or_method": | 
 |                 source_suffixes = [".pb.cc"] | 
 |                 header_suffixes = [".pb.h"] | 
 |             else: | 
 |                 source_suffixes = proto_configuration.cc_proto_library_source_suffixes | 
 |                 header_suffixes = proto_configuration.cc_proto_library_header_suffixes | 
 |             sources = _get_output_files(ctx.actions, proto_info, source_suffixes) | 
 |             headers = _get_output_files(ctx.actions, proto_info, header_suffixes) | 
 |             header_provider = _ProtoCcHeaderInfo(headers = depset(headers)) | 
 |         else: | 
 |             # If this proto_library doesn't have sources, it provides the combined headers of all its | 
 |             # direct dependencies. Thus, if a direct dependency does have sources, the generated files | 
 |             # are also provided by this library. If a direct dependency does not have sources, it will | 
 |             # do the same thing, so that effectively this library looks through all source-less | 
 |             # proto_libraries and provides all generated headers of the proto_libraries with sources | 
 |             # that it depends on. | 
 |             transitive_headers = [] | 
 |             for dep in getattr(ctx.rule.attr, "deps", []): | 
 |                 if _ProtoCcHeaderInfo in dep: | 
 |                     textual_hdrs.extend(dep[_ProtoCcHeaderInfo].headers.to_list()) | 
 |                     transitive_headers.append(dep[_ProtoCcHeaderInfo].headers) | 
 |             header_provider = _ProtoCcHeaderInfo(headers = depset(transitive = transitive_headers)) | 
 |  | 
 |     else:  # shouldn't generate code | 
 |         header_provider = _ProtoCcHeaderInfo(headers = depset()) | 
 |  | 
 |     proto_common.compile( | 
 |         actions = ctx.actions, | 
 |         proto_info = proto_info, | 
 |         proto_lang_toolchain_info = proto_toolchain, | 
 |         generated_files = sources + headers, | 
 |         experimental_output_files = "multiple", | 
 |     ) | 
 |  | 
 |     deps = [] | 
 |     if proto_toolchain.runtime: | 
 |         deps = [proto_toolchain.runtime] | 
 |     deps.extend(getattr(ctx.rule.attr, "deps", [])) | 
 |  | 
 |     cc_info, libraries, temps = cc_proto_compile_and_link( | 
 |         ctx = ctx, | 
 |         deps = deps, | 
 |         sources = sources, | 
 |         headers = headers, | 
 |         textual_hdrs = textual_hdrs, | 
 |         strip_include_prefix = _get_strip_include_prefix(ctx, proto_info), | 
 |     ) | 
 |  | 
 |     return [ | 
 |         cc_info, | 
 |         _ProtoCcFilesInfo(files = depset(sources + headers + libraries)), | 
 |         OutputGroupInfo(temp_files_INTERNAL_ = temps), | 
 |         header_provider, | 
 |     ] | 
 |  | 
 | cc_proto_aspect = aspect( | 
 |     implementation = _aspect_impl, | 
 |     attr_aspects = ["deps"], | 
 |     fragments = ["cpp", "proto"], | 
 |     required_providers = [ProtoInfo], | 
 |     provides = [CcInfo], | 
 |     attrs = toolchains.if_legacy_toolchain({"_aspect_cc_proto_toolchain": attr.label( | 
 |         default = configuration_field(fragment = "proto", name = "proto_toolchain_for_cc"), | 
 |     )}), | 
 |     toolchains = use_cc_toolchain() + toolchains.use_toolchain(_CC_PROTO_TOOLCHAIN), | 
 | ) | 
 |  | 
 | def _cc_proto_library_impl(ctx): | 
 |     if len(ctx.attr.deps) != 1: | 
 |         fail( | 
 |             "'deps' attribute must contain exactly one label " + | 
 |             "(we didn't name it 'dep' for consistency). " + | 
 |             "The main use-case for multiple deps is to create a rule that contains several " + | 
 |             "other targets. This makes dependency bloat more likely. It also makes it harder" + | 
 |             "to remove unused deps.", | 
 |             attr = "deps", | 
 |         ) | 
 |     dep = ctx.attr.deps[0] | 
 |  | 
 |     proto_toolchain = toolchains.find_toolchain(ctx, "_aspect_cc_proto_toolchain", _CC_PROTO_TOOLCHAIN) | 
 |     proto_common.check_collocated(ctx.label, dep[ProtoInfo], proto_toolchain) | 
 |  | 
 |     return [DefaultInfo(files = dep[_ProtoCcFilesInfo].files), dep[CcInfo], dep[OutputGroupInfo]] | 
 |  | 
 | cc_proto_library = rule( | 
 |     implementation = _cc_proto_library_impl, | 
 |     doc = """ | 
 | <p> | 
 | <code>cc_proto_library</code> generates C++ code from <code>.proto</code> files. | 
 | </p> | 
 |  | 
 | <p> | 
 | <code>deps</code> must point to <a href="protocol-buffer.html#proto_library"><code>proto_library | 
 | </code></a> rules. | 
 | </p> | 
 |  | 
 | <p> | 
 | Example: | 
 | </p> | 
 |  | 
 | <pre> | 
 | <code class="lang-starlark"> | 
 | cc_library( | 
 |     name = "lib", | 
 |     deps = [":foo_cc_proto"], | 
 | ) | 
 |  | 
 | cc_proto_library( | 
 |     name = "foo_cc_proto", | 
 |     deps = [":foo_proto"], | 
 | ) | 
 |  | 
 | proto_library( | 
 |     name = "foo_proto", | 
 | ) | 
 | </code> | 
 | </pre> | 
 | """, | 
 |     attrs = { | 
 |         "deps": attr.label_list( | 
 |             aspects = [cc_proto_aspect], | 
 |             providers = [ProtoInfo], | 
 |             allow_files = False, | 
 |             doc = """ | 
 | The list of <a href="protocol-buffer.html#proto_library"><code>proto_library</code></a> | 
 | rules to generate C++ code for.""", | 
 |         ), | 
 |     } | toolchains.if_legacy_toolchain({ | 
 |         "_aspect_cc_proto_toolchain": attr.label( | 
 |             default = configuration_field(fragment = "proto", name = "proto_toolchain_for_cc"), | 
 |         ), | 
 |     }), | 
 |     provides = [CcInfo], | 
 |     toolchains = toolchains.use_toolchain(_CC_PROTO_TOOLCHAIN), | 
 | ) |