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