|  | # Copyright 2021 The gRPC Authors | 
|  | # | 
|  | # Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | # you may not use this file except in compliance with the License. | 
|  | # You may obtain a copy of the License at | 
|  | # | 
|  | #     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | # | 
|  | # Unless required by applicable law or agreed to in writing, software | 
|  | # distributed under the License is distributed on an "AS IS" BASIS, | 
|  | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | # See the License for the specific language governing permissions and | 
|  | # limitations under the License. | 
|  | """Generates C++ grpc stubs from proto_library rules. | 
|  |  | 
|  | This is an internal rule used by cc_grpc_library, and shouldn't be used | 
|  | directly. | 
|  | """ | 
|  |  | 
|  | load("@rules_proto//proto:defs.bzl", "ProtoInfo") | 
|  | load( | 
|  | "//bazel:protobuf.bzl", | 
|  | "get_include_directory", | 
|  | "get_plugin_args", | 
|  | "get_proto_root", | 
|  | "proto_path_to_generated_filename", | 
|  | ) | 
|  |  | 
|  | _GRPC_PROTO_HEADER_FMT = "{}.grpc.pb.h" | 
|  | _GRPC_PROTO_SRC_FMT = "{}.grpc.pb.cc" | 
|  | _GRPC_PROTO_MOCK_HEADER_FMT = "{}_mock.grpc.pb.h" | 
|  | _PROTO_HEADER_FMT = "{}.pb.h" | 
|  | _PROTO_SRC_FMT = "{}.pb.cc" | 
|  |  | 
|  | def _strip_package_from_path(label_package, file): | 
|  | prefix_len = 0 | 
|  | if not file.is_source and file.path.startswith(file.root.path): | 
|  | prefix_len = len(file.root.path) + 1 | 
|  |  | 
|  | path = file.path | 
|  | if len(label_package) == 0: | 
|  | return path | 
|  | if not path.startswith(label_package + "/", prefix_len): | 
|  | fail("'{}' does not lie within '{}'.".format(path, label_package)) | 
|  | return path[prefix_len + len(label_package + "/"):] | 
|  |  | 
|  | def _get_srcs_file_path(file): | 
|  | if not file.is_source and file.path.startswith(file.root.path): | 
|  | return file.path[len(file.root.path) + 1:] | 
|  | return file.path | 
|  |  | 
|  | def _join_directories(directories): | 
|  | massaged_directories = [directory for directory in directories if len(directory) != 0] | 
|  | return "/".join(massaged_directories) | 
|  |  | 
|  | def generate_cc_impl(ctx): | 
|  | """Implementation of the generate_cc rule. | 
|  |  | 
|  | Args: | 
|  | ctx: The context object. | 
|  | Returns: | 
|  | The provider for the generated files. | 
|  | """ | 
|  | protos = [f for src in ctx.attr.srcs for f in src[ProtoInfo].check_deps_sources.to_list()] | 
|  | includes = [ | 
|  | f | 
|  | for src in ctx.attr.srcs | 
|  | for f in src[ProtoInfo].transitive_imports.to_list() | 
|  | ] | 
|  | outs = [] | 
|  | proto_root = get_proto_root( | 
|  | ctx.label.workspace_root, | 
|  | ) | 
|  |  | 
|  | label_package = _join_directories([ctx.label.workspace_root, ctx.label.package]) | 
|  | if ctx.executable.plugin: | 
|  | outs += [ | 
|  | proto_path_to_generated_filename( | 
|  | _strip_package_from_path(label_package, proto), | 
|  | _GRPC_PROTO_HEADER_FMT, | 
|  | ) | 
|  | for proto in protos | 
|  | ] | 
|  | outs += [ | 
|  | proto_path_to_generated_filename( | 
|  | _strip_package_from_path(label_package, proto), | 
|  | _GRPC_PROTO_SRC_FMT, | 
|  | ) | 
|  | for proto in protos | 
|  | ] | 
|  | if ctx.attr.generate_mocks: | 
|  | outs += [ | 
|  | proto_path_to_generated_filename( | 
|  | _strip_package_from_path(label_package, proto), | 
|  | _GRPC_PROTO_MOCK_HEADER_FMT, | 
|  | ) | 
|  | for proto in protos | 
|  | ] | 
|  | else: | 
|  | outs += [ | 
|  | proto_path_to_generated_filename( | 
|  | _strip_package_from_path(label_package, proto), | 
|  | _PROTO_HEADER_FMT, | 
|  | ) | 
|  | for proto in protos | 
|  | ] | 
|  | outs += [ | 
|  | proto_path_to_generated_filename( | 
|  | _strip_package_from_path(label_package, proto), | 
|  | _PROTO_SRC_FMT, | 
|  | ) | 
|  | for proto in protos | 
|  | ] | 
|  | out_files = [ctx.actions.declare_file(out) for out in outs] | 
|  | dir_out = str(ctx.genfiles_dir.path + proto_root) | 
|  |  | 
|  | arguments = [] | 
|  | if ctx.executable.plugin: | 
|  | arguments += get_plugin_args( | 
|  | ctx.executable.plugin, | 
|  | ctx.attr.flags, | 
|  | dir_out, | 
|  | ctx.attr.generate_mocks, | 
|  | ) | 
|  | tools = [ctx.executable.plugin] | 
|  | else: | 
|  | arguments.append("--cpp_out=" + ",".join(ctx.attr.flags) + ":" + dir_out) | 
|  | tools = [] | 
|  |  | 
|  | arguments += [ | 
|  | "--proto_path={}".format(get_include_directory(i)) | 
|  | for i in includes | 
|  | ] | 
|  |  | 
|  | # Include the output directory so that protoc puts the generated code in the | 
|  | # right directory. | 
|  | arguments.append("--proto_path={0}{1}".format(dir_out, proto_root)) | 
|  | arguments += [_get_srcs_file_path(proto) for proto in protos] | 
|  |  | 
|  | # create a list of well known proto files if the argument is non-None | 
|  | well_known_proto_files = [] | 
|  | if ctx.attr.well_known_protos: | 
|  | f = ctx.attr.well_known_protos.files.to_list()[0].dirname | 
|  | if f != "external/com_google_protobuf/src/google/protobuf": | 
|  | print( | 
|  | "Error: Only @com_google_protobuf//:well_known_type_protos is supported", | 
|  | )  # buildifier: disable=print | 
|  | else: | 
|  | # f points to "external/com_google_protobuf/src/google/protobuf" | 
|  | # add -I argument to protoc so it knows where to look for the proto files. | 
|  | arguments.append("-I{0}".format(f + "/../..")) | 
|  | well_known_proto_files = [ | 
|  | f | 
|  | for f in ctx.attr.well_known_protos.files.to_list() | 
|  | ] | 
|  |  | 
|  | ctx.actions.run( | 
|  | inputs = protos + includes + well_known_proto_files, | 
|  | tools = tools, | 
|  | outputs = out_files, | 
|  | executable = ctx.executable._protoc, | 
|  | arguments = arguments, | 
|  | use_default_shell_env = True, | 
|  | ) | 
|  |  | 
|  | return DefaultInfo(files = depset(out_files)) | 
|  |  | 
|  | _generate_cc = rule( | 
|  | attrs = { | 
|  | "srcs": attr.label_list( | 
|  | mandatory = True, | 
|  | allow_empty = False, | 
|  | providers = [ProtoInfo], | 
|  | ), | 
|  | "plugin": attr.label( | 
|  | executable = True, | 
|  | providers = ["files_to_run"], | 
|  | cfg = "host", | 
|  | ), | 
|  | "flags": attr.string_list( | 
|  | mandatory = False, | 
|  | allow_empty = True, | 
|  | ), | 
|  | "well_known_protos": attr.label(mandatory = False), | 
|  | "generate_mocks": attr.bool( | 
|  | default = False, | 
|  | mandatory = False, | 
|  | ), | 
|  | "_protoc": attr.label( | 
|  | default = Label("//external:protocol_compiler"), | 
|  | executable = True, | 
|  | cfg = "host", | 
|  | ), | 
|  | }, | 
|  | # We generate .h files, so we need to output to genfiles. | 
|  | output_to_genfiles = True, | 
|  | implementation = generate_cc_impl, | 
|  | ) | 
|  |  | 
|  | def generate_cc(well_known_protos, **kwargs): | 
|  | if well_known_protos: | 
|  | _generate_cc( | 
|  | well_known_protos = "@com_google_protobuf//:well_known_type_protos", | 
|  | **kwargs | 
|  | ) | 
|  | else: | 
|  | _generate_cc(**kwargs) |