1# -*- mode: python; -*- PYTHON-PREPROCESSING-REQUIRED
2
3def _GetPath(ctx, path):
4  if ctx.label.workspace_root:
5    return ctx.label.workspace_root + '/' + path
6  else:
7    return path
8
9def _GenDir(ctx):
10  if not ctx.attr.includes:
11    return ctx.label.workspace_root
12  if not ctx.attr.includes[0]:
13    return _GetPath(ctx, ctx.label.package)
14  if not ctx.label.package:
15    return _GetPath(ctx, ctx.attr.includes[0])
16  return _GetPath(ctx, ctx.label.package + '/' + ctx.attr.includes[0])
17
18def _CcOuts(srcs, use_grpc_plugin=False):
19  ret = [s[:-len(".proto")] + ".pb.h" for s in srcs] + \
20        [s[:-len(".proto")] + ".pb.cc" for s in srcs]
21  if use_grpc_plugin:
22    ret += [s[:-len(".proto")] + ".grpc.pb.h" for s in srcs] + \
23           [s[:-len(".proto")] + ".grpc.pb.cc" for s in srcs]
24  return ret
25
26def _PyOuts(srcs):
27  return [s[:-len(".proto")] + "_pb2.py" for s in srcs]
28
29def _RelativeOutputPath(path, include, dest=""):
30  if include == None:
31    return path
32
33  if not path.startswith(include):
34    fail("Include path %s isn't part of the path %s." % (include, path))
35
36  if include and include[-1] != '/':
37    include = include + '/'
38  if dest and dest[-1] != '/':
39    dest = dest + '/'
40
41  path = path[len(include):]
42  return dest + path
43
44def _proto_gen_impl(ctx):
45  """General implementation for generating protos"""
46  srcs = ctx.files.srcs
47  deps = []
48  deps += ctx.files.srcs
49  gen_dir = _GenDir(ctx)
50  if gen_dir:
51    import_flags = ["-I" + gen_dir, "-I" + ctx.var["GENDIR"] + "/" + gen_dir]
52  else:
53    import_flags = ["-I."]
54
55  for dep in ctx.attr.deps:
56    import_flags += dep.proto.import_flags
57    deps += dep.proto.deps
58
59  args = []
60  if ctx.attr.gen_cc:
61    args += ["--cpp_out=" + ctx.var["GENDIR"] + "/" + gen_dir]
62  if ctx.attr.gen_py:
63    args += ["--python_out=" + ctx.var["GENDIR"] + "/" + gen_dir]
64
65  if ctx.executable.grpc_cpp_plugin:
66    args += ["--plugin=protoc-gen-grpc=" + ctx.executable.grpc_cpp_plugin.path]
67    args += ["--grpc_out=" + ctx.var["GENDIR"] + "/" + gen_dir]
68
69  if args:
70    ctx.action(
71        inputs=srcs + deps,
72        outputs=ctx.outputs.outs,
73        arguments=args + import_flags + [s.path for s in srcs],
74        executable=ctx.executable.protoc,
75    )
76
77  return struct(
78      proto=struct(
79          srcs=srcs,
80          import_flags=import_flags,
81          deps=deps,
82      ),
83  )
84
85_proto_gen = rule(
86    attrs = {
87        "srcs": attr.label_list(allow_files = True),
88        "deps": attr.label_list(providers = ["proto"]),
89        "includes": attr.string_list(),
90        "protoc": attr.label(
91            cfg = HOST_CFG,
92            executable = True,
93            single_file = True,
94            mandatory = True,
95        ),
96        "grpc_cpp_plugin": attr.label(
97            cfg = HOST_CFG,
98            executable = True,
99            single_file = True,
100        ),
101        "gen_cc": attr.bool(),
102        "gen_py": attr.bool(),
103        "outs": attr.output_list(),
104    },
105    output_to_genfiles = True,
106    implementation = _proto_gen_impl,
107)
108
109def cc_proto_library(
110        name,
111        srcs=[],
112        deps=[],
113        cc_libs=[],
114        include=None,
115        protoc="//:protoc",
116        internal_bootstrap_hack=False,
117        use_grpc_plugin=False,
118        default_runtime="//:protobuf",
119        **kargs):
120  """Bazel rule to create a C++ protobuf library from proto source files
121
122  NOTE: the rule is only an internal workaround to generate protos. The
123  interface may change and the rule may be removed when bazel has introduced
124  the native rule.
125
126  Args:
127    name: the name of the cc_proto_library.
128    srcs: the .proto files of the cc_proto_library.
129    deps: a list of dependency labels; must be cc_proto_library.
130    cc_libs: a list of other cc_library targets depended by the generated
131        cc_library.
132    include: a string indicating the include path of the .proto files.
133    protoc: the label of the protocol compiler to generate the sources.
134    internal_bootstrap_hack: a flag indicate the cc_proto_library is used only
135        for bootstraping. When it is set to True, no files will be generated.
136        The rule will simply be a provider for .proto files, so that other
137        cc_proto_library can depend on it.
138    use_grpc_plugin: a flag to indicate whether to call the grpc C++ plugin
139        when processing the proto files.
140    default_runtime: the implicitly default runtime which will be depended on by
141        the generated cc_library target.
142    **kargs: other keyword arguments that are passed to cc_library.
143
144  """
145
146  includes = []
147  if include != None:
148    includes = [include]
149
150  if internal_bootstrap_hack:
151    # For pre-checked-in generated files, we add the internal_bootstrap_hack
152    # which will skip the codegen action.
153    _proto_gen(
154        name=name + "_genproto",
155        srcs=srcs,
156        deps=[s + "_genproto" for s in deps],
157        includes=includes,
158        protoc=protoc,
159        visibility=["//visibility:public"],
160    )
161    # An empty cc_library to make rule dependency consistent.
162    native.cc_library(
163        name=name,
164        **kargs)
165    return
166
167  grpc_cpp_plugin = None
168  if use_grpc_plugin:
169    grpc_cpp_plugin = "//external:grpc_cpp_plugin"
170
171  outs = _CcOuts(srcs, use_grpc_plugin)
172
173  _proto_gen(
174      name=name + "_genproto",
175      srcs=srcs,
176      deps=[s + "_genproto" for s in deps],
177      includes=includes,
178      protoc=protoc,
179      grpc_cpp_plugin=grpc_cpp_plugin,
180      gen_cc=1,
181      outs=outs,
182      visibility=["//visibility:public"],
183  )
184
185  if default_runtime and not default_runtime in cc_libs:
186    cc_libs += [default_runtime]
187  if use_grpc_plugin:
188    cc_libs += ["//external:grpc_lib"]
189
190  native.cc_library(
191      name=name,
192      srcs=outs,
193      deps=cc_libs + deps,
194      includes=includes,
195      **kargs)
196
197
198def internal_gen_well_known_protos_java(srcs):
199  """Bazel rule to generate the gen_well_known_protos_java genrule
200
201  Args:
202    srcs: the well known protos
203  """
204  root = Label("%s//protobuf_java" % (REPOSITORY_NAME)).workspace_root
205  if root == "":
206    include = " -Isrc "
207  else:
208    include = " -I%s/src " % root
209  native.genrule(
210    name = "gen_well_known_protos_java",
211    srcs = srcs,
212    outs = [
213        "wellknown.srcjar",
214    ],
215    cmd = "$(location :protoc) --java_out=$(@D)/wellknown.jar" +
216          " %s $(SRCS) " % include +
217          " && mv $(@D)/wellknown.jar $(@D)/wellknown.srcjar",
218    tools = [":protoc"],
219  )
220
221
222def internal_copied_filegroup(name, srcs, strip_prefix, dest, **kwargs):
223  """Macro to copy files to a different directory and then create a filegroup.
224
225  This is used by the //:protobuf_python py_proto_library target to work around
226  an issue caused by Python source files that are part of the same Python
227  package being in separate directories.
228
229  Args:
230    srcs: The source files to copy and add to the filegroup.
231    strip_prefix: Path to the root of the files to copy.
232    dest: The directory to copy the source files into.
233    **kwargs: extra arguments that will be passesd to the filegroup.
234  """
235  outs = [_RelativeOutputPath(s, strip_prefix, dest) for s in srcs]
236
237  native.genrule(
238      name = name + "_genrule",
239      srcs = srcs,
240      outs = outs,
241      cmd = " && ".join(
242          ["cp $(location %s) $(location %s)" %
243           (s, _RelativeOutputPath(s, strip_prefix, dest)) for s in srcs]),
244  )
245
246  native.filegroup(
247      name = name,
248      srcs = outs,
249      **kwargs)
250
251
252def py_proto_library(
253        name,
254        srcs=[],
255        deps=[],
256        py_libs=[],
257        py_extra_srcs=[],
258        include=None,
259        default_runtime="//:protobuf_python",
260        protoc="//:protoc",
261        **kargs):
262  """Bazel rule to create a Python protobuf library from proto source files
263
264  NOTE: the rule is only an internal workaround to generate protos. The
265  interface may change and the rule may be removed when bazel has introduced
266  the native rule.
267
268  Args:
269    name: the name of the py_proto_library.
270    srcs: the .proto files of the py_proto_library.
271    deps: a list of dependency labels; must be py_proto_library.
272    py_libs: a list of other py_library targets depended by the generated
273        py_library.
274    py_extra_srcs: extra source files that will be added to the output
275        py_library. This attribute is used for internal bootstrapping.
276    include: a string indicating the include path of the .proto files.
277    default_runtime: the implicitly default runtime which will be depended on by
278        the generated py_library target.
279    protoc: the label of the protocol compiler to generate the sources.
280    **kargs: other keyword arguments that are passed to cc_library.
281
282  """
283  outs = _PyOuts(srcs)
284
285  includes = []
286  if include != None:
287    includes = [include]
288
289  _proto_gen(
290      name=name + "_genproto",
291      srcs=srcs,
292      deps=[s + "_genproto" for s in deps],
293      includes=includes,
294      protoc=protoc,
295      gen_py=1,
296      outs=outs,
297      visibility=["//visibility:public"],
298  )
299
300  if default_runtime and not default_runtime in py_libs + deps:
301    py_libs += [default_runtime]
302
303  native.py_library(
304      name=name,
305      srcs=outs+py_extra_srcs,
306      deps=py_libs+deps,
307      imports=includes,
308      **kargs)
309
310def internal_protobuf_py_tests(
311    name,
312    modules=[],
313    **kargs):
314  """Bazel rules to create batch tests for protobuf internal.
315
316  Args:
317    name: the name of the rule.
318    modules: a list of modules for tests. The macro will create a py_test for
319        each of the parameter with the source "google/protobuf/%s.py"
320    kargs: extra parameters that will be passed into the py_test.
321
322  """
323  for m in modules:
324    s = "python/google/protobuf/internal/%s.py" % m
325    native.py_test(
326        name="py_%s" % m,
327        srcs=[s],
328        main=s,
329        **kargs)
330