| # Copyright 2022 The Pigweed Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy of |
| # the License at |
| # |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| |
| """Backend implementation for 'pw_protobuf_compiler/proto.bzl'""" |
| |
| # Apache License, Version 2.0, January 2004, http://www.apache.org/licenses/ |
| # Adapted from: https://github.com/rules-proto-grpc/rules_proto_grpc/ |
| # Files adapted: |
| # - rules_proto_grpc/cpp/cpp_grpc_library.bzl |
| # - rules_proto_grpc/cpp/cpp_grpc_compile.bzl |
| # These two files have been adapted for use in Pigweed and combined into this |
| # file. |
| |
| load("@rules_proto//proto:defs.bzl", "ProtoInfo") |
| load( |
| "@rules_proto_grpc//:defs.bzl", |
| "ProtoLibraryAspectNodeInfo", |
| "ProtoPluginInfo", |
| "proto_compile_aspect_attrs", |
| "proto_compile_aspect_impl", |
| "proto_compile_attrs", |
| "proto_compile_impl", |
| ) |
| load("@rules_proto_grpc//internal:filter_files.bzl", "filter_files") |
| |
| # Create compile rule |
| def _proto_compiler_aspect(plugin_group, prefix): |
| return aspect( |
| implementation = proto_compile_aspect_impl, |
| provides = [ProtoLibraryAspectNodeInfo], |
| attr_aspects = ["deps"], |
| attrs = dict( |
| proto_compile_aspect_attrs, |
| _plugins = attr.label_list( |
| doc = "List of protoc plugins to apply", |
| providers = [ProtoPluginInfo], |
| default = plugin_group, |
| ), |
| _prefix = attr.string( |
| doc = "String used to disambiguate aspects when generating \ |
| outputs", |
| default = prefix, |
| ), |
| ), |
| toolchains = [str(Label("@rules_proto_grpc//protobuf:toolchain_type"))], |
| ) |
| |
| def _proto_compiler_rule(plugin_group, aspect): |
| return rule( |
| implementation = proto_compile_impl, |
| attrs = dict( |
| proto_compile_attrs, |
| _plugins = attr.label_list( |
| doc = "List of protoc plugins to apply", |
| providers = [ProtoPluginInfo], |
| default = plugin_group, |
| ), |
| protos = attr.label_list( |
| providers = [ProtoInfo], |
| doc = "List of proto_library targets.", |
| ), |
| deps = attr.label_list( |
| doc = "List of proto_library targets. Prefer protos.", |
| aspects = [aspect], |
| ), |
| ), |
| toolchains = [str(Label("@rules_proto_grpc//protobuf:toolchain_type"))], |
| ) |
| |
| nanopb_compile_aspect = _proto_compiler_aspect( |
| [Label("//pw_rpc:nanopb_plugin")], |
| "nanopb_proto_compile_aspect", |
| ) |
| nanopb_compile = _proto_compiler_rule( |
| [Label("//pw_rpc:nanopb_plugin")], |
| nanopb_compile_aspect, |
| ) |
| |
| pwpb_compile_aspect = _proto_compiler_aspect( |
| [Label("@pigweed//pw_protobuf:pw_cc_plugin")], |
| "pwpb_proto_compile_aspect", |
| ) |
| pwpb_compile = _proto_compiler_rule( |
| [Label("@pigweed//pw_protobuf:pw_cc_plugin")], |
| pwpb_compile_aspect, |
| ) |
| |
| pwpb_rpc_compile_aspect = _proto_compiler_aspect( |
| [ |
| Label("@pigweed//pw_rpc:pw_cc_plugin_pwpb_rpc"), |
| Label("@pigweed//pw_protobuf:pw_cc_plugin"), |
| ], |
| "pwpb_rpc_proto_compile_aspect", |
| ) |
| pwpb_rpc_compile = _proto_compiler_rule( |
| [ |
| Label("@pigweed//pw_rpc:pw_cc_plugin_pwpb_rpc"), |
| Label("@pigweed//pw_protobuf:pw_cc_plugin"), |
| ], |
| pwpb_rpc_compile_aspect, |
| ) |
| |
| raw_rpc_compile_aspect = _proto_compiler_aspect( |
| [Label("@pigweed//pw_rpc:pw_cc_plugin_raw")], |
| "raw_rpc_proto_compile_aspect", |
| ) |
| raw_rpc_compile = _proto_compiler_rule( |
| [Label("@pigweed//pw_rpc:pw_cc_plugin_raw")], |
| raw_rpc_compile_aspect, |
| ) |
| |
| nanopb_rpc_compile_aspect = _proto_compiler_aspect( |
| [ |
| Label("@pigweed//pw_rpc:pw_cc_plugin_nanopb_rpc"), |
| Label("//pw_rpc:nanopb_plugin"), |
| ], |
| "nanopb_rpc_proto_compile_aspect", |
| ) |
| nanopb_rpc_compile = _proto_compiler_rule( |
| [ |
| Label("@pigweed//pw_rpc:pw_cc_plugin_nanopb_rpc"), |
| Label("//pw_rpc:nanopb_plugin"), |
| ], |
| nanopb_rpc_compile_aspect, |
| ) |
| |
| PLUGIN_INFO = { |
| "nanopb": { |
| "compiler": nanopb_compile, |
| "deps": ["@com_github_nanopb_nanopb//:nanopb"], |
| "has_srcs": True, |
| # TODO: Find a way to get Nanopb to generate nested structs. |
| # Otherwise add the manual tag to the resulting library, |
| # preventing it from being built unless directly depended on. |
| # e.g. The 'Pigweed' message in |
| # pw_protobuf/pw_protobuf_test_protos/full_test.proto will fail to |
| # compile as it has a self referring nested message. According to |
| # the docs |
| # https://jpa.kapsi.fi/nanopb/docs/reference.html#proto-file-options |
| # and https://github.com/nanopb/nanopb/issues/433 it seams like it |
| # should be possible to configure nanopb to generate nested structs. |
| "additional_tags": ["manual"], |
| }, |
| "nanopb_rpc": { |
| "compiler": nanopb_rpc_compile, |
| "deps": [ |
| "@com_github_nanopb_nanopb//:nanopb", |
| "@pigweed//pw_rpc/nanopb:server_api", |
| "@pigweed//pw_rpc/nanopb:client_api", |
| "@pigweed//pw_rpc", |
| ], |
| "has_srcs": True, |
| # See above todo. |
| "additional_tags": ["manual"], |
| }, |
| "pwpb": { |
| "compiler": pwpb_compile, |
| "deps": ["@pigweed//pw_protobuf"], |
| "has_srcs": False, |
| "additional_tags": [], |
| }, |
| "pwpb_rpc": { |
| "compiler": pwpb_rpc_compile, |
| "deps": [ |
| "@pigweed//pw_protobuf", |
| "@pigweed//pw_rpc/pwpb:server_api", |
| "@pigweed//pw_rpc/pwpb:client_api", |
| "@pigweed//pw_rpc", |
| ], |
| "has_srcs": False, |
| "additional_tags": [], |
| }, |
| "raw_rpc": { |
| "compiler": raw_rpc_compile, |
| "deps": [ |
| "@pigweed//pw_rpc", |
| "@pigweed//pw_rpc/raw:server_api", |
| "@pigweed//pw_rpc/raw:client_api", |
| ], |
| "has_srcs": False, |
| "additional_tags": [], |
| }, |
| } |
| |
| def pw_proto_library(name, **kwargs): # buildifier: disable=function-docstring |
| for plugin_name, info in PLUGIN_INFO.items(): |
| name_pb = name + "_pb" + "." + plugin_name |
| additional_tags = [ |
| tag |
| for tag in info["additional_tags"] |
| if tag not in kwargs.get("tags", []) |
| ] |
| info["compiler"]( |
| name = name_pb, |
| tags = additional_tags, |
| # Forward deps and verbose tags to implementation |
| verbose = kwargs.get("verbose", 0), |
| deps = kwargs.get("deps", []), |
| protos = kwargs.get("protos", []), |
| ) |
| |
| # Filter files to sources and headers |
| filter_files( |
| name = name_pb + "_srcs", |
| target = name_pb, |
| extensions = ["c", "cc", "cpp", "cxx"], |
| tags = additional_tags, |
| ) |
| |
| filter_files( |
| name = name_pb + "_hdrs", |
| target = name_pb, |
| extensions = ["h"], |
| tags = additional_tags, |
| ) |
| |
| # Cannot use pw_cc_library here as it will add cxxopts. |
| # Note that the srcs attribute here is passed in as a DefaultInfo |
| # object, which is not supported by pw_cc_library. |
| native.cc_library( |
| name = name + "." + plugin_name, |
| hdrs = [name_pb + "_hdrs"], |
| includes = [name_pb], |
| alwayslink = kwargs.get("alwayslink"), |
| copts = kwargs.get("copts", []), |
| defines = kwargs.get("defines", []), |
| srcs = [name_pb + "_srcs"] if info["has_srcs"] else [], |
| deps = info["deps"], |
| linkopts = kwargs.get("linkopts", []), |
| linkstatic = kwargs.get("linkstatic", True), |
| local_defines = kwargs.get("local_defines", []), |
| nocopts = kwargs.get("nocopts", ""), |
| visibility = kwargs.get("visibility"), |
| tags = kwargs.get("tags", []) + additional_tags, |
| ) |
| |
| if "manual" in kwargs.get("tags", []): |
| additional_tags = [] |
| else: |
| additional_tags = ["manual"] |
| |
| # Combine all plugins into a single library. |
| native.cc_library( |
| name = name, |
| deps = [ |
| name + "." + plugin_name |
| for plugin_name in PLUGIN_INFO.keys() |
| ], |
| tags = kwargs.get("tags", []) + additional_tags, |
| **{k: v for k, v in kwargs.items() if k not in ["deps", "protos"]} |
| ) |