8f30c84f6b9a105d4a338b043158a6592d45b238
[platform/upstream/grpc.git] / bazel / generate_cc.bzl
1 """Generates C++ grpc stubs from proto_library rules.
2
3 This is an internal rule used by cc_grpc_library, and shouldn't be used
4 directly.
5 """
6
7 def generate_cc_impl(ctx):
8   """Implementation of the generate_cc rule."""
9   protos = [f for src in ctx.attr.srcs for f in src.proto.direct_sources]
10   includes = [f for src in ctx.attr.srcs for f in src.proto.transitive_imports]
11   outs = []
12   # label_len is length of the path from WORKSPACE root to the location of this build file
13   label_len = 0
14   # proto_root is the directory relative to which generated include paths should be
15   proto_root = ""
16   if ctx.label.package:
17     # The +1 is for the trailing slash.
18     label_len += len(ctx.label.package) + 1
19   if ctx.label.workspace_root:
20     label_len += len(ctx.label.workspace_root) + 1
21     proto_root = "/" + ctx.label.workspace_root
22
23   if ctx.executable.plugin:
24     outs += [proto.path[label_len:-len(".proto")] + ".grpc.pb.h" for proto in protos]
25     outs += [proto.path[label_len:-len(".proto")] + ".grpc.pb.cc" for proto in protos]
26     if ctx.attr.generate_mocks:
27       outs += [proto.path[label_len:-len(".proto")] + "_mock.grpc.pb.h" for proto in protos]
28   else:
29     outs += [proto.path[label_len:-len(".proto")] + ".pb.h" for proto in protos]
30     outs += [proto.path[label_len:-len(".proto")] + ".pb.cc" for proto in protos]
31   out_files = [ctx.actions.declare_file(out) for out in outs]
32   dir_out = str(ctx.genfiles_dir.path + proto_root)
33
34   arguments = []
35   if ctx.executable.plugin:
36     arguments += ["--plugin=protoc-gen-PLUGIN=" + ctx.executable.plugin.path]
37     flags = list(ctx.attr.flags)
38     if ctx.attr.generate_mocks:
39       flags.append("generate_mock_code=true")
40     arguments += ["--PLUGIN_out=" + ",".join(flags) + ":" + dir_out]
41     tools = [ctx.executable.plugin]
42   else:
43     arguments += ["--cpp_out=" + ",".join(ctx.attr.flags) + ":" + dir_out]
44     tools = []
45
46   # Import protos relative to their workspace root so that protoc prints the
47   # right include paths.
48   for include in includes:
49     directory = include.path
50     if directory.startswith("external"):
51       external_sep = directory.find("/")
52       repository_sep = directory.find("/", external_sep + 1)
53       arguments += ["--proto_path=" + directory[:repository_sep]]
54     else:
55       arguments += ["--proto_path=."]
56   # Include the output directory so that protoc puts the generated code in the
57   # right directory.
58   arguments += ["--proto_path={0}{1}".format(dir_out, proto_root)]
59   arguments += [proto.path for proto in protos]
60
61   # create a list of well known proto files if the argument is non-None
62   well_known_proto_files = []
63   if ctx.attr.well_known_protos:
64     f = ctx.attr.well_known_protos.files.to_list()[0].dirname
65     if f != "external/com_google_protobuf/src/google/protobuf":
66       print("Error: Only @com_google_protobuf//:well_known_protos is supported")
67     else:
68       # f points to "external/com_google_protobuf/src/google/protobuf"
69       # add -I argument to protoc so it knows where to look for the proto files.
70       arguments += ["-I{0}".format(f + "/../..")]
71       well_known_proto_files = [f for f in ctx.attr.well_known_protos.files]
72
73   ctx.actions.run(
74       inputs = protos + includes + well_known_proto_files,
75       tools = tools,
76       outputs = out_files,
77       executable = ctx.executable._protoc,
78       arguments = arguments,
79   )
80
81   return struct(files=depset(out_files))
82
83 _generate_cc = rule(
84     attrs = {
85         "srcs": attr.label_list(
86             mandatory = True,
87             allow_empty = False,
88             providers = ["proto"],
89         ),
90         "plugin": attr.label(
91             executable = True,
92             providers = ["files_to_run"],
93             cfg = "host",
94         ),
95         "flags": attr.string_list(
96             mandatory = False,
97             allow_empty = True,
98         ),
99         "well_known_protos" : attr.label(
100             mandatory = False,
101         ),
102         "generate_mocks" : attr.bool(
103             default = False,
104             mandatory = False,
105         ),
106         "_protoc": attr.label(
107             default = Label("//external:protocol_compiler"),
108             executable = True,
109             cfg = "host",
110         ),
111     },
112     # We generate .h files, so we need to output to genfiles.
113     output_to_genfiles = True,
114     implementation = generate_cc_impl,
115 )
116
117 def generate_cc(well_known_protos, **kwargs):
118   if well_known_protos:
119     _generate_cc(well_known_protos="@com_google_protobuf//:well_known_protos", **kwargs)
120   else:
121     _generate_cc(**kwargs)