blob: e0653321f8c2fd69f81a6c31cc0e15dfcdf19368 [file] [log] [blame]
Roger Chen7492b562018-10-29 21:58:47 -07001load("@bazel_skylib//lib:versions.bzl", "versions")
Jingwen Chenb2a19082018-01-12 18:42:22 -05002
Damien Martin-Guillerez76547e52016-01-15 14:01:37 +01003def _GetPath(ctx, path):
Paul Yangcecba292018-12-14 16:05:03 -08004 if ctx.label.workspace_root:
5 return ctx.label.workspace_root + "/" + path
6 else:
7 return path
Damien Martin-Guillerez76547e52016-01-15 14:01:37 +01008
Kristina Chodorow4e7ecde2017-01-25 14:10:56 -05009def _IsNewExternal(ctx):
Paul Yangcecba292018-12-14 16:05:03 -080010 # Bazel 0.4.4 and older have genfiles paths that look like:
11 # bazel-out/local-fastbuild/genfiles/external/repo/foo
12 # After the exec root rearrangement, they look like:
13 # ../repo/bazel-out/local-fastbuild/genfiles/foo
14 return ctx.label.workspace_root.startswith("../")
Kristina Chodorow4e7ecde2017-01-25 14:10:56 -050015
Jisi Liu993fb702015-10-19 17:19:49 -070016def _GenDir(ctx):
Paul Yangcecba292018-12-14 16:05:03 -080017 if _IsNewExternal(ctx):
18 # We are using the fact that Bazel 0.4.4+ provides repository-relative paths
19 # for ctx.genfiles_dir.
20 return ctx.genfiles_dir.path + (
21 "/" + ctx.attr.includes[0] if ctx.attr.includes and ctx.attr.includes[0] else ""
Fahrzin Hemmatid1403e52018-03-16 13:23:34 -070022 )
Jisi Liu39362b32015-10-14 17:12:11 -070023
Paul Yangcecba292018-12-14 16:05:03 -080024 # This means that we're either in the old version OR the new version in the local repo.
25 # Either way, appending the source path to the genfiles dir works.
26 return ctx.var["GENDIR"] + "/" + _SourceDir(ctx)
27
28def _SourceDir(ctx):
29 if not ctx.attr.includes:
30 return ctx.label.workspace_root
31 if not ctx.attr.includes[0]:
32 return _GetPath(ctx, ctx.label.package)
33 if not ctx.label.package:
34 return _GetPath(ctx, ctx.attr.includes[0])
35 return _GetPath(ctx, ctx.label.package + "/" + ctx.attr.includes[0])
36
37def _CcHdrs(srcs, use_grpc_plugin = False):
38 ret = [s[:-len(".proto")] + ".pb.h" for s in srcs]
39 if use_grpc_plugin:
40 ret += [s[:-len(".proto")] + ".grpc.pb.h" for s in srcs]
41 return ret
42
43def _CcSrcs(srcs, use_grpc_plugin = False):
44 ret = [s[:-len(".proto")] + ".pb.cc" for s in srcs]
45 if use_grpc_plugin:
46 ret += [s[:-len(".proto")] + ".grpc.pb.cc" for s in srcs]
47 return ret
48
49def _CcOuts(srcs, use_grpc_plugin = False):
50 return _CcHdrs(srcs, use_grpc_plugin) + _CcSrcs(srcs, use_grpc_plugin)
51
52def _PyOuts(srcs, use_grpc_plugin = False):
53 ret = [s[:-len(".proto")] + "_pb2.py" for s in srcs]
54 if use_grpc_plugin:
55 ret += [s[:-len(".proto")] + "_pb2_grpc.py" for s in srcs]
56 return ret
57
58def _RelativeOutputPath(path, include, dest = ""):
59 if include == None:
60 return path
61
62 if not path.startswith(include):
63 fail("Include path %s isn't part of the path %s." % (include, path))
64
65 if include and include[-1] != "/":
66 include = include + "/"
67 if dest and dest[-1] != "/":
68 dest = dest + "/"
69
70 path = path[len(include):]
71 return dest + path
72
73def _proto_gen_impl(ctx):
74 """General implementation for generating protos"""
75 srcs = ctx.files.srcs
76 deps = []
77 deps += ctx.files.srcs
78 source_dir = _SourceDir(ctx)
79 gen_dir = _GenDir(ctx).rstrip("/")
80 if source_dir:
81 import_flags = ["-I" + source_dir, "-I" + gen_dir]
82 else:
83 import_flags = ["-I."]
84
85 for dep in ctx.attr.deps:
86 import_flags += dep.proto.import_flags
87 deps += dep.proto.deps
88
89 if not ctx.attr.gen_cc and not ctx.attr.gen_py and not ctx.executable.plugin:
90 return struct(
91 proto = struct(
92 srcs = srcs,
93 import_flags = import_flags,
94 deps = deps,
95 ),
96 )
97
98 for src in srcs:
99 args = []
100
101 in_gen_dir = src.root.path == gen_dir
102 if in_gen_dir:
103 import_flags_real = []
104 for f in depset(import_flags).to_list():
105 path = f.replace("-I", "")
106 import_flags_real.append("-I$(realpath -s %s)" % path)
107
108 outs = []
109 use_grpc_plugin = (ctx.attr.plugin_language == "grpc" and ctx.attr.plugin)
110 path_tpl = "$(realpath %s)" if in_gen_dir else "%s"
111 if ctx.attr.gen_cc:
112 args += [("--cpp_out=" + path_tpl) % gen_dir]
113 outs.extend(_CcOuts([src.basename], use_grpc_plugin = use_grpc_plugin))
114 if ctx.attr.gen_py:
115 args += [("--python_out=" + path_tpl) % gen_dir]
116 outs.extend(_PyOuts([src.basename], use_grpc_plugin = use_grpc_plugin))
117
118 outs = [ctx.actions.declare_file(out, sibling = src) for out in outs]
119 inputs = [src] + deps
Benjamin Peterson6153f802019-06-03 08:56:33 -0700120 tools = [ctx.executable.protoc]
Paul Yangcecba292018-12-14 16:05:03 -0800121 if ctx.executable.plugin:
122 plugin = ctx.executable.plugin
123 lang = ctx.attr.plugin_language
124 if not lang and plugin.basename.startswith("protoc-gen-"):
125 lang = plugin.basename[len("protoc-gen-"):]
126 if not lang:
127 fail("cannot infer the target language of plugin", "plugin_language")
128
129 outdir = "." if in_gen_dir else gen_dir
130
131 if ctx.attr.plugin_options:
132 outdir = ",".join(ctx.attr.plugin_options) + ":" + outdir
133 args += [("--plugin=protoc-gen-%s=" + path_tpl) % (lang, plugin.path)]
134 args += ["--%s_out=%s" % (lang, outdir)]
Benjamin Peterson6153f802019-06-03 08:56:33 -0700135 tools.append(plugin)
Paul Yangcecba292018-12-14 16:05:03 -0800136
137 if not in_gen_dir:
138 ctx.actions.run(
139 inputs = inputs,
Benjamin Peterson6153f802019-06-03 08:56:33 -0700140 tools = tools,
Paul Yangcecba292018-12-14 16:05:03 -0800141 outputs = outs,
142 arguments = args + import_flags + [src.path],
143 executable = ctx.executable.protoc,
144 mnemonic = "ProtoCompile",
145 use_default_shell_env = True,
146 )
147 else:
148 for out in outs:
149 orig_command = " ".join(
150 ["$(realpath %s)" % ctx.executable.protoc.path] + args +
151 import_flags_real + ["-I.", src.basename],
152 )
153 command = ";".join([
154 'CMD="%s"' % orig_command,
155 "cd %s" % src.dirname,
156 "${CMD}",
157 "cd -",
158 ])
159 generated_out = "/".join([gen_dir, out.basename])
160 if generated_out != out.path:
161 command += ";mv %s %s" % (generated_out, out.path)
162 ctx.actions.run_shell(
Keith Smileyca3ead72019-05-21 17:31:34 -0700163 inputs = inputs,
Paul Yangcecba292018-12-14 16:05:03 -0800164 outputs = [out],
165 command = command,
166 mnemonic = "ProtoCompile",
Benjamin Peterson6153f802019-06-03 08:56:33 -0700167 tools = tools,
Paul Yangcecba292018-12-14 16:05:03 -0800168 use_default_shell_env = True,
169 )
170
171 return struct(
172 proto = struct(
173 srcs = srcs,
174 import_flags = import_flags,
175 deps = deps,
176 ),
177 )
Jisi Liu39362b32015-10-14 17:12:11 -0700178
Yuki Yugui Sonoda5977fb02016-06-01 16:23:15 +0900179proto_gen = rule(
Jisi Liu39362b32015-10-14 17:12:11 -0700180 attrs = {
Jisi Liuee8131a2015-10-14 17:20:05 -0700181 "srcs": attr.label_list(allow_files = True),
182 "deps": attr.label_list(providers = ["proto"]),
Jisi Liu53a56be2015-10-20 15:18:20 -0700183 "includes": attr.string_list(),
Jisi Liuee8131a2015-10-14 17:20:05 -0700184 "protoc": attr.label(
Vladimir Moskvaa86e6d82016-09-09 13:21:35 +0200185 cfg = "host",
Jisi Liuee8131a2015-10-14 17:20:05 -0700186 executable = True,
James Juddd5f0dac2018-08-14 21:55:35 -0600187 allow_single_file = True,
Jisi Liuee8131a2015-10-14 17:20:05 -0700188 mandatory = True,
189 ),
Yuki Yugui Sonoda5977fb02016-06-01 16:23:15 +0900190 "plugin": attr.label(
Vladimir Moskvaa86e6d82016-09-09 13:21:35 +0200191 cfg = "host",
Yuki Yugui Sonoda5977fb02016-06-01 16:23:15 +0900192 allow_files = True,
Manjunath Kudlurf0966a72016-02-22 14:30:43 -0800193 executable = True,
Manjunath Kudlurf0966a72016-02-22 14:30:43 -0800194 ),
Yuki Yugui Sonoda5977fb02016-06-01 16:23:15 +0900195 "plugin_language": attr.string(),
196 "plugin_options": attr.string_list(),
Jisi Liuee8131a2015-10-14 17:20:05 -0700197 "gen_cc": attr.bool(),
198 "gen_py": attr.bool(),
199 "outs": attr.output_list(),
200 },
201 output_to_genfiles = True,
Jisi Liu9c7d9c02015-10-15 10:51:32 -0700202 implementation = _proto_gen_impl,
Jisi Liu39362b32015-10-14 17:12:11 -0700203)
Yuki Yugui Sonoda5977fb02016-06-01 16:23:15 +0900204"""Generates codes from Protocol Buffers definitions.
205
206This rule helps you to implement Skylark macros specific to the target
207language. You should prefer more specific `cc_proto_library `,
208`py_proto_library` and others unless you are adding such wrapper macros.
209
210Args:
211 srcs: Protocol Buffers definition files (.proto) to run the protocol compiler
212 against.
213 deps: a list of dependency labels; must be other proto libraries.
214 includes: a list of include paths to .proto files.
215 protoc: the label of the protocol compiler to generate the sources.
216 plugin: the label of the protocol compiler plugin to be passed to the protocol
217 compiler.
218 plugin_language: the language of the generated sources
219 plugin_options: a list of options to be passed to the plugin
Kristina Chodorow4e7ecde2017-01-25 14:10:56 -0500220 gen_cc: generates C++ sources in addition to the ones from the plugin.
Yuki Yugui Sonoda5977fb02016-06-01 16:23:15 +0900221 gen_py: generates Python sources in addition to the ones from the plugin.
222 outs: a list of labels of the expected outputs from the protocol compiler.
223"""
Jisi Liu39362b32015-10-14 17:12:11 -0700224
225def cc_proto_library(
Jisi Liu125a91b2015-10-14 17:37:39 -0700226 name,
Paul Yangcecba292018-12-14 16:05:03 -0800227 srcs = [],
228 deps = [],
229 cc_libs = [],
230 include = None,
231 protoc = "@com_google_protobuf//:protoc",
232 internal_bootstrap_hack = False,
233 use_grpc_plugin = False,
234 default_runtime = "@com_google_protobuf//:protobuf",
Jisi Liu125a91b2015-10-14 17:37:39 -0700235 **kargs):
Paul Yangcecba292018-12-14 16:05:03 -0800236 """Bazel rule to create a C++ protobuf library from proto source files
Jisi Liu3101e732015-10-16 12:46:26 -0700237
Paul Yangcecba292018-12-14 16:05:03 -0800238 NOTE: the rule is only an internal workaround to generate protos. The
239 interface may change and the rule may be removed when bazel has introduced
240 the native rule.
Jisi Liud4bef7d2015-11-02 12:24:32 -0800241
Paul Yangcecba292018-12-14 16:05:03 -0800242 Args:
243 name: the name of the cc_proto_library.
244 srcs: the .proto files of the cc_proto_library.
245 deps: a list of dependency labels; must be cc_proto_library.
246 cc_libs: a list of other cc_library targets depended by the generated
247 cc_library.
248 include: a string indicating the include path of the .proto files.
249 protoc: the label of the protocol compiler to generate the sources.
250 internal_bootstrap_hack: a flag indicate the cc_proto_library is used only
251 for bootstraping. When it is set to True, no files will be generated.
252 The rule will simply be a provider for .proto files, so that other
253 cc_proto_library can depend on it.
254 use_grpc_plugin: a flag to indicate whether to call the grpc C++ plugin
255 when processing the proto files.
256 default_runtime: the implicitly default runtime which will be depended on by
257 the generated cc_library target.
258 **kargs: other keyword arguments that are passed to cc_library.
Jisi Liu3101e732015-10-16 12:46:26 -0700259
Paul Yangcecba292018-12-14 16:05:03 -0800260 """
Jisi Liu39362b32015-10-14 17:12:11 -0700261
Paul Yangcecba292018-12-14 16:05:03 -0800262 includes = []
263 if include != None:
264 includes = [include]
Jisi Liu53a56be2015-10-20 15:18:20 -0700265
Paul Yangcecba292018-12-14 16:05:03 -0800266 if internal_bootstrap_hack:
267 # For pre-checked-in generated files, we add the internal_bootstrap_hack
268 # which will skip the codegen action.
269 proto_gen(
270 name = name + "_genproto",
271 srcs = srcs,
272 deps = [s + "_genproto" for s in deps],
273 includes = includes,
274 protoc = protoc,
275 visibility = ["//visibility:public"],
276 )
277
278 # An empty cc_library to make rule dependency consistent.
279 native.cc_library(
280 name = name,
281 **kargs
282 )
283 return
284
285 grpc_cpp_plugin = None
286 if use_grpc_plugin:
287 grpc_cpp_plugin = "//external:grpc_cpp_plugin"
288
289 gen_srcs = _CcSrcs(srcs, use_grpc_plugin)
290 gen_hdrs = _CcHdrs(srcs, use_grpc_plugin)
291 outs = gen_srcs + gen_hdrs
292
Yuki Yugui Sonoda5977fb02016-06-01 16:23:15 +0900293 proto_gen(
Paul Yangcecba292018-12-14 16:05:03 -0800294 name = name + "_genproto",
295 srcs = srcs,
296 deps = [s + "_genproto" for s in deps],
297 includes = includes,
298 protoc = protoc,
299 plugin = grpc_cpp_plugin,
300 plugin_language = "grpc",
301 gen_cc = 1,
302 outs = outs,
303 visibility = ["//visibility:public"],
Jisi Liu39362b32015-10-14 17:12:11 -0700304 )
Paul Yangcecba292018-12-14 16:05:03 -0800305
306 if default_runtime and not default_runtime in cc_libs:
307 cc_libs = cc_libs + [default_runtime]
308 if use_grpc_plugin:
309 cc_libs = cc_libs + ["//external:grpc_lib"]
310
Jisi Liu39362b32015-10-14 17:12:11 -0700311 native.cc_library(
Paul Yangcecba292018-12-14 16:05:03 -0800312 name = name,
313 srcs = gen_srcs,
314 hdrs = gen_hdrs,
315 deps = cc_libs + deps,
316 includes = includes,
317 **kargs
318 )
Jisi Liu993fb702015-10-19 17:19:49 -0700319
Steven Parkesea188662016-02-25 07:53:19 -0800320def internal_gen_well_known_protos_java(srcs):
Paul Yangcecba292018-12-14 16:05:03 -0800321 """Bazel rule to generate the gen_well_known_protos_java genrule
Steven Parkesea188662016-02-25 07:53:19 -0800322
Paul Yangcecba292018-12-14 16:05:03 -0800323 Args:
324 srcs: the well known protos
325 """
326 root = Label("%s//protobuf_java" % (native.repository_name())).workspace_root
327 pkg = native.package_name() + "/" if native.package_name() else ""
328 if root == "":
329 include = " -I%ssrc " % pkg
330 else:
331 include = " -I%s/%ssrc " % (root, pkg)
332 native.genrule(
333 name = "gen_well_known_protos_java",
334 srcs = srcs,
335 outs = [
336 "wellknown.srcjar",
337 ],
338 cmd = "$(location :protoc) --java_out=$(@D)/wellknown.jar" +
339 " %s $(SRCS) " % include +
340 " && mv $(@D)/wellknown.jar $(@D)/wellknown.srcjar",
341 tools = [":protoc"],
342 )
Steven Parkesea188662016-02-25 07:53:19 -0800343
David Z. Chen02cd45c2016-05-20 16:49:04 -0700344def internal_copied_filegroup(name, srcs, strip_prefix, dest, **kwargs):
Paul Yangcecba292018-12-14 16:05:03 -0800345 """Macro to copy files to a different directory and then create a filegroup.
David Z. Chen02cd45c2016-05-20 16:49:04 -0700346
Paul Yangcecba292018-12-14 16:05:03 -0800347 This is used by the //:protobuf_python py_proto_library target to work around
348 an issue caused by Python source files that are part of the same Python
349 package being in separate directories.
David Z. Chen02cd45c2016-05-20 16:49:04 -0700350
Paul Yangcecba292018-12-14 16:05:03 -0800351 Args:
352 srcs: The source files to copy and add to the filegroup.
353 strip_prefix: Path to the root of the files to copy.
354 dest: The directory to copy the source files into.
355 **kwargs: extra arguments that will be passesd to the filegroup.
356 """
357 outs = [_RelativeOutputPath(s, strip_prefix, dest) for s in srcs]
David Z. Chen02cd45c2016-05-20 16:49:04 -0700358
Paul Yangcecba292018-12-14 16:05:03 -0800359 native.genrule(
360 name = name + "_genrule",
361 srcs = srcs,
362 outs = outs,
363 cmd = " && ".join(
364 ["cp $(location %s) $(location %s)" %
365 (s, _RelativeOutputPath(s, strip_prefix, dest)) for s in srcs],
366 ),
367 )
David Z. Chen02cd45c2016-05-20 16:49:04 -0700368
Paul Yangcecba292018-12-14 16:05:03 -0800369 native.filegroup(
370 name = name,
371 srcs = outs,
372 **kwargs
373 )
David Z. Chen02cd45c2016-05-20 16:49:04 -0700374
Jisi Liu993fb702015-10-19 17:19:49 -0700375def py_proto_library(
376 name,
Paul Yangcecba292018-12-14 16:05:03 -0800377 srcs = [],
378 deps = [],
379 py_libs = [],
380 py_extra_srcs = [],
381 include = None,
382 default_runtime = "@com_google_protobuf//:protobuf_python",
383 protoc = "@com_google_protobuf//:protoc",
384 use_grpc_plugin = False,
Jisi Liu993fb702015-10-19 17:19:49 -0700385 **kargs):
Paul Yangcecba292018-12-14 16:05:03 -0800386 """Bazel rule to create a Python protobuf library from proto source files
Jisi Liu7b948cc2015-10-19 17:56:27 -0700387
Paul Yangcecba292018-12-14 16:05:03 -0800388 NOTE: the rule is only an internal workaround to generate protos. The
389 interface may change and the rule may be removed when bazel has introduced
390 the native rule.
Jisi Liud4bef7d2015-11-02 12:24:32 -0800391
Paul Yangcecba292018-12-14 16:05:03 -0800392 Args:
393 name: the name of the py_proto_library.
394 srcs: the .proto files of the py_proto_library.
395 deps: a list of dependency labels; must be py_proto_library.
396 py_libs: a list of other py_library targets depended by the generated
397 py_library.
398 py_extra_srcs: extra source files that will be added to the output
399 py_library. This attribute is used for internal bootstrapping.
400 include: a string indicating the include path of the .proto files.
401 default_runtime: the implicitly default runtime which will be depended on by
402 the generated py_library target.
403 protoc: the label of the protocol compiler to generate the sources.
404 use_grpc_plugin: a flag to indicate whether to call the Python C++ plugin
405 when processing the proto files.
406 **kargs: other keyword arguments that are passed to cc_library.
Jisi Liu7b948cc2015-10-19 17:56:27 -0700407
Paul Yangcecba292018-12-14 16:05:03 -0800408 """
409 outs = _PyOuts(srcs, use_grpc_plugin)
Jisi Liu53a56be2015-10-20 15:18:20 -0700410
Paul Yangcecba292018-12-14 16:05:03 -0800411 includes = []
412 if include != None:
413 includes = [include]
Jisi Liu53a56be2015-10-20 15:18:20 -0700414
Paul Yangcecba292018-12-14 16:05:03 -0800415 grpc_python_plugin = None
416 if use_grpc_plugin:
417 grpc_python_plugin = "//external:grpc_python_plugin"
418 # Note: Generated grpc code depends on Python grpc module. This dependency
419 # is not explicitly listed in py_libs. Instead, host system is assumed to
420 # have grpc installed.
Wiktor Tomczak0fa31b22016-11-22 20:18:46 +0100421
Paul Yangcecba292018-12-14 16:05:03 -0800422 proto_gen(
423 name = name + "_genproto",
424 srcs = srcs,
425 deps = [s + "_genproto" for s in deps],
426 includes = includes,
427 protoc = protoc,
428 gen_py = 1,
429 outs = outs,
430 visibility = ["//visibility:public"],
431 plugin = grpc_python_plugin,
432 plugin_language = "grpc",
433 )
Jisi Liu993fb702015-10-19 17:19:49 -0700434
Paul Yangcecba292018-12-14 16:05:03 -0800435 if default_runtime and not default_runtime in py_libs + deps:
436 py_libs = py_libs + [default_runtime]
Jisi Liube92ffb2015-10-27 15:11:38 -0700437
Paul Yangcecba292018-12-14 16:05:03 -0800438 native.py_library(
439 name = name,
440 srcs = outs + py_extra_srcs,
441 deps = py_libs + deps,
442 imports = includes,
443 **kargs
444 )
Jisi Liu993fb702015-10-19 17:19:49 -0700445
446def internal_protobuf_py_tests(
Paul Yangcecba292018-12-14 16:05:03 -0800447 name,
448 modules = [],
449 **kargs):
450 """Bazel rules to create batch tests for protobuf internal.
Jisi Liu7b948cc2015-10-19 17:56:27 -0700451
Paul Yangcecba292018-12-14 16:05:03 -0800452 Args:
453 name: the name of the rule.
454 modules: a list of modules for tests. The macro will create a py_test for
455 each of the parameter with the source "google/protobuf/%s.py"
456 kargs: extra parameters that will be passed into the py_test.
Jisi Liu7b948cc2015-10-19 17:56:27 -0700457
Paul Yangcecba292018-12-14 16:05:03 -0800458 """
459 for m in modules:
460 s = "python/google/protobuf/internal/%s.py" % m
461 native.py_test(
462 name = "py_%s" % m,
463 srcs = [s],
464 main = s,
465 **kargs
466 )
Fahrzin Hemmati35119e32017-11-28 14:24:53 -0800467
468def check_protobuf_required_bazel_version():
Paul Yangcecba292018-12-14 16:05:03 -0800469 """For WORKSPACE files, to check the installed version of bazel.
Fahrzin Hemmati35119e32017-11-28 14:24:53 -0800470
Paul Yangcecba292018-12-14 16:05:03 -0800471 This ensures bazel supports our approach to proto_library() depending on a
472 copied filegroup. (Fixed in bazel 0.5.4)
473 """
474 versions.check(minimum_bazel_version = "0.5.4")