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 26"""Public rules for using upb protos: 27 - upb_proto_library() 28 - upb_proto_reflection_library() 29""" 30 31load("@bazel_skylib//lib:paths.bzl", "paths") 32load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") 33load("@rules_proto//proto:defs.bzl", "ProtoInfo") # copybara:strip_for_google3 34 35# Generic support code ######################################################### 36 37_is_bazel = True # copybara:replace_for_google3 _is_bazel = False 38 39def _get_real_short_path(file): 40 # For some reason, files from other archives have short paths that look like: 41 # ../com_google_protobuf/google/protobuf/descriptor.proto 42 short_path = file.short_path 43 if short_path.startswith("../"): 44 second_slash = short_path.index("/", 3) 45 short_path = short_path[second_slash + 1:] 46 47 # Sometimes it has another few prefixes like: 48 # _virtual_imports/any_proto/google/protobuf/any.proto 49 # benchmarks/_virtual_imports/100_msgs_proto/benchmarks/100_msgs.proto 50 # We want just google/protobuf/any.proto. 51 virtual_imports = "_virtual_imports/" 52 if virtual_imports in short_path: 53 short_path = short_path.split(virtual_imports)[1].split("/", 1)[1] 54 return short_path 55 56def _get_real_root(file): 57 real_short_path = _get_real_short_path(file) 58 return file.path[:-len(real_short_path) - 1] 59 60def _generate_output_file(ctx, src, extension): 61 real_short_path = _get_real_short_path(src) 62 real_short_path = paths.relativize(real_short_path, ctx.label.package) 63 output_filename = paths.replace_extension(real_short_path, extension) 64 ret = ctx.actions.declare_file(output_filename) 65 return ret 66 67def _filter_none(elems): 68 out = [] 69 for elem in elems: 70 if elem: 71 out.append(elem) 72 return out 73 74def _cc_library_func(ctx, name, hdrs, srcs, copts, dep_ccinfos): 75 """Like cc_library(), but callable from rules. 76 77 Args: 78 ctx: Rule context. 79 name: Unique name used to generate output files. 80 hdrs: Public headers that can be #included from other rules. 81 srcs: C/C++ source files. 82 dep_ccinfos: CcInfo providers of dependencies we should build/link against. 83 84 Returns: 85 CcInfo provider for this compilation. 86 """ 87 88 compilation_contexts = [info.compilation_context for info in dep_ccinfos] 89 linking_contexts = [info.linking_context for info in dep_ccinfos] 90 toolchain = find_cpp_toolchain(ctx) 91 feature_configuration = cc_common.configure_features( 92 ctx = ctx, 93 cc_toolchain = toolchain, 94 requested_features = ctx.features, 95 unsupported_features = ctx.disabled_features, 96 ) 97 98 blaze_only_args = {} 99 100 if not _is_bazel: 101 blaze_only_args["grep_includes"] = ctx.file._grep_includes 102 103 (compilation_context, compilation_outputs) = cc_common.compile( 104 actions = ctx.actions, 105 feature_configuration = feature_configuration, 106 cc_toolchain = toolchain, 107 name = name, 108 srcs = srcs, 109 public_hdrs = hdrs, 110 user_compile_flags = copts, 111 compilation_contexts = compilation_contexts, 112 **blaze_only_args 113 ) 114 (linking_context, linking_outputs) = cc_common.create_linking_context_from_compilation_outputs( 115 actions = ctx.actions, 116 name = name, 117 feature_configuration = feature_configuration, 118 cc_toolchain = toolchain, 119 compilation_outputs = compilation_outputs, 120 linking_contexts = linking_contexts, 121 **blaze_only_args 122 ) 123 124 return CcInfo( 125 compilation_context = compilation_context, 126 linking_context = linking_context, 127 ) 128 129# Build setting for whether fasttable code generation is enabled ############### 130 131_FastTableEnabled = provider( 132 fields = { 133 "enabled": "whether fasttable is enabled", 134 }, 135) 136 137def fasttable_enabled_impl(ctx): 138 raw_setting = ctx.build_setting_value 139 140 if raw_setting: 141 # TODO(haberman): check that the target CPU supports fasttable. 142 pass 143 144 return _FastTableEnabled(enabled = raw_setting) 145 146upb_fasttable_enabled = rule( 147 implementation = fasttable_enabled_impl, 148 build_setting = config.bool(flag = True), 149) 150 151# Dummy rule to expose select() copts to aspects ############################## 152 153_UpbProtoLibraryCopts = provider( 154 fields = { 155 "copts": "copts for upb_proto_library()", 156 }, 157) 158 159def upb_proto_library_copts_impl(ctx): 160 return _UpbProtoLibraryCopts(copts = ctx.attr.copts) 161 162upb_proto_library_copts = rule( 163 implementation = upb_proto_library_copts_impl, 164 attrs = {"copts": attr.string_list(default = [])}, 165) 166 167# upb_proto_library / upb_proto_reflection_library shared code ################# 168 169GeneratedSrcsInfo = provider( 170 fields = { 171 "srcs": "list of srcs", 172 "hdrs": "list of hdrs", 173 }, 174) 175 176_UpbWrappedCcInfo = provider(fields = ["cc_info"]) 177_UpbDefsWrappedCcInfo = provider(fields = ["cc_info"]) 178_WrappedGeneratedSrcsInfo = provider(fields = ["srcs"]) 179_WrappedDefsGeneratedSrcsInfo = provider(fields = ["srcs"]) 180 181def _compile_upb_protos(ctx, generator, proto_info, proto_sources): 182 if len(proto_sources) == 0: 183 return GeneratedSrcsInfo(srcs = [], hdrs = []) 184 185 ext = "." + generator 186 tool = getattr(ctx.executable, "_gen_" + generator) 187 srcs = [_generate_output_file(ctx, name, ext + ".c") for name in proto_sources] 188 hdrs = [_generate_output_file(ctx, name, ext + ".h") for name in proto_sources] 189 transitive_sets = proto_info.transitive_descriptor_sets.to_list() 190 fasttable_enabled = (hasattr(ctx.attr, "_fasttable_enabled") and 191 ctx.attr._fasttable_enabled[_FastTableEnabled].enabled) 192 codegen_params = "fasttable:" if fasttable_enabled else "" 193 ctx.actions.run( 194 inputs = depset( 195 direct = [proto_info.direct_descriptor_set], 196 transitive = [proto_info.transitive_descriptor_sets], 197 ), 198 tools = [tool], 199 outputs = srcs + hdrs, 200 executable = ctx.executable._protoc, 201 arguments = [ 202 "--" + generator + "_out=" + codegen_params + _get_real_root(srcs[0]), 203 "--plugin=protoc-gen-" + generator + "=" + tool.path, 204 "--descriptor_set_in=" + ctx.configuration.host_path_separator.join([f.path for f in transitive_sets]), 205 ] + 206 [_get_real_short_path(file) for file in proto_sources], 207 progress_message = "Generating upb protos for :" + ctx.label.name, 208 ) 209 return GeneratedSrcsInfo(srcs = srcs, hdrs = hdrs) 210 211def _upb_proto_rule_impl(ctx): 212 if len(ctx.attr.deps) != 1: 213 fail("only one deps dependency allowed.") 214 dep = ctx.attr.deps[0] 215 216 if _WrappedDefsGeneratedSrcsInfo in dep: 217 srcs = dep[_WrappedDefsGeneratedSrcsInfo].srcs 218 elif _WrappedGeneratedSrcsInfo in dep: 219 srcs = dep[_WrappedGeneratedSrcsInfo].srcs 220 else: 221 fail("proto_library rule must generate _WrappedGeneratedSrcsInfo or " + 222 "_WrappedDefsGeneratedSrcsInfo (aspect should have handled this).") 223 224 if _UpbDefsWrappedCcInfo in dep: 225 cc_info = dep[_UpbDefsWrappedCcInfo].cc_info 226 elif _UpbWrappedCcInfo in dep: 227 cc_info = dep[_UpbWrappedCcInfo].cc_info 228 else: 229 fail("proto_library rule must generate _UpbWrappedCcInfo or " + 230 "_UpbDefsWrappedCcInfo (aspect should have handled this).") 231 232 lib = cc_info.linking_context.linker_inputs.to_list()[0].libraries[0] 233 files = _filter_none([ 234 lib.static_library, 235 lib.pic_static_library, 236 lib.dynamic_library, 237 ]) 238 return [ 239 DefaultInfo(files = depset(files + srcs.hdrs + srcs.srcs)), 240 srcs, 241 cc_info, 242 ] 243 244def _upb_proto_aspect_impl(target, ctx, generator, cc_provider, file_provider): 245 proto_info = target[ProtoInfo] 246 files = _compile_upb_protos(ctx, generator, proto_info, proto_info.direct_sources) 247 deps = ctx.rule.attr.deps + getattr(ctx.attr, "_" + generator) 248 dep_ccinfos = [dep[CcInfo] for dep in deps if CcInfo in dep] 249 dep_ccinfos += [dep[_UpbWrappedCcInfo].cc_info for dep in deps if _UpbWrappedCcInfo in dep] 250 dep_ccinfos += [dep[_UpbDefsWrappedCcInfo].cc_info for dep in deps if _UpbDefsWrappedCcInfo in dep] 251 if generator == "upbdefs": 252 if _UpbWrappedCcInfo not in target: 253 fail("Target should have _UpbDefsWrappedCcInfo provider") 254 dep_ccinfos += [target[_UpbWrappedCcInfo].cc_info] 255 cc_info = _cc_library_func( 256 ctx = ctx, 257 name = ctx.rule.attr.name + "." + generator, 258 hdrs = files.hdrs, 259 srcs = files.srcs, 260 copts = ctx.attr._copts[_UpbProtoLibraryCopts].copts, 261 dep_ccinfos = dep_ccinfos, 262 ) 263 return [cc_provider(cc_info = cc_info), file_provider(srcs = files)] 264 265def _upb_proto_library_aspect_impl(target, ctx): 266 return _upb_proto_aspect_impl(target, ctx, "upb", _UpbWrappedCcInfo, _WrappedGeneratedSrcsInfo) 267 268def _upb_proto_reflection_library_aspect_impl(target, ctx): 269 return _upb_proto_aspect_impl(target, ctx, "upbdefs", _UpbDefsWrappedCcInfo, _WrappedDefsGeneratedSrcsInfo) 270 271def _maybe_add(d): 272 if not _is_bazel: 273 d["_grep_includes"] = attr.label( 274 allow_single_file = True, 275 cfg = "host", 276 default = "//tools/cpp:grep-includes", 277 ) 278 return d 279 280# upb_proto_library() ########################################################## 281 282_upb_proto_library_aspect = aspect( 283 attrs = _maybe_add({ 284 "_copts": attr.label( 285 default = "//:upb_proto_library_copts__for_generated_code_only_do_not_use", 286 ), 287 "_gen_upb": attr.label( 288 executable = True, 289 cfg = "host", 290 default = "//upbc:protoc-gen-upb", 291 ), 292 "_protoc": attr.label( 293 executable = True, 294 cfg = "host", 295 default = "@com_google_protobuf//:protoc", 296 ), 297 "_cc_toolchain": attr.label( 298 default = "@bazel_tools//tools/cpp:current_cc_toolchain", 299 ), 300 "_upb": attr.label_list(default = [ 301 "//:generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me", 302 "//:upb", 303 ]), 304 "_fasttable_enabled": attr.label(default = "//:fasttable_enabled"), 305 }), 306 implementation = _upb_proto_library_aspect_impl, 307 provides = [ 308 _UpbWrappedCcInfo, 309 _WrappedGeneratedSrcsInfo, 310 ], 311 attr_aspects = ["deps"], 312 fragments = ["cpp"], 313 toolchains = ["@bazel_tools//tools/cpp:toolchain_type"], 314 incompatible_use_toolchain_transition = True, 315) 316 317upb_proto_library = rule( 318 output_to_genfiles = True, 319 implementation = _upb_proto_rule_impl, 320 attrs = { 321 "deps": attr.label_list( 322 aspects = [_upb_proto_library_aspect], 323 allow_rules = ["proto_library"], 324 providers = [ProtoInfo], 325 ), 326 }, 327) 328 329# upb_proto_reflection_library() ############################################### 330 331_upb_proto_reflection_library_aspect = aspect( 332 attrs = _maybe_add({ 333 "_copts": attr.label( 334 default = "//:upb_proto_library_copts__for_generated_code_only_do_not_use", 335 ), 336 "_gen_upbdefs": attr.label( 337 executable = True, 338 cfg = "host", 339 default = "//upbc:protoc-gen-upbdefs", 340 ), 341 "_protoc": attr.label( 342 executable = True, 343 cfg = "host", 344 default = "@com_google_protobuf//:protoc", 345 ), 346 "_cc_toolchain": attr.label( 347 default = "@bazel_tools//tools/cpp:current_cc_toolchain", 348 ), 349 "_upbdefs": attr.label_list( 350 default = [ 351 "//:generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me", 352 "//:upb", 353 "//:reflection", 354 ], 355 ), 356 }), 357 implementation = _upb_proto_reflection_library_aspect_impl, 358 provides = [ 359 _UpbDefsWrappedCcInfo, 360 _WrappedDefsGeneratedSrcsInfo, 361 ], 362 required_aspect_providers = [ 363 _UpbWrappedCcInfo, 364 _WrappedGeneratedSrcsInfo, 365 ], 366 attr_aspects = ["deps"], 367 fragments = ["cpp"], 368 toolchains = ["@bazel_tools//tools/cpp:toolchain_type"], 369 incompatible_use_toolchain_transition = True, 370) 371 372upb_proto_reflection_library = rule( 373 output_to_genfiles = True, 374 implementation = _upb_proto_rule_impl, 375 attrs = { 376 "deps": attr.label_list( 377 aspects = [ 378 _upb_proto_library_aspect, 379 _upb_proto_reflection_library_aspect, 380 ], 381 allow_rules = ["proto_library"], 382 providers = [ProtoInfo], 383 ), 384 }, 385) 386