generate_objc.bzl 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. # Copyright 2021 The gRPC Authors
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """
  15. This module contains build rules relating to gRPC Objective-C.
  16. """
  17. load("@rules_proto//proto:defs.bzl", "ProtoInfo")
  18. load(
  19. "//bazel:protobuf.bzl",
  20. "get_include_directory",
  21. "get_plugin_args",
  22. "proto_path_to_generated_filename",
  23. )
  24. load(":grpc_util.bzl", "to_upper_camel_with_extension")
  25. _GRPC_PROTO_HEADER_FMT = "{}.pbrpc.h"
  26. _GRPC_PROTO_SRC_FMT = "{}.pbrpc.m"
  27. _PROTO_HEADER_FMT = "{}.pbobjc.h"
  28. _PROTO_SRC_FMT = "{}.pbobjc.m"
  29. _GENERATED_PROTOS_DIR = "_generated_protos"
  30. _GENERATE_HDRS = 1
  31. _GENERATE_SRCS = 2
  32. _GENERATE_NON_ARC_SRCS = 3
  33. def _generate_objc_impl(ctx):
  34. """Implementation of the generate_objc rule."""
  35. protos = [
  36. f
  37. for src in ctx.attr.deps
  38. for f in src[ProtoInfo].transitive_imports.to_list()
  39. ]
  40. target_package = _join_directories([ctx.label.workspace_root, ctx.label.package])
  41. files_with_rpc = [_label_to_full_file_path(f, target_package) for f in ctx.attr.srcs]
  42. outs = []
  43. for proto in protos:
  44. outs.append(_get_output_file_name_from_proto(proto, _PROTO_HEADER_FMT))
  45. outs.append(_get_output_file_name_from_proto(proto, _PROTO_SRC_FMT))
  46. file_path = _get_full_path_from_file(proto)
  47. if file_path in files_with_rpc:
  48. outs.append(_get_output_file_name_from_proto(proto, _GRPC_PROTO_HEADER_FMT))
  49. outs.append(_get_output_file_name_from_proto(proto, _GRPC_PROTO_SRC_FMT))
  50. out_files = [ctx.actions.declare_file(out) for out in outs]
  51. dir_out = _join_directories([
  52. str(ctx.genfiles_dir.path),
  53. target_package,
  54. _GENERATED_PROTOS_DIR,
  55. ])
  56. arguments = []
  57. tools = []
  58. if ctx.executable.plugin:
  59. arguments += get_plugin_args(
  60. ctx.executable.plugin,
  61. [],
  62. dir_out,
  63. False,
  64. )
  65. tools = [ctx.executable.plugin]
  66. arguments.append("--objc_out=" + dir_out)
  67. arguments.append("--proto_path=.")
  68. arguments += [
  69. "--proto_path={}".format(get_include_directory(i))
  70. for i in protos
  71. ]
  72. # Include the output directory so that protoc puts the generated code in the
  73. # right directory.
  74. arguments.append("--proto_path={}".format(dir_out))
  75. arguments += ["--proto_path={}".format(_get_directory_from_proto(proto)) for proto in protos]
  76. arguments += [_get_full_path_from_file(proto) for proto in protos]
  77. # create a list of well known proto files if the argument is non-None
  78. well_known_proto_files = []
  79. if ctx.attr.use_well_known_protos:
  80. f = ctx.attr.well_known_protos.files.to_list()[0].dirname
  81. # go two levels up so that #import "google/protobuf/..." is correct
  82. arguments.append("-I{0}".format(f + "/../.."))
  83. well_known_proto_files = ctx.attr.well_known_protos.files.to_list()
  84. ctx.actions.run(
  85. inputs = protos + well_known_proto_files,
  86. tools = tools,
  87. outputs = out_files,
  88. executable = ctx.executable._protoc,
  89. arguments = arguments,
  90. )
  91. return struct(files = depset(out_files)) # buildifier: disable=rule-impl-return
  92. def _label_to_full_file_path(src, package):
  93. if not src.startswith("//"):
  94. # Relative from current package
  95. if not src.startswith(":"):
  96. # "a.proto" -> ":a.proto"
  97. src = ":" + src
  98. src = "//" + package + src
  99. # Converts //path/to/package:File.ext to path/to/package/File.ext.
  100. src = src.replace("//", "")
  101. src = src.replace(":", "/")
  102. if src.startswith("/"):
  103. # "//:a.proto" -> "/a.proto" so remove the initial slash
  104. return src[1:]
  105. else:
  106. return src
  107. def _get_output_file_name_from_proto(proto, fmt):
  108. return proto_path_to_generated_filename(
  109. _GENERATED_PROTOS_DIR + "/" +
  110. _get_directory_from_proto(proto) + _get_slash_or_null_from_proto(proto) +
  111. to_upper_camel_with_extension(_get_file_name_from_proto(proto), "proto"),
  112. fmt,
  113. )
  114. def _get_file_name_from_proto(proto):
  115. return proto.path.rpartition("/")[2]
  116. def _get_slash_or_null_from_proto(proto):
  117. """Potentially returns empty (if the file is in the root directory)"""
  118. return proto.path.rpartition("/")[1]
  119. def _get_directory_from_proto(proto):
  120. return proto.path.rpartition("/")[0]
  121. def _get_full_path_from_file(file):
  122. gen_dir_length = 0
  123. # if file is generated, then prepare to remote its root
  124. # (including CPU architecture...)
  125. if not file.is_source:
  126. gen_dir_length = len(file.root.path) + 1
  127. return file.path[gen_dir_length:]
  128. def _join_directories(directories):
  129. massaged_directories = [directory for directory in directories if len(directory) != 0]
  130. return "/".join(massaged_directories)
  131. generate_objc = rule(
  132. attrs = {
  133. "deps": attr.label_list(
  134. mandatory = True,
  135. allow_empty = False,
  136. providers = [ProtoInfo],
  137. ),
  138. "plugin": attr.label(
  139. default = "@com_github_grpc_grpc//src/compiler:grpc_objective_c_plugin",
  140. executable = True,
  141. providers = ["files_to_run"],
  142. cfg = "host",
  143. ),
  144. "srcs": attr.string_list(
  145. mandatory = False,
  146. allow_empty = True,
  147. ),
  148. "use_well_known_protos": attr.bool(
  149. mandatory = False,
  150. default = False,
  151. ),
  152. "well_known_protos": attr.label(
  153. default = "@com_google_protobuf//:well_known_protos",
  154. ),
  155. "_protoc": attr.label(
  156. default = Label("//external:protocol_compiler"),
  157. executable = True,
  158. cfg = "host",
  159. ),
  160. },
  161. output_to_genfiles = True,
  162. implementation = _generate_objc_impl,
  163. )
  164. def _group_objc_files_impl(ctx):
  165. suffix = ""
  166. if ctx.attr.gen_mode == _GENERATE_HDRS:
  167. suffix = "h"
  168. elif ctx.attr.gen_mode == _GENERATE_SRCS:
  169. suffix = "pbrpc.m"
  170. elif ctx.attr.gen_mode == _GENERATE_NON_ARC_SRCS:
  171. suffix = "pbobjc.m"
  172. else:
  173. fail("Undefined gen_mode")
  174. out_files = [
  175. file
  176. for file in ctx.attr.src.files.to_list()
  177. if file.basename.endswith(suffix)
  178. ]
  179. return struct(files = depset(out_files)) # buildifier: disable=rule-impl-return
  180. generate_objc_hdrs = rule(
  181. attrs = {
  182. "src": attr.label(
  183. mandatory = True,
  184. ),
  185. "gen_mode": attr.int(
  186. default = _GENERATE_HDRS,
  187. ),
  188. },
  189. implementation = _group_objc_files_impl,
  190. )
  191. generate_objc_srcs = rule(
  192. attrs = {
  193. "src": attr.label(
  194. mandatory = True,
  195. ),
  196. "gen_mode": attr.int(
  197. default = _GENERATE_SRCS,
  198. ),
  199. },
  200. implementation = _group_objc_files_impl,
  201. )
  202. generate_objc_non_arc_srcs = rule(
  203. attrs = {
  204. "src": attr.label(
  205. mandatory = True,
  206. ),
  207. "gen_mode": attr.int(
  208. default = _GENERATE_NON_ARC_SRCS,
  209. ),
  210. },
  211. implementation = _group_objc_files_impl,
  212. )