-
Notifications
You must be signed in to change notification settings - Fork 15.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Copy py_proto_library from rules_python to protobuf
https://github.com/bazelbuild/rules_python/blob/main/python/private/proto/py_proto_library.bzl Contributors: d96214f [email protected] Wed Nov 15 02:48:06 2023 -0800 fix: py_proto_library: transitive strip_import_prefix (#1558) 85e50d2 [email protected] Tue Nov 14 06:04:59 2023 -0800 fix: py_proto_library: append to PYTHONPATH less (#1553) bee35ef [email protected] Wed Oct 11 20:59:34 2023 -0700 fix: allowing to import code generated from proto with strip_import_prefix (#1406) 1a333ce [email protected] Tue Jun 20 19:36:39 2023 +0200 fix: plugin_output in py_proto_library rule (#1280) 6905e63 [email protected] Sat Feb 11 14:02:33 2023 +0900 fix: make py_proto_library respect PyInfo imports (#1046) 0d3c4f7 [email protected] Wed Jan 18 23:15:52 2023 +0000 Implement py_proto_library (#832) PiperOrigin-RevId: 623401031
- Loading branch information
1 parent
a94f57b
commit 8257c44
Showing
4 changed files
with
242 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
"""The implementation of the `py_proto_library` rule and its aspect.""" | ||
|
||
load("@rules_python//python:py_info.bzl", "PyInfo") | ||
load("//bazel/common:proto_common.bzl", "proto_common") | ||
load("//bazel/common:proto_info.bzl", "ProtoInfo") | ||
|
||
ProtoLangToolchainInfo = proto_common.ProtoLangToolchainInfo | ||
|
||
_PyProtoInfo = provider( | ||
doc = "Encapsulates information needed by the Python proto rules.", | ||
fields = { | ||
"imports": """ | ||
(depset[str]) The field forwarding PyInfo.imports coming from | ||
the proto language runtime dependency.""", | ||
"runfiles_from_proto_deps": """ | ||
(depset[File]) Files from the transitive closure implicit proto | ||
dependencies""", | ||
"transitive_sources": """(depset[File]) The Python sources.""", | ||
}, | ||
) | ||
|
||
def _filter_provider(provider, *attrs): | ||
return [dep[provider] for attr in attrs for dep in attr if provider in dep] | ||
|
||
def _py_proto_aspect_impl(target, ctx): | ||
"""Generates and compiles Python code for a proto_library. | ||
The function runs protobuf compiler on the `proto_library` target generating | ||
a .py file for each .proto file. | ||
Args: | ||
target: (Target) A target providing `ProtoInfo`. Usually this means a | ||
`proto_library` target, but not always; you must expect to visit | ||
non-`proto_library` targets, too. | ||
ctx: (RuleContext) The rule context. | ||
Returns: | ||
([_PyProtoInfo]) Providers collecting transitive information about | ||
generated files. | ||
""" | ||
|
||
_proto_library = ctx.rule.attr | ||
|
||
# Check Proto file names | ||
for proto in target[ProtoInfo].direct_sources: | ||
if proto.is_source and "-" in proto.dirname: | ||
fail("Cannot generate Python code for a .proto whose path contains '-' ({}).".format( | ||
proto.path, | ||
)) | ||
|
||
proto_lang_toolchain_info = ctx.attr._aspect_proto_toolchain[ProtoLangToolchainInfo] | ||
api_deps = [proto_lang_toolchain_info.runtime] | ||
|
||
generated_sources = [] | ||
proto_info = target[ProtoInfo] | ||
proto_root = proto_info.proto_source_root | ||
if proto_info.direct_sources: | ||
# Generate py files | ||
generated_sources = proto_common.declare_generated_files( | ||
actions = ctx.actions, | ||
proto_info = proto_info, | ||
extension = "_pb2.py", | ||
name_mapper = lambda name: name.replace("-", "_").replace(".", "/"), | ||
) | ||
|
||
# Handles multiple repository and virtual import cases | ||
if proto_root.startswith(ctx.bin_dir.path): | ||
proto_root = proto_root[len(ctx.bin_dir.path) + 1:] | ||
|
||
plugin_output = ctx.bin_dir.path + "/" + proto_root | ||
proto_root = ctx.workspace_name + "/" + proto_root | ||
|
||
proto_common.compile( | ||
actions = ctx.actions, | ||
proto_info = proto_info, | ||
proto_lang_toolchain_info = proto_lang_toolchain_info, | ||
generated_files = generated_sources, | ||
plugin_output = plugin_output, | ||
) | ||
|
||
# Generated sources == Python sources | ||
python_sources = generated_sources | ||
|
||
deps = _filter_provider(_PyProtoInfo, getattr(_proto_library, "deps", [])) | ||
runfiles_from_proto_deps = depset( | ||
transitive = [dep[DefaultInfo].default_runfiles.files for dep in api_deps] + | ||
[dep.runfiles_from_proto_deps for dep in deps], | ||
) | ||
transitive_sources = depset( | ||
direct = python_sources, | ||
transitive = [dep.transitive_sources for dep in deps], | ||
) | ||
|
||
return [ | ||
_PyProtoInfo( | ||
imports = depset( | ||
# Adding to PYTHONPATH so the generated modules can be | ||
# imported. This is necessary when there is | ||
# strip_import_prefix, the Python modules are generated under | ||
# _virtual_imports. But it's undesirable otherwise, because it | ||
# will put the repo root at the top of the PYTHONPATH, ahead of | ||
# directories added through `imports` attributes. | ||
[proto_root] if "_virtual_imports" in proto_root else [], | ||
transitive = [dep[PyInfo].imports for dep in api_deps] + [dep.imports for dep in deps], | ||
), | ||
runfiles_from_proto_deps = runfiles_from_proto_deps, | ||
transitive_sources = transitive_sources, | ||
), | ||
] | ||
|
||
_py_proto_aspect = aspect( | ||
implementation = _py_proto_aspect_impl, | ||
attrs = { | ||
"_aspect_proto_toolchain": attr.label( | ||
default = "//python:python_toolchain", | ||
), | ||
}, | ||
attr_aspects = ["deps"], | ||
required_providers = [ProtoInfo], | ||
provides = [_PyProtoInfo], | ||
) | ||
|
||
def _py_proto_library_rule(ctx): | ||
"""Merges results of `py_proto_aspect` in `deps`. | ||
Args: | ||
ctx: (RuleContext) The rule context. | ||
Returns: | ||
([PyInfo, DefaultInfo, OutputGroupInfo]) | ||
""" | ||
if not ctx.attr.deps: | ||
fail("'deps' attribute mustn't be empty.") | ||
|
||
pyproto_infos = _filter_provider(_PyProtoInfo, ctx.attr.deps) | ||
default_outputs = depset( | ||
transitive = [info.transitive_sources for info in pyproto_infos], | ||
) | ||
|
||
return [ | ||
DefaultInfo( | ||
files = default_outputs, | ||
default_runfiles = ctx.runfiles(transitive_files = depset( | ||
transitive = | ||
[default_outputs] + | ||
[info.runfiles_from_proto_deps for info in pyproto_infos], | ||
)), | ||
), | ||
OutputGroupInfo( | ||
default = depset(), | ||
), | ||
PyInfo( | ||
transitive_sources = default_outputs, | ||
imports = depset(transitive = [info.imports for info in pyproto_infos]), | ||
# Proto always produces 2- and 3- compatible source files | ||
has_py2_only_sources = False, | ||
has_py3_only_sources = False, | ||
), | ||
] | ||
|
||
py_proto_library = rule( | ||
implementation = _py_proto_library_rule, | ||
doc = """ | ||
Use `py_proto_library` to generate Python libraries from `.proto` files. | ||
The convention is to name the `py_proto_library` rule `foo_py_pb2`, | ||
when it is wrapping `proto_library` rule `foo_proto`. | ||
`deps` must point to a `proto_library` rule. | ||
Example: | ||
```starlark | ||
py_library( | ||
name = "lib", | ||
deps = [":foo_py_pb2"], | ||
) | ||
py_proto_library( | ||
name = "foo_py_pb2", | ||
deps = [":foo_proto"], | ||
) | ||
proto_library( | ||
name = "foo_proto", | ||
srcs = ["foo.proto"], | ||
) | ||
```""", | ||
attrs = { | ||
"deps": attr.label_list( | ||
doc = """ | ||
The list of `proto_library` rules to generate Python libraries for. | ||
Usually this is just the one target: the proto library of interest. | ||
It can be any target providing `ProtoInfo`.""", | ||
providers = [ProtoInfo], | ||
aspects = [_py_proto_aspect], | ||
), | ||
}, | ||
provides = [PyInfo], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters