blob: 8468fbd341420e29714465646818b9189e060b2d [file] [log] [blame]
Philipp Schrader15b92e02020-11-08 10:35:57 -08001load("@//tools/build_rules:select.bzl", "compiler_select")
2
Brian Silverman9c614bc2016-02-15 20:20:02 -05003def _GetPath(ctx, path):
Austin Schuhf9724442018-10-28 20:30:21 -07004 if ctx.label.workspace_root:
5 return ctx.label.workspace_root + '/' + path
6 else:
7 return path
8
9def _IsNewExternal(ctx):
10 # 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("../")
Brian Silverman9c614bc2016-02-15 20:20:02 -050015
16def _GenDir(ctx):
Austin Schuhf9724442018-10-28 20:30:21 -070017 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 "")
22 # This means that we're either in the old version OR the new version in the local repo.
23 # Either way, appending the source path to the genfiles dir works.
24 return ctx.var["GENDIR"] + "/" + _SourceDir(ctx)
25
26def _SourceDir(ctx):
Brian Silverman9c614bc2016-02-15 20:20:02 -050027 if not ctx.attr.includes:
Austin Schuhf9724442018-10-28 20:30:21 -070028 return ctx.label.workspace_root
Brian Silverman9c614bc2016-02-15 20:20:02 -050029 if not ctx.attr.includes[0]:
30 return _GetPath(ctx, ctx.label.package)
31 if not ctx.label.package:
32 return _GetPath(ctx, ctx.attr.includes[0])
33 return _GetPath(ctx, ctx.label.package + '/' + ctx.attr.includes[0])
34
Austin Schuhf9724442018-10-28 20:30:21 -070035def _CcHdrs(srcs, use_grpc_plugin=False):
36 ret = [s[:-len(".proto")] + ".pb.h" for s in srcs]
37 if use_grpc_plugin:
38 ret += [s[:-len(".proto")] + ".grpc.pb.h" for s in srcs]
39 return ret
Brian Silverman9c614bc2016-02-15 20:20:02 -050040
Austin Schuhf9724442018-10-28 20:30:21 -070041def _CcSrcs(srcs, use_grpc_plugin=False):
42 ret = [s[:-len(".proto")] + ".pb.cc" for s in srcs]
43 if use_grpc_plugin:
44 ret += [s[:-len(".proto")] + ".grpc.pb.cc" for s in srcs]
45 return ret
Brian Silverman9c614bc2016-02-15 20:20:02 -050046
Austin Schuhf9724442018-10-28 20:30:21 -070047def _CcOuts(srcs, use_grpc_plugin=False):
48 return _CcHdrs(srcs, use_grpc_plugin) + _CcSrcs(srcs, use_grpc_plugin)
49
50def _PyOuts(srcs, use_grpc_plugin=False):
51 ret = [s[:-len(".proto")] + "_pb2.py" for s in srcs]
52 if use_grpc_plugin:
53 ret += [s[:-len(".proto")] + "_pb2_grpc.py" for s in srcs]
54 return ret
55
56def _RelativeOutputPath(path, include, dest=""):
Brian Silverman9c614bc2016-02-15 20:20:02 -050057 if include == None:
58 return path
59
60 if not path.startswith(include):
61 fail("Include path %s isn't part of the path %s." % (include, path))
62
63 if include and include[-1] != '/':
64 include = include + '/'
Austin Schuhf9724442018-10-28 20:30:21 -070065 if dest and dest[-1] != '/':
66 dest = dest + '/'
Brian Silverman9c614bc2016-02-15 20:20:02 -050067
68 path = path[len(include):]
Austin Schuhf9724442018-10-28 20:30:21 -070069 return dest + path
Brian Silverman9c614bc2016-02-15 20:20:02 -050070
71def _proto_gen_impl(ctx):
72 """General implementation for generating protos"""
73 srcs = ctx.files.srcs
74 deps = []
75 deps += ctx.files.srcs
Austin Schuhf9724442018-10-28 20:30:21 -070076 source_dir = _SourceDir(ctx)
Brian Silverman9c614bc2016-02-15 20:20:02 -050077 gen_dir = _GenDir(ctx)
Austin Schuhf9724442018-10-28 20:30:21 -070078 if source_dir:
79 import_flags = ["-I" + source_dir, "-I" + gen_dir]
Brian Silverman9c614bc2016-02-15 20:20:02 -050080 else:
81 import_flags = ["-I."]
82
83 for dep in ctx.attr.deps:
84 import_flags += dep.proto.import_flags
85 deps += dep.proto.deps
86
87 args = []
88 if ctx.attr.gen_cc:
Austin Schuhf9724442018-10-28 20:30:21 -070089 args += ["--cpp_out=" + gen_dir]
Brian Silverman9c614bc2016-02-15 20:20:02 -050090 if ctx.attr.gen_py:
Austin Schuhf9724442018-10-28 20:30:21 -070091 args += ["--python_out=" + gen_dir]
92
93 inputs = srcs + deps
94 if ctx.executable.plugin:
95 plugin = ctx.executable.plugin
96 lang = ctx.attr.plugin_language
97 if not lang and plugin.basename.startswith('protoc-gen-'):
98 lang = plugin.basename[len('protoc-gen-'):]
99 if not lang:
100 fail("cannot infer the target language of plugin", "plugin_language")
101
102 outdir = gen_dir
103 if ctx.attr.plugin_options:
104 outdir = ",".join(ctx.attr.plugin_options) + ":" + outdir
105 args += ["--plugin=protoc-gen-%s=%s" % (lang, plugin.path)]
106 args += ["--%s_out=%s" % (lang, outdir)]
107 inputs += [plugin]
Brian Silverman9c614bc2016-02-15 20:20:02 -0500108
109 if args:
Philipp Schrader5dd9eda2020-11-08 10:16:35 -0800110 ctx.actions.run(
Austin Schuhf9724442018-10-28 20:30:21 -0700111 inputs=inputs,
Brian Silverman9c614bc2016-02-15 20:20:02 -0500112 outputs=ctx.outputs.outs,
113 arguments=args + import_flags + [s.path for s in srcs],
114 executable=ctx.executable.protoc,
Austin Schuhf9724442018-10-28 20:30:21 -0700115 mnemonic="ProtoCompile",
116 use_default_shell_env=True,
Brian Silverman9c614bc2016-02-15 20:20:02 -0500117 )
118
119 return struct(
120 proto=struct(
121 srcs=srcs,
122 import_flags=import_flags,
123 deps=deps,
124 ),
125 )
126
Austin Schuhf9724442018-10-28 20:30:21 -0700127proto_gen = rule(
Brian Silverman9c614bc2016-02-15 20:20:02 -0500128 attrs = {
129 "srcs": attr.label_list(allow_files = True),
130 "deps": attr.label_list(providers = ["proto"]),
131 "includes": attr.string_list(),
132 "protoc": attr.label(
Brian Silverman7b8899e2018-06-30 19:19:24 -0700133 cfg = "host",
Brian Silverman9c614bc2016-02-15 20:20:02 -0500134 executable = True,
Philipp Schrader5dd9eda2020-11-08 10:16:35 -0800135 allow_single_file = True,
Brian Silverman9c614bc2016-02-15 20:20:02 -0500136 mandatory = True,
137 ),
Austin Schuhf9724442018-10-28 20:30:21 -0700138 "plugin": attr.label(
139 cfg = "host",
140 allow_files = True,
141 executable = True,
142 ),
143 "plugin_language": attr.string(),
144 "plugin_options": attr.string_list(),
Brian Silverman9c614bc2016-02-15 20:20:02 -0500145 "gen_cc": attr.bool(),
146 "gen_py": attr.bool(),
147 "outs": attr.output_list(),
148 },
149 output_to_genfiles = True,
150 implementation = _proto_gen_impl,
151)
Austin Schuhf9724442018-10-28 20:30:21 -0700152"""Generates codes from Protocol Buffers definitions.
153
154This rule helps you to implement Skylark macros specific to the target
155language. You should prefer more specific `cc_proto_library `,
156`py_proto_library` and others unless you are adding such wrapper macros.
157
158Args:
159 srcs: Protocol Buffers definition files (.proto) to run the protocol compiler
160 against.
161 deps: a list of dependency labels; must be other proto libraries.
162 includes: a list of include paths to .proto files.
163 protoc: the label of the protocol compiler to generate the sources.
164 plugin: the label of the protocol compiler plugin to be passed to the protocol
165 compiler.
166 plugin_language: the language of the generated sources
167 plugin_options: a list of options to be passed to the plugin
168 gen_cc: generates C++ sources in addition to the ones from the plugin.
169 gen_py: generates Python sources in addition to the ones from the plugin.
170 outs: a list of labels of the expected outputs from the protocol compiler.
171"""
172
173MSVC_COPTS = [
174 "/DHAVE_PTHREAD",
175 "/wd4018", # -Wno-sign-compare
176 "/wd4514", # -Wno-unused-function
177]
178
Austin Schuhf9724442018-10-28 20:30:21 -0700179COPTS = [
180 "-DHAVE_PTHREAD",
181 "-DGOOGLE_THIRD_PARTY_PROTOBUF",
182 "-Wall",
183 "-Wwrite-strings",
184 "-Woverloaded-virtual",
185 "-Wno-sign-compare",
186 "-Wno-unused-function",
187 "-Wno-unused-parameter",
188 "-Wno-format-nonliteral",
189 "-Wno-switch-enum",
190 "-Wno-missing-field-initializers",
191 "-Wno-ignored-qualifiers",
192 ] + compiler_select({
193 "gcc": [
194 "-Wno-error=cast-align",
James Kuszmaul9776b392023-01-14 14:08:08 -0800195 "-Wno-class-memaccess",
196 "-Wno-restrict",
Austin Schuhf9724442018-10-28 20:30:21 -0700197 ],
198 "clang": [
199 "-Wno-unused-const-variable",
200 "-Wno-unused-private-field",
Brian Silverman4c7235a2021-11-17 19:04:37 -0800201 "-Wno-tautological-type-limit-compare",
Austin Schuhf9724442018-10-28 20:30:21 -0700202 ],
203 })
Brian Silverman9c614bc2016-02-15 20:20:02 -0500204
205def cc_proto_library(
206 name,
207 srcs=[],
208 deps=[],
209 cc_libs=[],
210 include=None,
Austin Schuhf9724442018-10-28 20:30:21 -0700211 protoc="@com_google_protobuf//:protoc",
Brian Silverman9c614bc2016-02-15 20:20:02 -0500212 internal_bootstrap_hack=False,
Austin Schuhf9724442018-10-28 20:30:21 -0700213 use_grpc_plugin=False,
214 default_runtime="@com_google_protobuf//:protobuf",
Philipp Schraderdada1072020-11-24 11:34:46 -0800215 target_compatible_with = None,
Austin Schuhf9724442018-10-28 20:30:21 -0700216 copts = [],
Brian Silverman9c614bc2016-02-15 20:20:02 -0500217 **kargs):
218 """Bazel rule to create a C++ protobuf library from proto source files
219
220 NOTE: the rule is only an internal workaround to generate protos. The
221 interface may change and the rule may be removed when bazel has introduced
222 the native rule.
223
224 Args:
225 name: the name of the cc_proto_library.
226 srcs: the .proto files of the cc_proto_library.
227 deps: a list of dependency labels; must be cc_proto_library.
228 cc_libs: a list of other cc_library targets depended by the generated
229 cc_library.
230 include: a string indicating the include path of the .proto files.
231 protoc: the label of the protocol compiler to generate the sources.
232 internal_bootstrap_hack: a flag indicate the cc_proto_library is used only
233 for bootstraping. When it is set to True, no files will be generated.
234 The rule will simply be a provider for .proto files, so that other
235 cc_proto_library can depend on it.
Austin Schuhf9724442018-10-28 20:30:21 -0700236 use_grpc_plugin: a flag to indicate whether to call the grpc C++ plugin
237 when processing the proto files.
Brian Silverman9c614bc2016-02-15 20:20:02 -0500238 default_runtime: the implicitly default runtime which will be depended on by
239 the generated cc_library target.
240 **kargs: other keyword arguments that are passed to cc_library.
241
242 """
243
244 includes = []
245 if include != None:
246 includes = [include]
247
248 if internal_bootstrap_hack:
249 # For pre-checked-in generated files, we add the internal_bootstrap_hack
250 # which will skip the codegen action.
Austin Schuhf9724442018-10-28 20:30:21 -0700251 proto_gen(
Brian Silverman9c614bc2016-02-15 20:20:02 -0500252 name=name + "_genproto",
253 srcs=srcs,
254 deps=[s + "_genproto" for s in deps],
255 includes=includes,
256 protoc=protoc,
257 visibility=["//visibility:public"],
258 )
259 # An empty cc_library to make rule dependency consistent.
260 native.cc_library(
261 name=name,
262 **kargs)
263 return
264
Austin Schuhf9724442018-10-28 20:30:21 -0700265 grpc_cpp_plugin = None
266 if use_grpc_plugin:
267 grpc_cpp_plugin = "//external:grpc_cpp_plugin"
268
269 gen_srcs = _CcSrcs(srcs, use_grpc_plugin)
270 gen_hdrs = _CcHdrs(srcs, use_grpc_plugin)
271 outs = gen_srcs + gen_hdrs
272
273 proto_gen(
Brian Silverman9c614bc2016-02-15 20:20:02 -0500274 name=name + "_genproto",
275 srcs=srcs,
276 deps=[s + "_genproto" for s in deps],
277 includes=includes,
278 protoc=protoc,
Austin Schuhf9724442018-10-28 20:30:21 -0700279 plugin=grpc_cpp_plugin,
280 plugin_language="grpc",
Brian Silverman9c614bc2016-02-15 20:20:02 -0500281 gen_cc=1,
282 outs=outs,
Philipp Schraderdada1072020-11-24 11:34:46 -0800283 target_compatible_with = target_compatible_with,
Brian Silverman9c614bc2016-02-15 20:20:02 -0500284 visibility=["//visibility:public"],
285 )
286
287 if default_runtime and not default_runtime in cc_libs:
Austin Schuhbe8c9b12017-11-25 15:53:12 -0800288 cc_libs = cc_libs + [default_runtime]
Austin Schuhf9724442018-10-28 20:30:21 -0700289 if use_grpc_plugin:
290 cc_libs = cc_libs + ["//external:grpc_lib"]
Brian Silverman9c614bc2016-02-15 20:20:02 -0500291
292 native.cc_library(
293 name=name,
Austin Schuhf9724442018-10-28 20:30:21 -0700294 srcs=gen_srcs,
295 hdrs=gen_hdrs,
Brian Silverman9c614bc2016-02-15 20:20:02 -0500296 deps=cc_libs + deps,
297 includes=includes,
Austin Schuhf9724442018-10-28 20:30:21 -0700298 copts = COPTS + copts,
Philipp Schraderdada1072020-11-24 11:34:46 -0800299 target_compatible_with = target_compatible_with,
Brian Silverman9c614bc2016-02-15 20:20:02 -0500300 **kargs)
301
Austin Schuhf9724442018-10-28 20:30:21 -0700302def internal_gen_well_known_protos_java(srcs):
303 """Bazel rule to generate the gen_well_known_protos_java genrule
Brian Silverman9c614bc2016-02-15 20:20:02 -0500304
305 Args:
Austin Schuhf9724442018-10-28 20:30:21 -0700306 srcs: the well known protos
Brian Silverman9c614bc2016-02-15 20:20:02 -0500307 """
Philipp Schrader15b92e02020-11-08 10:35:57 -0800308 root = Label("%s//protobuf_java" % (native.repository_name())).workspace_root
309 pkg = native.package_name() + "/" if native.package_name() else ""
Austin Schuhf9724442018-10-28 20:30:21 -0700310 if root == "":
311 include = " -I%ssrc " % pkg
312 else:
313 include = " -I%s/%ssrc " % (root, pkg)
314 native.genrule(
315 name = "gen_well_known_protos_java",
316 srcs = srcs,
317 outs = [
318 "wellknown.srcjar",
319 ],
320 cmd = "$(location :protoc) --java_out=$(@D)/wellknown.jar" +
321 " %s $(SRCS) " % include +
322 " && mv $(@D)/wellknown.jar $(@D)/wellknown.srcjar",
323 tools = [":protoc"],
324 )
325
326def internal_copied_filegroup(name, srcs, strip_prefix, dest, **kwargs):
327 """Macro to copy files to a different directory and then create a filegroup.
328
329 This is used by the //:protobuf_python py_proto_library target to work around
330 an issue caused by Python source files that are part of the same Python
331 package being in separate directories.
332
333 Args:
334 srcs: The source files to copy and add to the filegroup.
335 strip_prefix: Path to the root of the files to copy.
336 dest: The directory to copy the source files into.
337 **kwargs: extra arguments that will be passesd to the filegroup.
338 """
339 outs = [_RelativeOutputPath(s, strip_prefix, dest) for s in srcs]
Brian Silverman9c614bc2016-02-15 20:20:02 -0500340
341 native.genrule(
Austin Schuhf9724442018-10-28 20:30:21 -0700342 name = name + "_genrule",
343 srcs = srcs,
344 outs = outs,
345 cmd = " && ".join(
346 ["cp $(location %s) $(location %s)" %
347 (s, _RelativeOutputPath(s, strip_prefix, dest)) for s in srcs]),
348 )
Brian Silverman9c614bc2016-02-15 20:20:02 -0500349
350 native.filegroup(
Austin Schuhf9724442018-10-28 20:30:21 -0700351 name = name,
352 srcs = outs,
353 **kwargs)
Brian Silverman9c614bc2016-02-15 20:20:02 -0500354
Brian Silverman9c614bc2016-02-15 20:20:02 -0500355def py_proto_library(
356 name,
357 srcs=[],
358 deps=[],
359 py_libs=[],
360 py_extra_srcs=[],
Brian Silverman7b8899e2018-06-30 19:19:24 -0700361 py_imports=[],
Brian Silverman9c614bc2016-02-15 20:20:02 -0500362 include=None,
Austin Schuhf9724442018-10-28 20:30:21 -0700363 default_runtime="@com_google_protobuf//:protobuf_python",
364 protoc="@com_google_protobuf//:protoc",
365 use_grpc_plugin=False,
Brian Silverman9c614bc2016-02-15 20:20:02 -0500366 **kargs):
367 """Bazel rule to create a Python protobuf library from proto source files
368
369 NOTE: the rule is only an internal workaround to generate protos. The
370 interface may change and the rule may be removed when bazel has introduced
371 the native rule.
372
373 Args:
374 name: the name of the py_proto_library.
375 srcs: the .proto files of the py_proto_library.
376 deps: a list of dependency labels; must be py_proto_library.
377 py_libs: a list of other py_library targets depended by the generated
378 py_library.
379 py_extra_srcs: extra source files that will be added to the output
380 py_library. This attribute is used for internal bootstrapping.
381 include: a string indicating the include path of the .proto files.
382 default_runtime: the implicitly default runtime which will be depended on by
383 the generated py_library target.
384 protoc: the label of the protocol compiler to generate the sources.
Austin Schuhf9724442018-10-28 20:30:21 -0700385 use_grpc_plugin: a flag to indicate whether to call the Python C++ plugin
386 when processing the proto files.
Brian Silverman9c614bc2016-02-15 20:20:02 -0500387 **kargs: other keyword arguments that are passed to cc_library.
388
389 """
Austin Schuhf9724442018-10-28 20:30:21 -0700390 outs = _PyOuts(srcs, use_grpc_plugin)
Brian Silverman9c614bc2016-02-15 20:20:02 -0500391
392 includes = []
393 if include != None:
394 includes = [include]
395
Austin Schuhf9724442018-10-28 20:30:21 -0700396 grpc_python_plugin = None
397 if use_grpc_plugin:
398 grpc_python_plugin = "//external:grpc_python_plugin"
399 # Note: Generated grpc code depends on Python grpc module. This dependency
400 # is not explicitly listed in py_libs. Instead, host system is assumed to
401 # have grpc installed.
402
403 proto_gen(
Brian Silverman9c614bc2016-02-15 20:20:02 -0500404 name=name + "_genproto",
405 srcs=srcs,
406 deps=[s + "_genproto" for s in deps],
407 includes=includes,
408 protoc=protoc,
409 gen_py=1,
410 outs=outs,
411 visibility=["//visibility:public"],
Austin Schuhf9724442018-10-28 20:30:21 -0700412 plugin=grpc_python_plugin,
413 plugin_language="grpc"
Brian Silverman9c614bc2016-02-15 20:20:02 -0500414 )
415
Brian Silverman9c614bc2016-02-15 20:20:02 -0500416 if default_runtime and not default_runtime in py_libs + deps:
Austin Schuhbe8c9b12017-11-25 15:53:12 -0800417 py_libs = py_libs + [default_runtime]
Brian Silverman9c614bc2016-02-15 20:20:02 -0500418
419 native.py_library(
420 name=name,
421 srcs=outs+py_extra_srcs,
422 deps=py_libs+deps,
Austin Schuhf9724442018-10-28 20:30:21 -0700423 imports=includes,
Brian Silverman9c614bc2016-02-15 20:20:02 -0500424 **kargs)
425
426def internal_protobuf_py_tests(
427 name,
428 modules=[],
429 **kargs):
430 """Bazel rules to create batch tests for protobuf internal.
431
432 Args:
433 name: the name of the rule.
434 modules: a list of modules for tests. The macro will create a py_test for
435 each of the parameter with the source "google/protobuf/%s.py"
436 kargs: extra parameters that will be passed into the py_test.
437
438 """
439 for m in modules:
Austin Schuhf9724442018-10-28 20:30:21 -0700440 s = "python/google/protobuf/internal/%s.py" % m
Brian Silverman9c614bc2016-02-15 20:20:02 -0500441 native.py_test(
442 name="py_%s" % m,
443 srcs=[s],
444 main=s,
445 **kargs)
Austin Schuhf9724442018-10-28 20:30:21 -0700446
447
448def check_protobuf_required_bazel_version():
449 """For WORKSPACE files, to check the installed version of bazel.
450
451 This ensures bazel supports our approach to proto_library() depending on a
452 copied filegroup. (Fixed in bazel 0.5.4)
453 """
454 expected = apple_common.dotted_version("0.5.4")
455 current = apple_common.dotted_version(native.bazel_version)
456 if current.compare_to(expected) < 0:
457 fail("Bazel must be newer than 0.5.4")