1"""Rule for running the gRPC C++ code generator. 2 3This is a simplified and modernised version of the upstream gRPC 4`bazel/cc_grpc_library.bzl` file, which as of release v1.45 5(published 2022-03-19) does not support separating the `proto_library` and 6`cc_proto_library` targets into separate packages or repositories. 7 8The following logic should eventually find a home in upstream gRPC, rules_proto, 9or rules_cc so that the Bazel Remote APIs repository can be further decoupled 10from language-specific concerns. 11""" 12 13load("@com_github_grpc_grpc//bazel:protobuf.bzl", "get_include_protoc_args") 14 15_EXT_PROTO = ".proto" 16_EXT_PROTODEVEL = ".protodevel" 17_EXT_GRPC_HDR = ".grpc.pb.h" 18_EXT_GRPC_SRC = ".grpc.pb.cc" 19 20def _drop_proto_ext(name): 21 if name.endswith(_EXT_PROTO): 22 return name[:-len(_EXT_PROTO)] 23 if name.endswith(_EXT_PROTODEVEL): 24 return name[:-len(_EXT_PROTODEVEL)] 25 fail("{!r} does not end with {!r} or {!r}".format( 26 name, 27 _EXT_PROTO, 28 _EXT_PROTODEVEL, 29 )) 30 31def _proto_srcname(file): 32 """Return the Protobuf source name for a proto_library source file. 33 34 The source name is what the Protobuf compiler uses to identify a .proto 35 source file. It is relative to the compiler's `--proto_path` flag. 36 """ 37 ws_root = file.owner.workspace_root 38 if ws_root != "" and file.path.startswith(ws_root): 39 return file.path[len(ws_root) + 1:] 40 return file.short_path 41 42def _cc_grpc_codegen(ctx): 43 """Run the gRPC C++ code generator to produce sources and headers""" 44 proto = ctx.attr.proto[ProtoInfo] 45 proto_srcs = proto.check_deps_sources.to_list() 46 proto_imports = proto.transitive_imports.to_list() 47 48 protoc_out = ctx.actions.declare_directory(ctx.attr.name + "_protoc_out") 49 protoc_outputs = [protoc_out] 50 rule_outputs = [] 51 52 for proto_src in proto_srcs: 53 srcname = _drop_proto_ext(_proto_srcname(proto_src)) 54 basename = _drop_proto_ext(proto_src.basename) 55 56 out_hdr = ctx.actions.declare_file(basename + _EXT_GRPC_HDR) 57 out_src = ctx.actions.declare_file(basename + _EXT_GRPC_SRC) 58 59 protoc_out_prefix = protoc_out.basename 60 protoc_out_hdr = ctx.actions.declare_file( 61 "{}/{}".format(protoc_out_prefix, srcname + _EXT_GRPC_HDR), 62 ) 63 protoc_out_src = ctx.actions.declare_file( 64 "{}/{}".format(protoc_out_prefix, srcname + _EXT_GRPC_SRC), 65 ) 66 67 rule_outputs.extend([out_hdr, out_src]) 68 protoc_outputs.extend([protoc_out_hdr, protoc_out_src]) 69 70 ctx.actions.expand_template( 71 template = protoc_out_hdr, 72 output = out_hdr, 73 substitutions = {}, 74 ) 75 ctx.actions.expand_template( 76 template = protoc_out_src, 77 output = out_src, 78 substitutions = {}, 79 ) 80 81 plugin = ctx.executable._protoc_gen_grpc 82 protoc_args = ctx.actions.args() 83 protoc_args.add("--plugin", "protoc-gen-grpc=" + plugin.path) 84 protoc_args.add("--grpc_out", protoc_out.path) 85 86 protoc_args.add_all(get_include_protoc_args(proto_imports)) 87 protoc_args.add_all(proto_srcs, map_each = _proto_srcname) 88 89 ctx.actions.run( 90 executable = ctx.executable._protoc, 91 arguments = [protoc_args], 92 inputs = proto_srcs + proto_imports, 93 outputs = protoc_outputs, 94 tools = [plugin], 95 ) 96 97 return DefaultInfo(files = depset(rule_outputs)) 98 99cc_grpc_codegen = rule( 100 implementation = _cc_grpc_codegen, 101 attrs = { 102 "proto": attr.label( 103 mandatory = True, 104 allow_single_file = True, 105 providers = [ProtoInfo], 106 ), 107 "_protoc_gen_grpc": attr.label( 108 default = Label("@com_github_grpc_grpc//src/compiler:grpc_cpp_plugin"), 109 executable = True, 110 cfg = "host", 111 ), 112 "_protoc": attr.label( 113 default = Label("//external:protocol_compiler"), 114 executable = True, 115 cfg = "host", 116 ), 117 }, 118) 119