1# Copyright 2022 The Pigweed Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4# use this file except in compliance with the License. You may obtain a copy of 5# the License at 6# 7# https://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, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations under 13# the License. 14"""WORK IN PROGRESS! 15 16# Overview of implementation 17 18(If you just want to use the macros, see their docstrings; this section is 19intended to orient future maintainers.) 20 21Proto code generation is carried out by the _pwpb_proto_library, 22_nanopb_proto_library, _pw_raw_rpc_proto_library and 23_pw_nanopb_rpc_proto_library rules using aspects 24(https://docs.bazel.build/versions/main/skylark/aspects.html). 25 26As an example, _pwpb_proto_library has a single proto_library as a dependency, 27but that proto_library may depend on other proto_library targets; as a result, 28the generated .pwpb.h file #include's .pwpb.h files generated from the 29dependency proto_libraries. The aspect propagates along the proto_library 30dependency graph, running the proto compiler on each proto_library in the 31original target's transitive dependencies, ensuring that we're not missing any 32.pwpb.h files at C++ compile time. 33 34Although we have a separate rule for each protocol compiler plugin 35(_pwpb_proto_library, _nanopb_proto_library, _pw_raw_rpc_proto_library, 36_pw_nanopb_rpc_proto_library), they actually share an implementation 37(_impl_pw_proto_library) and use similar aspects, all generated by 38_proto_compiler_aspect. 39""" 40 41load("@bazel_skylib//lib:paths.bzl", "paths") 42load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "use_cpp_toolchain") 43load("@rules_proto//proto:defs.bzl", "ProtoInfo") 44load( 45 "//pw_build/bazel_internal:pigweed_internal.bzl", 46 _compile_cc = "compile_cc", 47) 48 49# For Copybara use only 50ADDITIONAL_PWPB_DEPS = [] 51 52# TODO: b/373693434 - The `oneof_callbacks` parameter is temporary to assist 53# with migration. 54def pwpb_proto_library(name, deps, tags = None, visibility = None, oneof_callbacks = True): 55 """A C++ proto library generated using pw_protobuf. 56 57 Attributes: 58 deps: proto_library targets for which to generate this library. 59 """ 60 if oneof_callbacks: 61 _pwpb_proto_library( 62 name = name, 63 protos = deps, 64 deps = [ 65 Label("//pw_assert"), 66 Label("//pw_containers:vector"), 67 Label("//pw_preprocessor"), 68 Label("//pw_protobuf"), 69 Label("//pw_result"), 70 Label("//pw_span"), 71 Label("//pw_status"), 72 Label("//pw_string:string"), 73 ] + ADDITIONAL_PWPB_DEPS, 74 tags = tags, 75 visibility = visibility, 76 ) 77 else: 78 _pwpb_legacy_oneof_proto_library( 79 name = name, 80 protos = deps, 81 deps = [ 82 Label("//pw_assert"), 83 Label("//pw_containers:vector"), 84 Label("//pw_preprocessor"), 85 Label("//pw_protobuf"), 86 Label("//pw_result"), 87 Label("//pw_span"), 88 Label("//pw_status"), 89 Label("//pw_string:string"), 90 ] + ADDITIONAL_PWPB_DEPS, 91 tags = tags, 92 visibility = visibility, 93 ) 94 95def pwpb_rpc_proto_library(name, deps, pwpb_proto_library_deps, tags = None, visibility = None): 96 """A pwpb_rpc proto library target. 97 98 Attributes: 99 deps: proto_library targets for which to generate this library. 100 pwpb_proto_library_deps: A pwpb_proto_library generated 101 from the same proto_library. Required. 102 """ 103 _pw_pwpb_rpc_proto_library( 104 name = name, 105 protos = deps, 106 deps = [ 107 Label("//pw_protobuf"), 108 Label("//pw_rpc"), 109 Label("//pw_rpc/pwpb:client_api"), 110 Label("//pw_rpc/pwpb:server_api"), 111 ] + pwpb_proto_library_deps, 112 tags = tags, 113 visibility = visibility, 114 ) 115 116def raw_rpc_proto_library(name, deps, tags = None, visibility = None): 117 """A raw C++ RPC proto library.""" 118 _pw_raw_rpc_proto_library( 119 name = name, 120 protos = deps, 121 deps = [ 122 Label("//pw_rpc"), 123 Label("//pw_rpc/raw:client_api"), 124 Label("//pw_rpc/raw:server_api"), 125 ], 126 tags = tags, 127 visibility = visibility, 128 ) 129 130# TODO: b/234873954 - Enable unused variable check. 131# buildifier: disable=unused-variable 132def nanopb_proto_library(name, deps, tags = [], visibility = None, options = None): 133 """A C++ proto library generated using pw_protobuf. 134 135 Attributes: 136 deps: proto_library targets for which to generate this library. 137 """ 138 139 # TODO(tpudlik): Find a way to get Nanopb to generate nested structs. 140 # Otherwise add the manual tag to the resulting library, preventing it 141 # from being built unless directly depended on. e.g. The 'Pigweed' 142 # message in 143 # pw_protobuf/pw_protobuf_test_protos/full_test.proto will fail to 144 # compile as it has a self referring nested message. According to 145 # the docs 146 # https://jpa.kapsi.fi/nanopb/docs/reference.html#proto-file-options 147 # and https://github.com/nanopb/nanopb/issues/433 it seems like it 148 # should be possible to configure nanopb to generate nested structs via 149 # flags in .options files. 150 # 151 # One issue is nanopb doesn't silently ignore unknown options in .options 152 # files so we can't share .options files with pwpb. 153 extra_tags = ["manual"] 154 _nanopb_proto_library( 155 name = name, 156 protos = deps, 157 deps = [ 158 "@com_github_nanopb_nanopb//:nanopb", 159 Label("//pw_assert"), 160 Label("//pw_containers:vector"), 161 Label("//pw_preprocessor"), 162 Label("//pw_result"), 163 Label("//pw_span"), 164 Label("//pw_status"), 165 Label("//pw_string:string"), 166 ], 167 tags = tags + extra_tags, 168 visibility = visibility, 169 ) 170 171def nanopb_rpc_proto_library(name, deps, nanopb_proto_library_deps, tags = [], visibility = None): 172 """A C++ RPC proto library using nanopb. 173 174 Attributes: 175 deps: proto_library targets for which to generate this library. 176 nanopb_proto_library_deps: A pw_nanopb_cc_library generated 177 from the same proto_library. Required. 178 """ 179 180 # See comment in nanopb_proto_library. 181 extra_tags = ["manual"] 182 _pw_nanopb_rpc_proto_library( 183 name = name, 184 protos = deps, 185 # TODO: b/339280821 - This is required to avoid breaking internal 186 # Google builds but shouldn't matter for any external user. Remove this 187 # when possible. 188 features = ["-layering_check"], 189 deps = [ 190 Label("//pw_rpc"), 191 Label("//pw_rpc/nanopb:client_api"), 192 Label("//pw_rpc/nanopb:server_api"), 193 ] + nanopb_proto_library_deps, 194 tags = tags + extra_tags, 195 visibility = visibility, 196 ) 197 198def pw_proto_library( 199 name, 200 deps, 201 visibility = None, 202 tags = [], 203 nanopb_options = None, 204 enabled_targets = None): 205 """Generate Pigweed proto C++ code. 206 207 DEPRECATED. This macro is deprecated and will be removed in a future 208 Pigweed version. Please use the single-target macros above. 209 210 Args: 211 name: The name of the target. 212 deps: proto_library targets from which to generate Pigweed C++. 213 visibility: The visibility of the target. See 214 https://bazel.build/concepts/visibility. 215 tags: Tags for the target. See 216 https://bazel.build/reference/be/common-definitions#common-attributes. 217 nanopb_options: path to file containing nanopb options, if any 218 (https://jpa.kapsi.fi/nanopb/docs/reference.html#proto-file-options). 219 enabled_targets: Specifies which libraries should be generated. Libraries 220 will only be generated as needed, but unnecessary outputs may conflict 221 with other build rules and thus cause build failures. This filter allows 222 manual selection of which libraries should be supported by this build 223 target in order to prevent such conflicts. The argument, if provided, 224 should be a subset of ["pwpb", "nanopb", "raw_rpc", "nanopb_rpc"]. All 225 are enabled by default. Note that "nanopb_rpc" relies on "nanopb". 226 227 Example usage: 228 229 proto_library( 230 name = "benchmark_proto", 231 srcs = [ 232 "benchmark.proto", 233 ], 234 ) 235 236 pw_proto_library( 237 name = "benchmark_pw_proto", 238 deps = [":benchmark_proto"], 239 ) 240 241 pw_cc_binary( 242 name = "proto_user", 243 srcs = ["proto_user.cc"], 244 deps = [":benchmark_pw_proto.pwpb"], 245 ) 246 247 The pw_proto_library generates the following targets in this example: 248 249 "benchmark_pw_proto.pwpb": C++ library exposing the "benchmark.pwpb.h" header. 250 "benchmark_pw_proto.pwpb_rpc": C++ library exposing the 251 "benchmark.rpc.pwpb.h" header. 252 "benchmark_pw_proto.raw_rpc": C++ library exposing the "benchmark.raw_rpc.h" 253 header. 254 "benchmark_pw_proto.nanopb": C++ library exposing the "benchmark.pb.h" 255 header. 256 "benchmark_pw_proto.nanopb_rpc": C++ library exposing the 257 "benchmark.rpc.pb.h" header. 258 """ 259 260 def is_plugin_enabled(plugin): 261 return (enabled_targets == None or plugin in enabled_targets) 262 263 if is_plugin_enabled("nanopb"): 264 # Use nanopb to generate the pb.h and pb.c files, and the target 265 # exposing them. 266 nanopb_proto_library( 267 name = name + ".nanopb", 268 deps = deps, 269 tags = tags, 270 visibility = visibility, 271 options = nanopb_options, 272 ) 273 274 if is_plugin_enabled("pwpb"): 275 pwpb_proto_library( 276 name = name + ".pwpb", 277 deps = deps, 278 tags = tags, 279 visibility = visibility, 280 ) 281 282 if is_plugin_enabled("pwpb_rpc"): 283 pwpb_rpc_proto_library( 284 name = name + ".pwpb_rpc", 285 deps = deps, 286 pwpb_proto_library_deps = [":" + name + ".pwpb"], 287 tags = tags, 288 visibility = visibility, 289 ) 290 291 if is_plugin_enabled("raw_rpc"): 292 raw_rpc_proto_library( 293 name = name + ".raw_rpc", 294 deps = deps, 295 tags = tags, 296 visibility = visibility, 297 ) 298 299 if is_plugin_enabled("nanopb_rpc"): 300 nanopb_rpc_proto_library( 301 name = name + ".nanopb_rpc", 302 deps = deps, 303 nanopb_proto_library_deps = [":" + name + ".nanopb"], 304 tags = tags, 305 visibility = visibility, 306 ) 307 308PwProtoInfo = provider( 309 "Returned by PW proto compilation aspect", 310 fields = { 311 "hdrs": "generated C++ header files", 312 "includes": "include paths for generated C++ header files", 313 "srcs": "generated C++ src files", 314 }, 315) 316 317PwProtoOptionsInfo = provider( 318 "Allows `pw_proto_filegroup` targets to pass along `.options` files " + 319 "without polluting the `DefaultInfo` provider, which means they can " + 320 "still be used in the `srcs` of `proto_library` targets.", 321 fields = { 322 "options_files": (".options file(s) associated with a proto_library " + 323 "for Pigweed codegen."), 324 }, 325) 326 327def _proto_compiler_aspect_impl(target, ctx): 328 # List the files we will generate for this proto_library target. 329 proto_info = target[ProtoInfo] 330 331 srcs = [] 332 hdrs = [] 333 334 # Setup the output root for the plugin to point to targets output 335 # directory. This allows us to declare the location of the files that protoc 336 # will output in a way that `ctx.actions.declare_file` will understand, 337 # since it works relative to the target. 338 out_path = ctx.bin_dir.path 339 if target.label.workspace_root: 340 out_path += "/" + target.label.workspace_root 341 if target.label.package: 342 out_path += "/" + target.label.package 343 344 # Add location of headers to cc include path. 345 # Depending on prefix rules, the include path can be directly from the 346 # output path, or underneath the package. 347 includes = [out_path] 348 349 for src in proto_info.direct_sources: 350 # Get the relative import path for this .proto file. 351 src_rel = paths.relativize(src.path, proto_info.proto_source_root) 352 proto_dir = paths.dirname(src_rel) 353 354 # Add location of headers to cc include path. 355 includes.append("{}/{}".format(out_path, src.owner.package)) 356 357 for ext in ctx.attr._extensions: 358 # Declare all output files, in target package dir. 359 generated_filename = src.basename[:-len("proto")] + ext 360 if proto_dir: 361 out_file_name = "{}/{}".format( 362 proto_dir, 363 generated_filename, 364 ) 365 else: 366 out_file_name = generated_filename 367 368 out_file = ctx.actions.declare_file(out_file_name) 369 370 if ext.endswith(".h"): 371 hdrs.append(out_file) 372 else: 373 srcs.append(out_file) 374 375 # List the `.options` files from any `pw_proto_filegroup` targets listed 376 # under this target's `srcs`. 377 options_files = [ 378 options_file 379 for src in ctx.rule.attr.srcs 380 if PwProtoOptionsInfo in src 381 for options_file in src[PwProtoOptionsInfo].options_files.to_list() 382 ] 383 384 # Local repository options files. 385 options_file_include_paths = [paths.join(".", ctx.rule.attr.strip_import_prefix.lstrip("/"))] 386 for options_file in options_files: 387 # Handle .options files residing in external repositories. 388 if options_file.owner.workspace_root: 389 options_file_include_paths.append( 390 paths.join( 391 options_file.owner.workspace_root, 392 ctx.rule.attr.strip_import_prefix.lstrip("/"), 393 ), 394 ) 395 396 # Handle generated .options files. 397 if options_file.root.path: 398 options_file_include_paths.append( 399 paths.join( 400 options_file.root.path, 401 ctx.rule.attr.strip_import_prefix.lstrip("/"), 402 ), 403 ) 404 405 args = ctx.actions.args() 406 for path in proto_info.transitive_proto_path.to_list(): 407 args.add("-I{}".format(path)) 408 409 args.add("--plugin=protoc-gen-custom={}".format(ctx.executable._protoc_plugin.path)) 410 411 # Convert include paths to a depset and back to deduplicate entries. 412 for options_file_include_path in depset(options_file_include_paths).to_list(): 413 args.add("--custom_opt=-I{}".format(options_file_include_path)) 414 415 for plugin_option in ctx.attr._plugin_options: 416 # If the plugin supports directly specifying the location of the options files, pass them here. 417 if plugin_option == "--options-file={}": 418 for options_file in options_files: 419 plugin_options_arg = plugin_option.format(options_file.path) 420 args.add("--custom_opt={}".format(plugin_options_arg)) 421 continue 422 args.add("--custom_opt={}".format(plugin_option)) 423 424 # In certain environments the path to pb.h must be defined, rather 425 # than relying on the default location. 426 # Use a format string rather than `quote`, to maintain support 427 # for nanopb 3. 428 if hasattr(ctx.attr, "_pb_h"): 429 plugin_options_arg = "--library-include-format=#include \"{}/%s\"".format(ctx.file._pb_h.dirname) 430 args.add("--custom_opt={}".format(plugin_options_arg)) 431 432 args.add("--custom_out={}".format(out_path)) 433 args.add_all(proto_info.direct_sources) 434 435 all_tools = [ 436 ctx.executable._protoc, 437 ctx.executable._protoc_plugin, 438 ] 439 440 ctx.actions.run( 441 inputs = depset( 442 direct = proto_info.direct_sources + 443 proto_info.transitive_sources.to_list() + 444 options_files, 445 transitive = [proto_info.transitive_descriptor_sets], 446 ), 447 progress_message = "Generating %s C++ files for %s" % (ctx.attr._extensions, ctx.label.name), 448 tools = all_tools, 449 outputs = srcs + hdrs, 450 executable = ctx.executable._protoc, 451 arguments = [args], 452 env = { 453 # The nanopb protobuf plugin likes to compile some temporary protos 454 # next to source files. This forces them to be written to Bazel's 455 # genfiles directory. 456 "NANOPB_PB2_TEMP_DIR": str(ctx.genfiles_dir), 457 }, 458 ) 459 460 transitive_srcs = srcs 461 transitive_hdrs = hdrs 462 transitive_includes = includes 463 for dep in ctx.rule.attr.deps: 464 transitive_srcs += dep[PwProtoInfo].srcs 465 transitive_hdrs += dep[PwProtoInfo].hdrs 466 transitive_includes += dep[PwProtoInfo].includes 467 return [PwProtoInfo( 468 srcs = transitive_srcs, 469 hdrs = transitive_hdrs, 470 includes = transitive_includes, 471 )] 472 473def _proto_compiler_aspect(extensions, protoc_plugin, plugin_options = [], additional_attrs = {}): 474 """Returns an aspect that runs the proto compiler. 475 476 The aspect propagates through the deps of proto_library targets, running 477 the proto compiler with the specified plugin for each of their source 478 files. The proto compiler is assumed to produce one output file per input 479 .proto file. That file is placed under bazel-bin at the same path as the 480 input file, but with the specified extension (i.e., with _extensions = [ 481 .pwpb.h], the aspect converts pw_log/log.proto into 482 bazel-bin/pw_log/log.pwpb.h). 483 484 The aspect returns a provider exposing all the File objects generated from 485 the dependency graph. 486 """ 487 return aspect( 488 attr_aspects = ["deps"], 489 attrs = { 490 "_extensions": attr.string_list(default = extensions), 491 "_plugin_options": attr.string_list( 492 default = plugin_options, 493 ), 494 "_protoc": attr.label( 495 default = Label("@com_google_protobuf//:protoc"), 496 executable = True, 497 cfg = "exec", 498 ), 499 "_protoc_plugin": attr.label( 500 default = Label(protoc_plugin), 501 executable = True, 502 cfg = "exec", 503 ), 504 } | additional_attrs, 505 implementation = _proto_compiler_aspect_impl, 506 provides = [PwProtoInfo], 507 toolchains = ["@rules_python//python:exec_tools_toolchain_type"], 508 ) 509 510def _impl_pw_proto_library(ctx): 511 """Implementation of the proto codegen rule. 512 513 The work of actually generating the code is done by the aspect, so here we 514 compile and return a CcInfo to link against. 515 """ 516 517 # Note that we don't distinguish between the files generated from the 518 # target, and the files generated from its dependencies. We return all of 519 # them together, and in pw_proto_library expose all of them as hdrs. 520 # Pigweed's plugins happen to only generate .h files, so this works, but 521 # strictly speaking we should expose only the files generated from the 522 # target itself in hdrs, and place the headers generated from dependencies 523 # in srcs. We don't perform layering_check in Pigweed, so this is not a big 524 # deal. 525 # 526 # TODO: b/234873954 - Tidy this up. 527 all_srcs = [] 528 all_hdrs = [] 529 all_includes = [] 530 for dep in ctx.attr.protos: 531 for f in dep[PwProtoInfo].hdrs: 532 all_hdrs.append(f) 533 for f in dep[PwProtoInfo].srcs: 534 all_srcs.append(f) 535 for i in dep[PwProtoInfo].includes: 536 all_includes.append(i) 537 538 return _compile_cc( 539 ctx, 540 all_srcs, 541 all_hdrs, 542 ctx.attr.deps, 543 all_includes, 544 defines = [], 545 ) 546 547# Instantiate the aspects and rules for generating code using specific plugins. 548_pwpb_proto_compiler_aspect = _proto_compiler_aspect( 549 ["pwpb.h"], 550 "//pw_protobuf/py:plugin", 551 ["--no-legacy-namespace", "--options-file={}"], 552) 553 554# TODO: b/373693434 - This aspect and its corresponding rule should be removed 555# once oneof callback migration is complete. 556_pwpb_legacy_oneof_compiler_aspect = _proto_compiler_aspect( 557 ["pwpb.h"], 558 "//pw_protobuf/py:plugin", 559 ["--no-oneof-callbacks", "--no-legacy-namespace", "--options-file={}"], 560) 561 562_pwpb_legacy_oneof_proto_library = rule( 563 implementation = _impl_pw_proto_library, 564 attrs = { 565 "deps": attr.label_list( 566 providers = [CcInfo], 567 ), 568 "protos": attr.label_list( 569 providers = [ProtoInfo], 570 aspects = [_pwpb_legacy_oneof_compiler_aspect], 571 ), 572 }, 573 fragments = ["cpp"], 574 toolchains = use_cpp_toolchain(), 575) 576 577_pwpb_proto_library = rule( 578 implementation = _impl_pw_proto_library, 579 attrs = { 580 "deps": attr.label_list( 581 providers = [CcInfo], 582 ), 583 "protos": attr.label_list( 584 providers = [ProtoInfo], 585 aspects = [_pwpb_proto_compiler_aspect], 586 ), 587 }, 588 fragments = ["cpp"], 589 toolchains = use_cpp_toolchain(), 590) 591 592_nanopb_proto_compiler_aspect = _proto_compiler_aspect( 593 ["pb.h", "pb.c"], 594 "@com_github_nanopb_nanopb//:protoc-gen-nanopb", 595 [], 596 { 597 "_pb_h": attr.label( 598 default = Label("@com_github_nanopb_nanopb//:pb.h"), 599 allow_single_file = True, 600 ), 601 }, 602) 603 604_nanopb_proto_library = rule( 605 implementation = _impl_pw_proto_library, 606 attrs = { 607 "deps": attr.label_list( 608 providers = [CcInfo], 609 ), 610 "protos": attr.label_list( 611 providers = [ProtoInfo], 612 aspects = [_nanopb_proto_compiler_aspect], 613 ), 614 }, 615 fragments = ["cpp"], 616 toolchains = use_cpp_toolchain(), 617) 618 619_pw_pwpb_rpc_proto_compiler_aspect = _proto_compiler_aspect( 620 ["rpc.pwpb.h"], 621 "//pw_rpc/py:plugin_pwpb", 622 ["--no-legacy-namespace"], 623) 624 625_pw_pwpb_rpc_proto_library = rule( 626 implementation = _impl_pw_proto_library, 627 attrs = { 628 "deps": attr.label_list( 629 providers = [CcInfo], 630 ), 631 "protos": attr.label_list( 632 providers = [ProtoInfo], 633 aspects = [_pw_pwpb_rpc_proto_compiler_aspect], 634 ), 635 }, 636 fragments = ["cpp"], 637 toolchains = use_cpp_toolchain(), 638) 639 640_pw_raw_rpc_proto_compiler_aspect = _proto_compiler_aspect( 641 ["raw_rpc.pb.h"], 642 "//pw_rpc/py:plugin_raw", 643 ["--no-legacy-namespace"], 644) 645 646_pw_raw_rpc_proto_library = rule( 647 implementation = _impl_pw_proto_library, 648 attrs = { 649 "deps": attr.label_list( 650 providers = [CcInfo], 651 ), 652 "protos": attr.label_list( 653 providers = [ProtoInfo], 654 aspects = [_pw_raw_rpc_proto_compiler_aspect], 655 ), 656 }, 657 fragments = ["cpp"], 658 toolchains = use_cpp_toolchain(), 659) 660 661_pw_nanopb_rpc_proto_compiler_aspect = _proto_compiler_aspect( 662 ["rpc.pb.h"], 663 "//pw_rpc/py:plugin_nanopb", 664 ["--no-legacy-namespace"], 665) 666 667_pw_nanopb_rpc_proto_library = rule( 668 implementation = _impl_pw_proto_library, 669 attrs = { 670 "deps": attr.label_list( 671 providers = [CcInfo], 672 ), 673 "protos": attr.label_list( 674 providers = [ProtoInfo], 675 aspects = [_pw_nanopb_rpc_proto_compiler_aspect], 676 ), 677 }, 678 fragments = ["cpp"], 679 toolchains = use_cpp_toolchain(), 680) 681 682def _pw_proto_filegroup_impl(ctx): 683 source_files = list() 684 options_files = list() 685 686 for src in ctx.attr.srcs: 687 source_files += src.files.to_list() 688 689 for options_src in ctx.attr.options_files: 690 for file in options_src.files.to_list(): 691 if file.extension == "options" or file.extension == "pwpb_options": 692 options_files.append(file) 693 else: 694 fail(( 695 "Files provided as `options_files` to a " + 696 "`pw_proto_filegroup` must have the `.options` or " + 697 "`.pwpb_options` extensions; the file `{}` was provided." 698 ).format(file.basename)) 699 700 return [ 701 DefaultInfo(files = depset(source_files)), 702 PwProtoOptionsInfo(options_files = depset(options_files)), 703 ] 704 705pw_proto_filegroup = rule( 706 doc = ( 707 "Acts like a `filegroup`, but with an additional `options_files` " + 708 "attribute that accepts a list of `.options` files. These `.options` " + 709 "files should typically correspond to `.proto` files provided under " + 710 "the `srcs` attribute." + 711 "\n\n" + 712 "A `pw_proto_filegroup` is intended to be passed into the `srcs` of " + 713 "a `proto_library` target as if it were a normal `filegroup` " + 714 "containing only `.proto` files. For the purposes of the " + 715 "`proto_library` itself, the `pw_proto_filegroup` does indeed act " + 716 "just like a normal `filegroup`; the `options_files` attribute is " + 717 "ignored. However, if that `proto_library` target is then passed " + 718 "(directly or transitively) into the `deps` of a `pw_proto_library` " + 719 "for code generation, the `pw_proto_library` target will have access " + 720 "to the provided `.options` files and will pass them to the code " + 721 "generator." + 722 "\n\n" + 723 "Note that, in order for a `pw_proto_filegroup` to be a valid `srcs` " + 724 "entry for a `proto_library`, it must meet the same conditions " + 725 "required of a standard `filegroup` in that context. Namely, its " + 726 "`srcs` must provide at least one `.proto` (or `.protodevel`) file. " + 727 "Put simply, a `pw_proto_filegroup` cannot be used as a vector for " + 728 "injecting solely `.options` files; it must contain at least one " + 729 "proto as well (generally one associated with an included `.options` " + 730 "file in the interest of clarity)." + 731 "\n\n" + 732 "Regarding the somewhat unusual usage, this feature's design was " + 733 "mostly preordained by the combination of Bazel's strict access " + 734 "controls, the restrictions imposed on inputs to the `proto_library` " + 735 "rule, and the need to support `.options` files from transitive " + 736 "dependencies." 737 ), 738 implementation = _pw_proto_filegroup_impl, 739 attrs = { 740 "options_files": attr.label_list( 741 allow_files = True, 742 ), 743 "srcs": attr.label_list( 744 allow_files = True, 745 ), 746 }, 747 provides = [PwProtoOptionsInfo], 748) 749