Imported Upstream version 1.22.0
[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 load(
8     "//bazel:protobuf.bzl",
9     "get_include_protoc_args",
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
21 def _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
33 def _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
38 def _join_directories(directories):
39     massaged_directories = [directory for directory in directories if len(directory) != 0]
40     return "/".join(massaged_directories)
41
42 def generate_cc_impl(ctx):
43     """Implementation of the generate_cc rule."""
44     protos = [f for src in ctx.attr.srcs for f in src.proto.check_deps_sources.to_list()]
45     includes = [
46         f
47         for src in ctx.attr.srcs
48         for f in src.proto.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 += get_include_protoc_args(includes)
111
112     # Include the output directory so that protoc puts the generated code in the
113     # right directory.
114     arguments += ["--proto_path={0}{1}".format(dir_out, proto_root)]
115     arguments += [_get_srcs_file_path(proto) for proto in protos]
116
117     # create a list of well known proto files if the argument is non-None
118     well_known_proto_files = []
119     if ctx.attr.well_known_protos:
120         f = ctx.attr.well_known_protos.files.to_list()[0].dirname
121         if f != "external/com_google_protobuf/src/google/protobuf":
122             print(
123                 "Error: Only @com_google_protobuf//:well_known_protos is supported",
124             )
125         else:
126             # f points to "external/com_google_protobuf/src/google/protobuf"
127             # add -I argument to protoc so it knows where to look for the proto files.
128             arguments += ["-I{0}".format(f + "/../..")]
129             well_known_proto_files = [
130                 f
131                 for f in ctx.attr.well_known_protos.files.to_list()
132             ]
133
134     ctx.actions.run(
135         inputs = protos + includes + well_known_proto_files,
136         tools = tools,
137         outputs = out_files,
138         executable = ctx.executable._protoc,
139         arguments = arguments,
140     )
141
142     return struct(files = depset(out_files))
143
144 _generate_cc = rule(
145     attrs = {
146         "srcs": attr.label_list(
147             mandatory = True,
148             allow_empty = False,
149             providers = ["proto"],
150         ),
151         "plugin": attr.label(
152             executable = True,
153             providers = ["files_to_run"],
154             cfg = "host",
155         ),
156         "flags": attr.string_list(
157             mandatory = False,
158             allow_empty = True,
159         ),
160         "well_known_protos": attr.label(mandatory = False),
161         "generate_mocks": attr.bool(
162             default = False,
163             mandatory = False,
164         ),
165         "_protoc": attr.label(
166             default = Label("//external:protocol_compiler"),
167             executable = True,
168             cfg = "host",
169         ),
170     },
171     # We generate .h files, so we need to output to genfiles.
172     output_to_genfiles = True,
173     implementation = generate_cc_impl,
174 )
175
176 def generate_cc(well_known_protos, **kwargs):
177     if well_known_protos:
178         _generate_cc(
179             well_known_protos = "@com_google_protobuf//:well_known_protos",
180             **kwargs
181         )
182     else:
183         _generate_cc(**kwargs)