upb_proto_library.bzl 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. # Copyright (c) 2009-2021, Google LLC
  2. # All rights reserved.
  3. #
  4. # Redistribution and use in source and binary forms, with or without
  5. # modification, are permitted provided that the following conditions are met:
  6. # * Redistributions of source code must retain the above copyright
  7. # notice, this list of conditions and the following disclaimer.
  8. # * Redistributions in binary form must reproduce the above copyright
  9. # notice, this list of conditions and the following disclaimer in the
  10. # documentation and/or other materials provided with the distribution.
  11. # * Neither the name of Google LLC nor the
  12. # names of its contributors may be used to endorse or promote products
  13. # derived from this software without specific prior written permission.
  14. #
  15. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  16. # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  17. # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. # DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
  19. # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  20. # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  21. # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  22. # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  23. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  24. # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. """Public rules for using upb protos:
  26. - upb_proto_library()
  27. - upb_proto_reflection_library()
  28. """
  29. load("@bazel_skylib//lib:paths.bzl", "paths")
  30. load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
  31. load("@rules_proto//proto:defs.bzl", "ProtoInfo") # copybara:strip_for_google3
  32. # Generic support code #########################################################
  33. _is_bazel = True # copybara:replace_for_google3 _is_bazel = False
  34. def _get_real_short_path(file):
  35. # For some reason, files from other archives have short paths that look like:
  36. # ../com_google_protobuf/google/protobuf/descriptor.proto
  37. short_path = file.short_path
  38. if short_path.startswith("../"):
  39. second_slash = short_path.index("/", 3)
  40. short_path = short_path[second_slash + 1:]
  41. # Sometimes it has another few prefixes like:
  42. # _virtual_imports/any_proto/google/protobuf/any.proto
  43. # benchmarks/_virtual_imports/100_msgs_proto/benchmarks/100_msgs.proto
  44. # We want just google/protobuf/any.proto.
  45. virtual_imports = "_virtual_imports/"
  46. if virtual_imports in short_path:
  47. short_path = short_path.split(virtual_imports)[1].split("/", 1)[1]
  48. return short_path
  49. def _get_real_root(file):
  50. real_short_path = _get_real_short_path(file)
  51. return file.path[:-len(real_short_path) - 1]
  52. def _generate_output_file(ctx, src, extension):
  53. real_short_path = _get_real_short_path(src)
  54. real_short_path = paths.relativize(real_short_path, ctx.label.package)
  55. output_filename = paths.replace_extension(real_short_path, extension)
  56. ret = ctx.actions.declare_file(output_filename)
  57. return ret
  58. def _filter_none(elems):
  59. out = []
  60. for elem in elems:
  61. if elem:
  62. out.append(elem)
  63. return out
  64. def _cc_library_func(ctx, name, hdrs, srcs, copts, dep_ccinfos):
  65. """Like cc_library(), but callable from rules.
  66. Args:
  67. ctx: Rule context.
  68. name: Unique name used to generate output files.
  69. hdrs: Public headers that can be #included from other rules.
  70. srcs: C/C++ source files.
  71. dep_ccinfos: CcInfo providers of dependencies we should build/link against.
  72. Returns:
  73. CcInfo provider for this compilation.
  74. """
  75. compilation_contexts = [info.compilation_context for info in dep_ccinfos]
  76. linking_contexts = [info.linking_context for info in dep_ccinfos]
  77. toolchain = find_cpp_toolchain(ctx)
  78. feature_configuration = cc_common.configure_features(
  79. ctx = ctx,
  80. cc_toolchain = toolchain,
  81. requested_features = ctx.features,
  82. unsupported_features = ctx.disabled_features,
  83. )
  84. blaze_only_args = {}
  85. if not _is_bazel:
  86. blaze_only_args["grep_includes"] = ctx.file._grep_includes
  87. (compilation_context, compilation_outputs) = cc_common.compile(
  88. actions = ctx.actions,
  89. feature_configuration = feature_configuration,
  90. cc_toolchain = toolchain,
  91. name = name,
  92. srcs = srcs,
  93. public_hdrs = hdrs,
  94. user_compile_flags = copts,
  95. compilation_contexts = compilation_contexts,
  96. **blaze_only_args
  97. )
  98. (linking_context, linking_outputs) = cc_common.create_linking_context_from_compilation_outputs(
  99. actions = ctx.actions,
  100. name = name,
  101. feature_configuration = feature_configuration,
  102. cc_toolchain = toolchain,
  103. compilation_outputs = compilation_outputs,
  104. linking_contexts = linking_contexts,
  105. **blaze_only_args
  106. )
  107. return CcInfo(
  108. compilation_context = compilation_context,
  109. linking_context = linking_context,
  110. )
  111. # Build setting for whether fasttable code generation is enabled ###############
  112. _FastTableEnabled = provider(
  113. fields = {
  114. "enabled": "whether fasttable is enabled",
  115. },
  116. )
  117. def fasttable_enabled_impl(ctx):
  118. raw_setting = ctx.build_setting_value
  119. if raw_setting:
  120. # TODO(haberman): check that the target CPU supports fasttable.
  121. pass
  122. return _FastTableEnabled(enabled = raw_setting)
  123. upb_fasttable_enabled = rule(
  124. implementation = fasttable_enabled_impl,
  125. build_setting = config.bool(flag = True),
  126. )
  127. # Dummy rule to expose select() copts to aspects ##############################
  128. _UpbProtoLibraryCopts = provider(
  129. fields = {
  130. "copts": "copts for upb_proto_library()",
  131. },
  132. )
  133. def upb_proto_library_copts_impl(ctx):
  134. return _UpbProtoLibraryCopts(copts = ctx.attr.copts)
  135. upb_proto_library_copts = rule(
  136. implementation = upb_proto_library_copts_impl,
  137. attrs = {"copts": attr.string_list(default = [])},
  138. )
  139. # upb_proto_library / upb_proto_reflection_library shared code #################
  140. GeneratedSrcsInfo = provider(
  141. fields = {
  142. "srcs": "list of srcs",
  143. "hdrs": "list of hdrs",
  144. },
  145. )
  146. _UpbWrappedCcInfo = provider(fields = ["cc_info"])
  147. _UpbDefsWrappedCcInfo = provider(fields = ["cc_info"])
  148. _WrappedGeneratedSrcsInfo = provider(fields = ["srcs"])
  149. _WrappedDefsGeneratedSrcsInfo = provider(fields = ["srcs"])
  150. def _compile_upb_protos(ctx, generator, proto_info, proto_sources):
  151. if len(proto_sources) == 0:
  152. return GeneratedSrcsInfo(srcs = [], hdrs = [])
  153. ext = "." + generator
  154. tool = getattr(ctx.executable, "_gen_" + generator)
  155. srcs = [_generate_output_file(ctx, name, ext + ".c") for name in proto_sources]
  156. hdrs = [_generate_output_file(ctx, name, ext + ".h") for name in proto_sources]
  157. transitive_sets = proto_info.transitive_descriptor_sets.to_list()
  158. fasttable_enabled = (hasattr(ctx.attr, "_fasttable_enabled") and
  159. ctx.attr._fasttable_enabled[_FastTableEnabled].enabled)
  160. codegen_params = "fasttable:" if fasttable_enabled else ""
  161. ctx.actions.run(
  162. inputs = depset(
  163. direct = [proto_info.direct_descriptor_set],
  164. transitive = [proto_info.transitive_descriptor_sets],
  165. ),
  166. tools = [tool],
  167. outputs = srcs + hdrs,
  168. executable = ctx.executable._protoc,
  169. arguments = [
  170. "--" + generator + "_out=" + codegen_params + _get_real_root(srcs[0]),
  171. "--plugin=protoc-gen-" + generator + "=" + tool.path,
  172. "--descriptor_set_in=" + ctx.configuration.host_path_separator.join([f.path for f in transitive_sets]),
  173. ] +
  174. [_get_real_short_path(file) for file in proto_sources],
  175. progress_message = "Generating upb protos for :" + ctx.label.name,
  176. )
  177. return GeneratedSrcsInfo(srcs = srcs, hdrs = hdrs)
  178. def _upb_proto_rule_impl(ctx):
  179. if len(ctx.attr.deps) != 1:
  180. fail("only one deps dependency allowed.")
  181. dep = ctx.attr.deps[0]
  182. if _WrappedDefsGeneratedSrcsInfo in dep:
  183. srcs = dep[_WrappedDefsGeneratedSrcsInfo].srcs
  184. elif _WrappedGeneratedSrcsInfo in dep:
  185. srcs = dep[_WrappedGeneratedSrcsInfo].srcs
  186. else:
  187. fail("proto_library rule must generate _WrappedGeneratedSrcsInfo or " +
  188. "_WrappedDefsGeneratedSrcsInfo (aspect should have handled this).")
  189. if _UpbDefsWrappedCcInfo in dep:
  190. cc_info = dep[_UpbDefsWrappedCcInfo].cc_info
  191. elif _UpbWrappedCcInfo in dep:
  192. cc_info = dep[_UpbWrappedCcInfo].cc_info
  193. else:
  194. fail("proto_library rule must generate _UpbWrappedCcInfo or " +
  195. "_UpbDefsWrappedCcInfo (aspect should have handled this).")
  196. lib = cc_info.linking_context.linker_inputs.to_list()[0].libraries[0]
  197. files = _filter_none([
  198. lib.static_library,
  199. lib.pic_static_library,
  200. lib.dynamic_library,
  201. ])
  202. return [
  203. DefaultInfo(files = depset(files + srcs.hdrs + srcs.srcs)),
  204. srcs,
  205. cc_info,
  206. ]
  207. def _upb_proto_aspect_impl(target, ctx, generator, cc_provider, file_provider):
  208. proto_info = target[ProtoInfo]
  209. files = _compile_upb_protos(ctx, generator, proto_info, proto_info.direct_sources)
  210. deps = ctx.rule.attr.deps + getattr(ctx.attr, "_" + generator)
  211. dep_ccinfos = [dep[CcInfo] for dep in deps if CcInfo in dep]
  212. dep_ccinfos += [dep[_UpbWrappedCcInfo].cc_info for dep in deps if _UpbWrappedCcInfo in dep]
  213. dep_ccinfos += [dep[_UpbDefsWrappedCcInfo].cc_info for dep in deps if _UpbDefsWrappedCcInfo in dep]
  214. if generator == "upbdefs":
  215. if _UpbWrappedCcInfo not in target:
  216. fail("Target should have _UpbWrappedCcInfo provider")
  217. dep_ccinfos += [target[_UpbWrappedCcInfo].cc_info]
  218. cc_info = _cc_library_func(
  219. ctx = ctx,
  220. name = ctx.rule.attr.name + "." + generator,
  221. hdrs = files.hdrs,
  222. srcs = files.srcs,
  223. copts = ctx.attr._copts[_UpbProtoLibraryCopts].copts,
  224. dep_ccinfos = dep_ccinfos,
  225. )
  226. return [cc_provider(cc_info = cc_info), file_provider(srcs = files)]
  227. def _upb_proto_library_aspect_impl(target, ctx):
  228. return _upb_proto_aspect_impl(target, ctx, "upb", _UpbWrappedCcInfo, _WrappedGeneratedSrcsInfo)
  229. def _upb_proto_reflection_library_aspect_impl(target, ctx):
  230. return _upb_proto_aspect_impl(target, ctx, "upbdefs", _UpbDefsWrappedCcInfo, _WrappedDefsGeneratedSrcsInfo)
  231. def _maybe_add(d):
  232. if not _is_bazel:
  233. d["_grep_includes"] = attr.label(
  234. allow_single_file = True,
  235. cfg = "exec",
  236. default = "//tools/cpp:grep-includes",
  237. )
  238. return d
  239. # upb_proto_library() ##########################################################
  240. _upb_proto_library_aspect = aspect(
  241. attrs = _maybe_add({
  242. "_copts": attr.label(
  243. default = "//:upb_proto_library_copts__for_generated_code_only_do_not_use",
  244. ),
  245. "_gen_upb": attr.label(
  246. executable = True,
  247. cfg = "exec",
  248. default = "//upbc:protoc-gen-upb",
  249. ),
  250. "_protoc": attr.label(
  251. executable = True,
  252. cfg = "exec",
  253. default = "@com_google_protobuf//:protoc",
  254. ),
  255. "_cc_toolchain": attr.label(
  256. default = "@bazel_tools//tools/cpp:current_cc_toolchain",
  257. ),
  258. "_upb": attr.label_list(default = [
  259. "//:generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
  260. "//:upb",
  261. ]),
  262. "_fasttable_enabled": attr.label(default = "//:fasttable_enabled"),
  263. }),
  264. implementation = _upb_proto_library_aspect_impl,
  265. provides = [
  266. _UpbWrappedCcInfo,
  267. _WrappedGeneratedSrcsInfo,
  268. ],
  269. attr_aspects = ["deps"],
  270. fragments = ["cpp"],
  271. toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
  272. incompatible_use_toolchain_transition = True,
  273. )
  274. upb_proto_library = rule(
  275. output_to_genfiles = True,
  276. implementation = _upb_proto_rule_impl,
  277. attrs = {
  278. "deps": attr.label_list(
  279. aspects = [_upb_proto_library_aspect],
  280. allow_rules = ["proto_library"],
  281. providers = [ProtoInfo],
  282. ),
  283. },
  284. )
  285. # upb_proto_reflection_library() ###############################################
  286. _upb_proto_reflection_library_aspect = aspect(
  287. attrs = _maybe_add({
  288. "_copts": attr.label(
  289. default = "//:upb_proto_library_copts__for_generated_code_only_do_not_use",
  290. ),
  291. "_gen_upbdefs": attr.label(
  292. executable = True,
  293. cfg = "exec",
  294. default = "//upbc:protoc-gen-upbdefs",
  295. ),
  296. "_protoc": attr.label(
  297. executable = True,
  298. cfg = "exec",
  299. default = "@com_google_protobuf//:protoc",
  300. ),
  301. "_cc_toolchain": attr.label(
  302. default = "@bazel_tools//tools/cpp:current_cc_toolchain",
  303. ),
  304. "_upbdefs": attr.label_list(
  305. default = [
  306. "//:generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
  307. "//:upb",
  308. "//:reflection",
  309. ],
  310. ),
  311. }),
  312. implementation = _upb_proto_reflection_library_aspect_impl,
  313. provides = [
  314. _UpbDefsWrappedCcInfo,
  315. _WrappedDefsGeneratedSrcsInfo,
  316. ],
  317. required_aspect_providers = [
  318. _UpbWrappedCcInfo,
  319. _WrappedGeneratedSrcsInfo,
  320. ],
  321. attr_aspects = ["deps"],
  322. fragments = ["cpp"],
  323. toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
  324. incompatible_use_toolchain_transition = True,
  325. )
  326. upb_proto_reflection_library = rule(
  327. output_to_genfiles = True,
  328. implementation = _upb_proto_rule_impl,
  329. attrs = {
  330. "deps": attr.label_list(
  331. aspects = [
  332. _upb_proto_library_aspect,
  333. _upb_proto_reflection_library_aspect,
  334. ],
  335. allow_rules = ["proto_library"],
  336. providers = [ProtoInfo],
  337. ),
  338. },
  339. )