protobuf.bzl 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. load("@bazel_tools//tools/jdk:toolchain_utils.bzl", "find_java_runtime_toolchain", "find_java_toolchain")
  2. load("@rules_proto//proto:defs.bzl", "ProtoInfo")
  3. # Borrowed from https://github.com/grpc/grpc-java/blob/v1.28.0/java_grpc_library.bzl#L59
  4. # "repository" here is for Bazel builds that span multiple WORKSPACES.
  5. def _path_ignoring_repository(f):
  6. # Bazel creates a _virtual_imports directory in case the .proto source files
  7. # need to be accessed at a path that's different from their source path:
  8. # https://github.com/bazelbuild/bazel/blob/0.27.1/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCommon.java#L289
  9. #
  10. # In that case, the import path of the .proto file is the path relative to
  11. # the virtual imports directory of the rule in question.
  12. virtual_imports = "/_virtual_imports/"
  13. if virtual_imports in f.path:
  14. return f.path.split(virtual_imports)[1].split("/", 1)[1]
  15. elif len(f.owner.workspace_root) == 0:
  16. # |f| is in the main repository
  17. return f.short_path
  18. else:
  19. # If |f| is a generated file, it will have "bazel-out/*/genfiles" prefix
  20. # before "external/workspace", so we need to add the starting index of "external/workspace"
  21. return f.path[f.path.find(f.owner.workspace_root) + len(f.owner.workspace_root) + 1:]
  22. def _protoc_cc_output_file(ctx, proto_file):
  23. file_path = proto_file.basename
  24. if proto_file.basename.endswith(".proto"):
  25. file_path = file_path[:-len(".proto")]
  26. return (file_path + ".pb.validate.h", file_path + ".pb.validate.cc")
  27. def _proto_sources(ctx):
  28. protos = []
  29. for dep in ctx.attr.deps:
  30. protos += [f for f in dep[ProtoInfo].direct_sources]
  31. return protos
  32. def _output_dir(ctx):
  33. dir_out = ctx.genfiles_dir.path
  34. if ctx.label.workspace_root:
  35. dir_out += "/" + ctx.label.workspace_root
  36. return dir_out
  37. def _protoc_gen_validate_cc_impl(ctx):
  38. """Generate C++ protos using protoc-gen-validate plugin"""
  39. protos = _proto_sources(ctx)
  40. out_files = []
  41. for f in protos:
  42. for out in _protoc_cc_output_file(ctx, f):
  43. out_files.append(ctx.actions.declare_file(out, sibling = f))
  44. dir_out = _output_dir(ctx)
  45. args = [
  46. "--validate_out=lang=cc:" + dir_out,
  47. ]
  48. return _protoc_gen_validate_impl(
  49. ctx = ctx,
  50. lang = "cc",
  51. protos = protos,
  52. out_files = out_files,
  53. protoc_args = args,
  54. package_command = "true",
  55. )
  56. def _protoc_gen_validate_impl(ctx, lang, protos, out_files, protoc_args, package_command):
  57. protoc_args.append("--plugin=protoc-gen-validate=" + ctx.executable._plugin.path)
  58. dir_out = ctx.genfiles_dir.path
  59. if ctx.label.workspace_root:
  60. dir_out += "/" + ctx.label.workspace_root
  61. tds = depset([], transitive = [dep[ProtoInfo].transitive_descriptor_sets for dep in ctx.attr.deps])
  62. descriptor_args = [ds.path for ds in tds.to_list()]
  63. if len(descriptor_args) != 0:
  64. protoc_args.append("--descriptor_set_in=%s" % ctx.configuration.host_path_separator.join(descriptor_args))
  65. package_command = package_command.format(dir_out = dir_out)
  66. ctx.actions.run_shell(
  67. outputs = out_files,
  68. inputs = protos + tds.to_list(),
  69. tools = [ctx.executable._plugin, ctx.executable._protoc],
  70. command = " && ".join([
  71. ctx.executable._protoc.path + " $@",
  72. package_command,
  73. ]),
  74. arguments = protoc_args + [_path_ignoring_repository(proto) for proto in protos],
  75. mnemonic = "ProtoGenValidate" + lang.capitalize() + "Generate",
  76. use_default_shell_env = True,
  77. )
  78. return struct(
  79. files = depset(out_files),
  80. )
  81. cc_proto_gen_validate = rule(
  82. attrs = {
  83. "deps": attr.label_list(
  84. mandatory = True,
  85. providers = [ProtoInfo],
  86. ),
  87. "_validate_deps": attr.label_list(
  88. default = [Label("@com_googlesource_code_re2//:re2")],
  89. ),
  90. "_protoc": attr.label(
  91. cfg = "host",
  92. default = Label("@com_google_protobuf//:protoc"),
  93. executable = True,
  94. allow_single_file = True,
  95. ),
  96. "_plugin": attr.label(
  97. cfg = "host",
  98. default = Label("@com_envoyproxy_protoc_gen_validate//:protoc-gen-validate"),
  99. allow_files = True,
  100. executable = True,
  101. ),
  102. },
  103. output_to_genfiles = True,
  104. implementation = _protoc_gen_validate_cc_impl,
  105. )
  106. _ProtoValidateSourceInfo = provider(
  107. fields = {
  108. "sources": "Depset of sources created by protoc with protoc-gen-validate plugin",
  109. },
  110. )
  111. def _create_include_path(include):
  112. return "--proto_path={0}={1}".format(_path_ignoring_repository(include), include.path)
  113. def _java_proto_gen_validate_aspect_impl(target, ctx):
  114. proto_info = target[ProtoInfo]
  115. includes = proto_info.transitive_imports
  116. srcs = proto_info.direct_sources
  117. options = ",".join(["lang=java"])
  118. srcjar = ctx.actions.declare_file("%s-validate-gensrc.jar" % ctx.label.name)
  119. args = ctx.actions.args()
  120. args.add(ctx.executable._plugin.path, format = "--plugin=protoc-gen-validate=%s")
  121. args.add("--validate_out={0}:{1}".format(options, srcjar.path))
  122. args.add_all(includes, map_each = _create_include_path)
  123. args.add_all(srcs, map_each = _path_ignoring_repository)
  124. ctx.actions.run(
  125. inputs = depset(transitive = [proto_info.transitive_imports]),
  126. outputs = [srcjar],
  127. executable = ctx.executable._protoc,
  128. arguments = [args],
  129. tools = [ctx.executable._plugin],
  130. progress_message = "Generating %s" % srcjar.path,
  131. )
  132. return [_ProtoValidateSourceInfo(
  133. sources = depset(
  134. [srcjar],
  135. transitive = [dep[_ProtoValidateSourceInfo].sources for dep in ctx.rule.attr.deps],
  136. ),
  137. )]
  138. _java_proto_gen_validate_aspect = aspect(
  139. _java_proto_gen_validate_aspect_impl,
  140. provides = [_ProtoValidateSourceInfo],
  141. attr_aspects = ["deps"],
  142. attrs = {
  143. "_protoc": attr.label(
  144. cfg = "host",
  145. default = Label("@com_google_protobuf//:protoc"),
  146. executable = True,
  147. allow_single_file = True,
  148. ),
  149. "_plugin": attr.label(
  150. cfg = "host",
  151. default = Label("@com_envoyproxy_protoc_gen_validate//:protoc-gen-validate"),
  152. allow_files = True,
  153. executable = True,
  154. ),
  155. },
  156. )
  157. def _java_proto_gen_validate_impl(ctx):
  158. source_jars = [source_jar for dep in ctx.attr.deps for source_jar in dep[_ProtoValidateSourceInfo].sources.to_list()]
  159. deps = [java_common.make_non_strict(dep[JavaInfo]) for dep in ctx.attr.java_deps]
  160. deps += [dep[JavaInfo] for dep in ctx.attr._validate_deps]
  161. java_info = java_common.compile(
  162. ctx,
  163. source_jars = source_jars,
  164. deps = deps,
  165. output_source_jar = ctx.outputs.srcjar,
  166. output = ctx.outputs.jar,
  167. java_toolchain = find_java_toolchain(ctx, ctx.attr._java_toolchain),
  168. host_javabase = find_java_runtime_toolchain(ctx, ctx.attr._host_javabase),
  169. )
  170. return [java_info]
  171. """Bazel rule to create a Java protobuf validation library from proto sources files.
  172. Args:
  173. deps: proto_library rules that contain the necessary .proto files
  174. java_deps: the java_proto_library of the protos being compiled.
  175. """
  176. java_proto_gen_validate = rule(
  177. attrs = {
  178. "deps": attr.label_list(
  179. providers = [ProtoInfo],
  180. aspects = [_java_proto_gen_validate_aspect],
  181. mandatory = True,
  182. ),
  183. "java_deps": attr.label_list(
  184. providers = [JavaInfo],
  185. mandatory = True,
  186. ),
  187. "_validate_deps": attr.label_list(
  188. default = [
  189. Label("@com_envoyproxy_protoc_gen_validate//validate:validate_java"),
  190. Label("@com_google_re2j//jar"),
  191. Label("@com_google_protobuf//:protobuf_java"),
  192. Label("@com_google_protobuf//:protobuf_java_util"),
  193. Label("@com_envoyproxy_protoc_gen_validate//java/pgv-java-stub/src/main/java/io/envoyproxy/pgv"),
  194. Label("@com_envoyproxy_protoc_gen_validate//java/pgv-java-validation/src/main/java/io/envoyproxy/pgv"),
  195. ],
  196. ),
  197. "_java_toolchain": attr.label(default = Label("@bazel_tools//tools/jdk:current_java_toolchain")),
  198. "_host_javabase": attr.label(
  199. cfg = "host",
  200. default = Label("@bazel_tools//tools/jdk:current_host_java_runtime"),
  201. ),
  202. },
  203. fragments = ["java"],
  204. provides = [JavaInfo],
  205. outputs = {
  206. "jar": "lib%{name}.jar",
  207. "srcjar": "lib%{name}-src.jar",
  208. },
  209. implementation = _java_proto_gen_validate_impl,
  210. )