pw_protobuf_compiler: Allow subset of sub-targets

Allow a `pw_proto_library` target to explicitly specify which
sub-targets should be enabled. This can prevent build failures in cases
where an unused sub-target conflicts with another target in the same
package.

Bugs: b/235132083, b/230487701
Change-Id: Ia9205ef1a3fa94c1033665be499cce84949a6f0e
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/96980
Commit-Queue: Matthias Guenther <mrguenther@google.com>
Reviewed-by: Ted Pudlik <tpudlik@google.com>
diff --git a/pw_protobuf_compiler/docs.rst b/pw_protobuf_compiler/docs.rst
index 48053d6..e1b6a7a 100644
--- a/pw_protobuf_compiler/docs.rst
+++ b/pw_protobuf_compiler/docs.rst
@@ -106,6 +106,14 @@
   input files must be nested under this path.
 * ``python_package``: Label of Python package to which to add the proto modules.
   The .python subtarget will redirect to this package.
+* ``enabled_targets``: List of sub-targets to enable (see Supported Codegen),
+  e.g. ``["pwpb", "raw_rpc"]``. By default, all sub-targets are enabled. The
+  enabled sub-targets are built only as requested by the build system, but it
+  may be necessary to explicitly disable an unused sub-target if it conflicts
+  with another target in the same package. (For example, ``nanopb`` codegen can
+  conflict with the default C++ codegen provided by ``protoc``.)
+  TODO(b/235132083): Remove this argument once we've removed the file-name
+  conflict between nanopb and protoc code generators.
 
 **Example**
 
diff --git a/pw_protobuf_compiler/pw_proto_library.bzl b/pw_protobuf_compiler/pw_proto_library.bzl
index 162df20..4a5de15 100644
--- a/pw_protobuf_compiler/pw_proto_library.bzl
+++ b/pw_protobuf_compiler/pw_proto_library.bzl
@@ -53,7 +53,11 @@
 load("@rules_proto//proto:defs.bzl", "ProtoInfo")
 load("//pw_protobuf_compiler:pw_nanopb_cc_library", "pw_nanopb_cc_library")
 
-def pw_proto_library(name = "", deps = [], nanopb_options = None):
+def pw_proto_library(
+        name = "",
+        deps = [],
+        nanopb_options = None,
+        enabled_targets = None):
     """Generate Pigweed proto C++ code.
 
     This is the only public symbol in this file: everything else is
@@ -64,6 +68,13 @@
       deps: proto_library targets from which to generate Pigweed C++.
       nanopb_options: path to file containing nanopb options, if any
         (https://jpa.kapsi.fi/nanopb/docs/reference.html#proto-file-options).
+      enabled_targets: Specifies which libraries should be generated. Libraries
+        will only be generated as needed, but unnecessary outputs may conflict
+        with other build rules and thus cause build failures. This filter allows
+        manual selection of which libraries should be supported by this build
+        target in order to prevent such conflicts. The argument, if provided,
+        should be a subset of ["pwpb", "nanopb", "raw_rpc", "nanopb_rpc"]. All
+        are enabled by default. Note that "nanopb_rpc" relies on "nanopb".
 
     Example usage:
 
@@ -98,14 +109,23 @@
         "benchmark.rpc.pb.h" header.
     """
 
-    # Use nanopb to generate the pb.h and pb.c files, and the target exposing
-    # them.
-    pw_nanopb_cc_library(name + ".nanopb", deps, options = nanopb_options)
+    def is_plugin_enabled(plugin):
+        return (enabled_targets == None or plugin in enabled_targets)
+
+    if is_plugin_enabled("nanobp"):
+        # Use nanopb to generate the pb.h and pb.c files, and the target
+        # exposing them.
+        pw_nanopb_cc_library(name + ".nanopb", deps, options = nanopb_options)
 
     # Use Pigweed proto plugins to generate the remaining files and targets.
     for plugin_name, info in PIGWEED_PLUGIN.items():
+        if not is_plugin_enabled(plugin_name):
+            continue
+
         name_pb = name + "_pb." + plugin_name
-        info["compiler"](
+
+        plugin_rule = info["compiler"]
+        plugin_rule(
             name = name_pb,
             deps = deps,
         )
@@ -141,6 +161,7 @@
 def _proto_compiler_aspect_impl(target, ctx):
     # List the files we will generate for this proto_library target.
     genfiles = []
+
     for src in target[ProtoInfo].direct_sources:
         path = src.basename[:-len("proto")] + ctx.attr._extension
         genfiles.append(ctx.actions.declare_file(path, sibling = src))
@@ -154,6 +175,7 @@
         join_with = ctx.host_configuration.host_path_separator,
         map_each = _get_path,
     )
+
     args.add_all(target[ProtoInfo].direct_sources, map_each = _get_short_path)
 
     ctx.actions.run(
@@ -200,6 +222,7 @@
             ),
         },
         implementation = _proto_compiler_aspect_impl,
+        provides = [PwProtoInfo],
     )
 
 def _impl_pw_proto_library(ctx):
@@ -279,8 +302,13 @@
     "pwpb": {
         "compiler": _pw_proto_library,
         "deps": [
+            "//pw_assert:facade",
+            "//pw_containers:vector",
+            "//pw_preprocessor",
+            "//pw_protobuf",
+            "//pw_result",
             "//pw_span",
-            "//pw_protobuf:pw_protobuf",
+            "//pw_status",
         ],
         "include_nanopb_dep": False,
         "include_pwpb_dep": False,