• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (C) 2022 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15load("@bazel_skylib//lib:paths.bzl", "paths")
16
17def _py_proto_sources_gen_rule_impl(ctx):
18    imports = []
19    all_outputs = []
20    for dep in ctx.attr.deps:
21        proto_info = dep[ProtoInfo]
22
23        outputs = []
24        for name in proto_info.direct_sources:
25            outputs.append(ctx.actions.declare_file(paths.replace_extension(name.basename, "_pb2.py"), sibling = name))
26
27        args = ctx.actions.args()
28        args.add("--python_out=" + proto_info.proto_source_root)
29        args.add_all(["-I", proto_info.proto_source_root])
30        args.add_all(proto_info.direct_sources)
31
32        if proto_info.proto_source_root != ".":
33            imports.append(paths.join("__main__", paths.relativize(proto_info.proto_source_root, ctx.bin_dir.path)))
34
35        # It's not clear what to do with transititve imports/sources
36        if len(proto_info.transitive_imports.to_list()) > len(proto_info.direct_sources) or len(proto_info.transitive_sources.to_list()) > len(proto_info.direct_sources):
37            fail("TODO: Transitive imports/sources of python protos")
38
39        ctx.actions.run(
40            inputs = depset(
41                direct = proto_info.direct_sources,
42                transitive = [proto_info.transitive_imports],
43            ),
44            executable = ctx.executable._protoc,
45            outputs = outputs,
46            arguments = [args],
47            mnemonic = "PyProtoGen",
48        )
49
50        all_outputs.extend(outputs)
51
52    output_depset = depset(direct = all_outputs)
53    return [
54        DefaultInfo(files = output_depset),
55        PyInfo(
56            transitive_sources = output_depset,
57            # If proto_source_root is set to something other than the root of the workspace, import the current package.
58            # It's always the current package because it's the path to where we generated the python sources, not to where
59            # the proto sources are.
60            imports = depset(direct = imports),
61        ),
62    ]
63
64_py_proto_sources_gen = rule(
65    implementation = _py_proto_sources_gen_rule_impl,
66    attrs = {
67        "deps": attr.label_list(
68            providers = [ProtoInfo],
69            doc = "proto_library or any other target exposing ProtoInfo provider with *.proto files",
70            mandatory = True,
71        ),
72        "_protoc": attr.label(
73            default = Label("//external/protobuf:aprotoc"),
74            executable = True,
75            cfg = "exec",
76        ),
77    },
78)
79
80def py_proto_library(
81        name,
82        deps = [],
83        target_compatible_with = [],
84        **kwargs):
85    proto_lib_name = name + "_proto_gen"
86
87    _py_proto_sources_gen(
88        name = proto_lib_name,
89        deps = deps,
90        **kwargs
91    )
92
93    # There may be a better way to do this, but proto_lib_name appears in both srcs
94    # and deps because it must appear in srcs to cause the protobuf files to
95    # actually be compiled, and it must appear in deps for the PyInfo provider to
96    # be respected and the "imports" path to be included in this library.
97    native.py_library(
98        name = name,
99        srcs = [":" + proto_lib_name],
100        deps = [":" + proto_lib_name] + (["//external/protobuf:libprotobuf-python"] if "libprotobuf-python" not in name else []),
101        target_compatible_with = target_compatible_with,
102        **kwargs
103    )
104