blob: a6e8ab0de1366c048c43366bb75f8978ac88476a [file] [log] [blame] [edit]
"""Rule for running the gRPC C++ code generator.
This is a simplified and modernised version of the upstream gRPC
`bazel/cc_grpc_library.bzl` file, which as of release v1.45
(published 2022-03-19) does not support separating the `proto_library` and
`cc_proto_library` targets into separate packages or repositories.
The following logic should eventually find a home in upstream gRPC, rules_proto,
or rules_cc so that the Bazel Remote APIs repository can be further decoupled
from language-specific concerns.
"""
load("@com_github_grpc_grpc//bazel:protobuf.bzl", "get_include_protoc_args")
_EXT_PROTO = ".proto"
_EXT_PROTODEVEL = ".protodevel"
_EXT_GRPC_HDR = ".grpc.pb.h"
_EXT_GRPC_SRC = ".grpc.pb.cc"
def _drop_proto_ext(name):
if name.endswith(_EXT_PROTO):
return name[:-len(_EXT_PROTO)]
if name.endswith(_EXT_PROTODEVEL):
return name[:-len(_EXT_PROTODEVEL)]
fail("{!r} does not end with {!r} or {!r}".format(
name,
_EXT_PROTO,
_EXT_PROTODEVEL,
))
def _proto_srcname(file):
"""Return the Protobuf source name for a proto_library source file.
The source name is what the Protobuf compiler uses to identify a .proto
source file. It is relative to the compiler's `--proto_path` flag.
"""
ws_root = file.owner.workspace_root
if ws_root != "" and file.path.startswith(ws_root):
return file.path[len(ws_root) + 1:]
return file.short_path
def _cc_grpc_codegen(ctx):
"""Run the gRPC C++ code generator to produce sources and headers"""
proto = ctx.attr.proto[ProtoInfo]
proto_srcs = proto.check_deps_sources.to_list()
proto_imports = proto.transitive_imports.to_list()
protoc_out = ctx.actions.declare_directory(ctx.attr.name + "_protoc_out")
protoc_outputs = [protoc_out]
rule_outputs = []
for proto_src in proto_srcs:
srcname = _drop_proto_ext(_proto_srcname(proto_src))
basename = _drop_proto_ext(proto_src.basename)
out_hdr = ctx.actions.declare_file(basename + _EXT_GRPC_HDR)
out_src = ctx.actions.declare_file(basename + _EXT_GRPC_SRC)
protoc_out_prefix = protoc_out.basename
protoc_out_hdr = ctx.actions.declare_file(
"{}/{}".format(protoc_out_prefix, srcname + _EXT_GRPC_HDR),
)
protoc_out_src = ctx.actions.declare_file(
"{}/{}".format(protoc_out_prefix, srcname + _EXT_GRPC_SRC),
)
rule_outputs.extend([out_hdr, out_src])
protoc_outputs.extend([protoc_out_hdr, protoc_out_src])
ctx.actions.expand_template(
template = protoc_out_hdr,
output = out_hdr,
substitutions = {},
)
ctx.actions.expand_template(
template = protoc_out_src,
output = out_src,
substitutions = {},
)
plugin = ctx.executable._protoc_gen_grpc
protoc_args = ctx.actions.args()
protoc_args.add("--plugin", "protoc-gen-grpc=" + plugin.path)
protoc_args.add("--grpc_out", protoc_out.path)
protoc_args.add_all(get_include_protoc_args(proto_imports))
protoc_args.add_all(proto_srcs, map_each = _proto_srcname)
ctx.actions.run(
executable = ctx.executable._protoc,
arguments = [protoc_args],
inputs = proto_srcs + proto_imports,
outputs = protoc_outputs,
tools = [plugin],
)
return DefaultInfo(files = depset(rule_outputs))
cc_grpc_codegen = rule(
implementation = _cc_grpc_codegen,
attrs = {
"proto": attr.label(
mandatory = True,
allow_single_file = True,
providers = [ProtoInfo],
),
"_protoc_gen_grpc": attr.label(
default = Label("@com_github_grpc_grpc//src/compiler:grpc_cpp_plugin"),
executable = True,
cfg = "host",
),
"_protoc": attr.label(
default = Label("//external:protocol_compiler"),
executable = True,
cfg = "host",
),
},
)