blob: ad7c90698d5c6a91d59819e3b8a2d888f8801e2e [file] [log] [blame]
# 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"]}
)