blob: 29a888f608fc22e75c37582abc889f41f4b24c5a [file] [log] [blame]
"""Generates C++ grpc stubs from proto_library rules.
This is an internal rule used by cc_grpc_library, and shouldn't be used
directly.
"""
load(
"//bazel:protobuf.bzl",
"get_include_protoc_args",
"get_plugin_args",
"get_proto_root",
"proto_path_to_generated_filename",
)
_GRPC_PROTO_HEADER_FMT = "{}.grpc.pb.h"
_GRPC_PROTO_SRC_FMT = "{}.grpc.pb.cc"
_GRPC_PROTO_MOCK_HEADER_FMT = "{}_mock.grpc.pb.h"
_PROTO_HEADER_FMT = "{}.pb.h"
_PROTO_SRC_FMT = "{}.pb.cc"
def _strip_package_from_path(label_package, file):
prefix_len = 0
if not file.is_source and file.path.startswith(file.root.path):
prefix_len = len(file.root.path) + 1
path = file.path
if len(label_package) == 0:
return path
if not path.startswith(label_package + "/", prefix_len):
fail("'{}' does not lie within '{}'.".format(path, label_package))
return path[prefix_len + len(label_package + "/"):]
def _get_srcs_file_path(file):
if not file.is_source and file.path.startswith(file.root.path):
return file.path[len(file.root.path) + 1:]
return file.path
def _join_directories(directories):
massaged_directories = [directory for directory in directories if len(directory) != 0]
return "/".join(massaged_directories)
def generate_cc_impl(ctx):
"""Implementation of the generate_cc rule."""
protos = [f for src in ctx.attr.srcs for f in src.proto.check_deps_sources]
includes = [
f
for src in ctx.attr.srcs
for f in src.proto.transitive_imports
]
outs = []
proto_root = get_proto_root(
ctx.label.workspace_root,
)
label_package = _join_directories([ctx.label.workspace_root, ctx.label.package])
if ctx.executable.plugin:
outs += [
proto_path_to_generated_filename(
_strip_package_from_path(label_package, proto),
_GRPC_PROTO_HEADER_FMT,
)
for proto in protos
]
outs += [
proto_path_to_generated_filename(
_strip_package_from_path(label_package, proto),
_GRPC_PROTO_SRC_FMT,
)
for proto in protos
]
if ctx.attr.generate_mocks:
outs += [
proto_path_to_generated_filename(
_strip_package_from_path(label_package, proto),
_GRPC_PROTO_MOCK_HEADER_FMT,
)
for proto in protos
]
else:
outs += [
proto_path_to_generated_filename(
_strip_package_from_path(label_package, proto),
_PROTO_HEADER_FMT,
)
for proto in protos
]
outs += [
proto_path_to_generated_filename(
_strip_package_from_path(label_package, proto),
_PROTO_SRC_FMT,
)
for proto in protos
]
out_files = [ctx.actions.declare_file(out) for out in outs]
dir_out = str(ctx.genfiles_dir.path + proto_root)
arguments = []
if ctx.executable.plugin:
arguments += get_plugin_args(
ctx.executable.plugin,
ctx.attr.flags,
dir_out,
ctx.attr.generate_mocks,
)
tools = [ctx.executable.plugin]
else:
arguments += ["--cpp_out=" + ",".join(ctx.attr.flags) + ":" + dir_out]
tools = []
arguments += get_include_protoc_args(includes)
# Include the output directory so that protoc puts the generated code in the
# right directory.
arguments += ["--proto_path={0}{1}".format(dir_out, proto_root)]
arguments += [_get_srcs_file_path(proto) for proto in protos]
# create a list of well known proto files if the argument is non-None
well_known_proto_files = []
if ctx.attr.well_known_protos:
f = ctx.attr.well_known_protos.files.to_list()[0].dirname
if f != "external/com_google_protobuf/src/google/protobuf":
print(
"Error: Only @com_google_protobuf//:well_known_protos is supported",
)
else:
# f points to "external/com_google_protobuf/src/google/protobuf"
# add -I argument to protoc so it knows where to look for the proto files.
arguments += ["-I{0}".format(f + "/../..")]
well_known_proto_files = [
f
for f in ctx.attr.well_known_protos.files
]
ctx.actions.run(
inputs = protos + includes + well_known_proto_files,
tools = tools,
outputs = out_files,
executable = ctx.executable._protoc,
arguments = arguments,
)
return struct(files = depset(out_files))
_generate_cc = rule(
attrs = {
"srcs": attr.label_list(
mandatory = True,
allow_empty = False,
providers = ["proto"],
),
"plugin": attr.label(
executable = True,
providers = ["files_to_run"],
cfg = "host",
),
"flags": attr.string_list(
mandatory = False,
allow_empty = True,
),
"well_known_protos": attr.label(mandatory = False),
"generate_mocks": attr.bool(
default = False,
mandatory = False,
),
"_protoc": attr.label(
default = Label("//external:protocol_compiler"),
executable = True,
cfg = "host",
),
},
# We generate .h files, so we need to output to genfiles.
output_to_genfiles = True,
implementation = generate_cc_impl,
)
def generate_cc(well_known_protos, **kwargs):
if well_known_protos:
_generate_cc(
well_known_protos = "@com_google_protobuf//:well_known_protos",
**kwargs
)
else:
_generate_cc(**kwargs)