xref: /aosp_15_r20/external/bazelbuild-remote-apis/internal/cc_grpc_library.bzl (revision ae21b2b400d1606a797985382019aea74177085c)
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