1 """Generates and compiles Python gRPC stubs from proto_library rules."""
3 load("@grpc_python_dependencies//:requirements.bzl", "requirement")
5 "//bazel:protobuf.bzl",
6 "get_include_protoc_args",
9 "proto_path_to_generated_filename",
12 _GENERATED_PROTO_FORMAT = "{}_pb2.py"
13 _GENERATED_GRPC_PROTO_FORMAT = "{}_pb2_grpc.py"
15 def _get_staged_proto_file(context, source_file):
16 if source_file.dirname == context.label.package:
19 copied_proto = context.actions.declare_file(source_file.basename)
20 context.actions.run_shell(
21 inputs = [source_file],
22 outputs = [copied_proto],
23 command = "cp {} {}".format(source_file.path, copied_proto.path),
24 mnemonic = "CopySourceProto",
28 def _generate_py_impl(context):
30 for src in context.attr.deps:
31 for file in src.proto.direct_sources:
32 protos.append(_get_staged_proto_file(context, file))
35 for src in context.attr.deps
36 for file in src.proto.transitive_imports.to_list()
38 proto_root = get_proto_root(context.label.workspace_root)
39 format_str = (_GENERATED_GRPC_PROTO_FORMAT if context.executable.plugin else _GENERATED_PROTO_FORMAT)
41 context.actions.declare_file(
42 proto_path_to_generated_filename(
51 tools = [context.executable._protoc]
52 if context.executable.plugin:
53 arguments += get_plugin_args(
54 context.executable.plugin,
56 context.genfiles_dir.path,
59 tools += [context.executable.plugin]
62 "--python_out={}:{}".format(
63 ",".join(context.attr.flags),
64 context.genfiles_dir.path,
68 arguments += get_include_protoc_args(includes)
70 "--proto_path={}".format(context.genfiles_dir.path)
74 massaged_path = proto.path
75 if massaged_path.startswith(context.genfiles_dir.path):
76 massaged_path = proto.path[len(context.genfiles_dir.path) + 1:]
77 arguments.append(massaged_path)
79 well_known_proto_files = []
80 if context.attr.well_known_protos:
81 well_known_proto_directory = context.attr.well_known_protos.files.to_list(
84 arguments += ["-I{}".format(well_known_proto_directory + "/../..")]
85 well_known_proto_files = context.attr.well_known_protos.files.to_list()
88 inputs = protos + includes + well_known_proto_files,
91 executable = context.executable._protoc,
92 arguments = arguments,
93 mnemonic = "ProtocInvocation",
95 return struct(files = depset(out_files))
99 "deps": attr.label_list(
102 providers = ["proto"],
104 "plugin": attr.label(
106 providers = ["files_to_run"],
109 "flags": attr.string_list(
113 "well_known_protos": attr.label(mandatory = False),
114 "_protoc": attr.label(
115 default = Label("//external:protocol_compiler"),
120 output_to_genfiles = True,
121 implementation = _generate_py_impl,
124 def _generate_py(well_known_protos, **kwargs):
125 if well_known_protos:
127 well_known_protos = "@com_google_protobuf//:well_known_protos",
131 __generate_py(**kwargs)
133 def py_proto_library(
136 well_known_protos = True,
139 """Generate python code for a protobuf.
142 name: The name of the target.
143 deps: A list of dependencies. Must contain a single element.
144 well_known_protos: A bool indicating whether or not to include well-known
146 proto_only: A bool indicating whether to generate vanilla protobuf code
147 or to also generate gRPC code.
150 fail("The supported length of 'deps' is 1.")
152 codegen_target = "_{}_codegen".format(name)
153 codegen_grpc_target = "_{}_grpc_codegen".format(name)
156 name = codegen_target,
158 well_known_protos = well_known_protos,
164 name = codegen_grpc_target,
166 plugin = "//:grpc_python_plugin",
167 well_known_protos = well_known_protos,
174 ":{}".format(codegen_grpc_target),
175 ":{}".format(codegen_target),
177 deps = [requirement("protobuf")],
183 srcs = [":{}".format(codegen_target), ":{}".format(codegen_target)],
184 deps = [requirement("protobuf")],