blob: cf997aa7a092a4015d02e24d57d34e01ea678653 [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")
def pwpb_proto_library(name, deps, **kwargs):
"""A C++ proto library generated using pw_protobuf.
Attributes:
deps: proto_library targets for which to generate this library.
"""
_pw_proto_library(
name = name,
compiler = pwpb_compile,
deps = deps,
cc_deps = ["@pigweed//pw_protobuf"],
has_srcs = False,
extra_tags = [],
**kwargs
)
def pwpb_rpc_proto_library(name, deps, pwpb_proto_library_deps, **kwargs):
"""A pwpb_rpc proto library target.
Attributes:
deps: proto_library targets for which to generate this library.
pwpb_proto_library_deps: A pwpb_proto_library generated
from the same proto_library. Required.
"""
_pw_proto_library(
name = name,
compiler = pwpb_rpc_compile,
deps = deps,
cc_deps = [
"@pigweed//pw_protobuf",
"@pigweed//pw_rpc/pwpb:server_api",
"@pigweed//pw_rpc/pwpb:client_api",
"@pigweed//pw_rpc",
] + pwpb_proto_library_deps,
has_srcs = False,
extra_tags = [],
**kwargs
)
def raw_rpc_proto_library(name, deps, **kwargs):
"""A raw C++ RPC proto library."""
_pw_proto_library(
name = name,
compiler = raw_rpc_compile,
deps = deps,
cc_deps = [
"@pigweed//pw_rpc",
"@pigweed//pw_rpc/raw:server_api",
"@pigweed//pw_rpc/raw:client_api",
],
has_srcs = False,
extra_tags = [],
**kwargs
)
def nanopb_proto_library(name, deps, **kwargs):
"""A C++ proto library generated using nanopb."""
_pw_proto_library(
name = name,
compiler = nanopb_compile,
deps = deps,
cc_deps = [
"@com_github_nanopb_nanopb//:nanopb",
],
has_srcs = True,
# TODO(tpudlik): 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.
extra_tags = ["manual"],
**kwargs
)
def nanopb_rpc_proto_library(name, deps, nanopb_proto_library_deps, **kwargs):
"""A C++ RPC proto library using nanopb.
Attributes:
deps: proto_library targets for which to generate this library.
nanopb_proto_library_deps: A pw_nanopb_cc_library generated
from the same proto_library. Required.
"""
_pw_proto_library(
name = name,
compiler = nanopb_rpc_compile,
deps = deps,
cc_deps = [
"@com_github_nanopb_nanopb//:nanopb",
"@pigweed//pw_rpc/nanopb:server_api",
"@pigweed//pw_rpc/nanopb:client_api",
"@pigweed//pw_rpc",
] + nanopb_proto_library_deps,
has_srcs = True,
# TODO(tpudlik): See nanopb_proto_library.
extra_tags = ["manual"],
**kwargs
)
# 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,
)
def _pw_proto_library(name, compiler, deps, cc_deps, has_srcs, extra_tags, **kwargs):
name_pb = name + ".pb"
additional_tags = [
tag
for tag in extra_tags
if tag not in kwargs.get("tags", [])
]
compiler(
name = name_pb,
tags = additional_tags,
# Forward deps and verbose tags to implementation
verbose = kwargs.get("verbose", 0),
deps = 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,
hdrs = [name_pb + "_hdrs"],
includes = [name_pb],
alwayslink = kwargs.get("alwayslink"),
copts = kwargs.get("copts", []),
defines = kwargs.get("defines", []),
srcs = [name_pb + "_srcs"] if has_srcs else [],
deps = cc_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,
)
def pw_proto_library(name, **kwargs): # buildifier: disable=function-docstring
"""Generate all the Pigweed proto library targets.
Args:
name: Name of this proto library.
**kwargs: Forwarded to wrapped native.cc_library.
Deprecated: This macro is deprecated and will be removed in a future
Pigweed version. Please use one of the single-target macros above.
"""
pwpb_proto_library(
name = name + ".pwpb",
**kwargs
)
pwpb_rpc_proto_library(
name = name + ".pwpb_rpc",
pwpb_proto_library_deps = [name + ".pwpb"],
**kwargs
)
raw_rpc_proto_library(
name = name + ".raw_rpc",
**kwargs
)
nanopb_proto_library(
name = name + ".nanopb",
**kwargs
)
nanopb_rpc_proto_library(
name = name + ".nanopb_rpc",
nanopb_proto_library_deps = [name + ".nanopb"],
**kwargs
)
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 ["pwpb", "pwpb_rpc", "raw_rpc", "nanopb", "nanopb_rpc"]
],
tags = kwargs.get("tags", []) + additional_tags,
**{
k: v
for k, v in kwargs.items()
if k not in ["deps", "protos", "tags"]
}
)