1"""Generates C++ grpc stubs from proto_library rules. 2 3This is an internal rule used by cc_grpc_library, and shouldn't be used 4directly. 5""" 6 7load("@rules_proto//proto:defs.bzl", "ProtoInfo") 8load( 9 "//bazel:protobuf.bzl", 10 "get_include_directory", 11 "get_plugin_args", 12 "get_proto_root", 13 "proto_path_to_generated_filename", 14) 15 16_GRPC_PROTO_HEADER_FMT = "{}.grpc.pb.h" 17_GRPC_PROTO_SRC_FMT = "{}.grpc.pb.cc" 18_GRPC_PROTO_MOCK_HEADER_FMT = "{}_mock.grpc.pb.h" 19_PROTO_HEADER_FMT = "{}.pb.h" 20_PROTO_SRC_FMT = "{}.pb.cc" 21 22def _strip_package_from_path(label_package, file): 23 prefix_len = 0 24 if not file.is_source and file.path.startswith(file.root.path): 25 prefix_len = len(file.root.path) + 1 26 27 path = file.path 28 if len(label_package) == 0: 29 return path 30 if not path.startswith(label_package + "/", prefix_len): 31 fail("'{}' does not lie within '{}'.".format(path, label_package)) 32 return path[prefix_len + len(label_package + "/"):] 33 34def _get_srcs_file_path(file): 35 if not file.is_source and file.path.startswith(file.root.path): 36 return file.path[len(file.root.path) + 1:] 37 return file.path 38 39def _join_directories(directories): 40 massaged_directories = [directory for directory in directories if len(directory) != 0] 41 return "/".join(massaged_directories) 42 43def generate_cc_impl(ctx): 44 """Implementation of the generate_cc rule.""" 45 protos = [f for src in ctx.attr.srcs for f in src[ProtoInfo].check_deps_sources.to_list()] 46 includes = [ 47 f 48 for src in ctx.attr.srcs 49 for f in src[ProtoInfo].transitive_imports.to_list() 50 ] 51 outs = [] 52 proto_root = get_proto_root( 53 ctx.label.workspace_root, 54 ) 55 56 label_package = _join_directories([ctx.label.workspace_root, ctx.label.package]) 57 if ctx.executable.plugin: 58 outs += [ 59 proto_path_to_generated_filename( 60 _strip_package_from_path(label_package, proto), 61 _GRPC_PROTO_HEADER_FMT, 62 ) 63 for proto in protos 64 ] 65 outs += [ 66 proto_path_to_generated_filename( 67 _strip_package_from_path(label_package, proto), 68 _GRPC_PROTO_SRC_FMT, 69 ) 70 for proto in protos 71 ] 72 if ctx.attr.generate_mocks: 73 outs += [ 74 proto_path_to_generated_filename( 75 _strip_package_from_path(label_package, proto), 76 _GRPC_PROTO_MOCK_HEADER_FMT, 77 ) 78 for proto in protos 79 ] 80 else: 81 outs += [ 82 proto_path_to_generated_filename( 83 _strip_package_from_path(label_package, proto), 84 _PROTO_HEADER_FMT, 85 ) 86 for proto in protos 87 ] 88 outs += [ 89 proto_path_to_generated_filename( 90 _strip_package_from_path(label_package, proto), 91 _PROTO_SRC_FMT, 92 ) 93 for proto in protos 94 ] 95 out_files = [ctx.actions.declare_file(out) for out in outs] 96 dir_out = str(ctx.genfiles_dir.path + proto_root) 97 98 arguments = [] 99 if ctx.executable.plugin: 100 arguments += get_plugin_args( 101 ctx.executable.plugin, 102 ctx.attr.flags, 103 dir_out, 104 ctx.attr.generate_mocks, 105 ) 106 tools = [ctx.executable.plugin] 107 else: 108 arguments += ["--cpp_out=" + ",".join(ctx.attr.flags) + ":" + dir_out] 109 tools = [] 110 111 arguments += [ 112 "--proto_path={}".format(get_include_directory(i)) 113 for i in includes 114 ] 115 116 # Include the output directory so that protoc puts the generated code in the 117 # right directory. 118 arguments += ["--proto_path={0}{1}".format(dir_out, proto_root)] 119 arguments += [_get_srcs_file_path(proto) for proto in protos] 120 121 # create a list of well known proto files if the argument is non-None 122 well_known_proto_files = [] 123 if ctx.attr.well_known_protos: 124 f = ctx.attr.well_known_protos.files.to_list()[0].dirname 125 if f != "external/com_google_protobuf/src/google/protobuf": 126 print( 127 "Error: Only @com_google_protobuf//:well_known_protos is supported", 128 ) 129 else: 130 # f points to "external/com_google_protobuf/src/google/protobuf" 131 # add -I argument to protoc so it knows where to look for the proto files. 132 arguments += ["-I{0}".format(f + "/../..")] 133 well_known_proto_files = [ 134 f 135 for f in ctx.attr.well_known_protos.files.to_list() 136 ] 137 138 ctx.actions.run( 139 inputs = protos + includes + well_known_proto_files, 140 tools = tools, 141 outputs = out_files, 142 executable = ctx.executable._protoc, 143 arguments = arguments, 144 ) 145 146 return struct(files = depset(out_files)) 147 148_generate_cc = rule( 149 attrs = { 150 "srcs": attr.label_list( 151 mandatory = True, 152 allow_empty = False, 153 providers = [ProtoInfo], 154 ), 155 "plugin": attr.label( 156 executable = True, 157 providers = ["files_to_run"], 158 cfg = "host", 159 ), 160 "flags": attr.string_list( 161 mandatory = False, 162 allow_empty = True, 163 ), 164 "well_known_protos": attr.label(mandatory = False), 165 "generate_mocks": attr.bool( 166 default = False, 167 mandatory = False, 168 ), 169 "_protoc": attr.label( 170 default = Label("//external:protocol_compiler"), 171 executable = True, 172 cfg = "host", 173 ), 174 }, 175 # We generate .h files, so we need to output to genfiles. 176 output_to_genfiles = True, 177 implementation = generate_cc_impl, 178) 179 180def generate_cc(well_known_protos, **kwargs): 181 if well_known_protos: 182 _generate_cc( 183 well_known_protos = "@com_google_protobuf//:well_known_protos", 184 **kwargs 185 ) 186 else: 187 _generate_cc(**kwargs) 188