1# Copyright (C) 2019 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
15# This file defines the proto_gen() rule that is used for generating protos
16# with custom plugins (ipc and protozero).
17
18def _proto_gen_impl(ctx):
19    proto_src = [
20        f
21        for dep in ctx.attr.deps
22        for f in dep[ProtoInfo].direct_sources
23    ]
24    includes = [
25        f
26        for dep in ctx.attr.deps
27        for f in dep[ProtoInfo].transitive_imports.to_list()
28    ]
29
30    proto_path = "."
31
32    out_dir = str(ctx.genfiles_dir.path)
33    strip_base_path = ""
34    if ctx.attr.root != "//":
35        # This path is hit in Google internal builds, where root is typically
36        # //third_party/perfetto.
37        proto_path = "."
38
39        # The below will likely be //third_party/perfetto/ but may also be any
40        # subdir under //third_party/perfetto.
41        last_slash_idx = ctx.build_file_path.rfind("/")
42        strip_base_path = ctx.build_file_path[:last_slash_idx + 1]
43    elif ctx.label.workspace_root:
44        # This path is hit when proto targets are built as @perfetto//:xxx
45        # instead of //:xxx. This happens in embedder builds. In this case,
46        # workspace_root == "external/perfetto" and we need to rebase the paths
47        # passed to protoc.
48        proto_path = ctx.label.workspace_root
49        out_dir += "/" + ctx.label.workspace_root
50        strip_base_path = ctx.label.workspace_root + "/"
51
52    out_files = []
53    suffix = ctx.attr.suffix
54    for src in proto_src:
55        base_path = src.path[:-len(".proto")]
56        if base_path.startswith(strip_base_path):
57            base_path = base_path[len(strip_base_path):]
58        out_files += [ctx.actions.declare_file(base_path + ".%s.h" % suffix)]
59        out_files += [ctx.actions.declare_file(base_path + ".%s.cc" % suffix)]
60
61    arguments = [
62        "--proto_path=" + proto_path,
63    ]
64    plugin_deps = []
65    if ctx.attr.plugin:
66        wrap_arg = ctx.attr.wrapper_namespace
67        arguments += [
68            "--plugin=protoc-gen-plugin=" + ctx.executable.plugin.path,
69            "--plugin_out=wrapper_namespace=" + wrap_arg + ":" + out_dir,
70        ]
71        plugin_deps += [ctx.executable.plugin]
72    else:
73        arguments += [
74            "--cpp_out=lite=true:" + out_dir,
75        ]
76
77    arguments += [src.path for src in proto_src]
78    ctx.actions.run(
79        inputs = proto_src + includes + plugin_deps,
80        tools = plugin_deps,
81        outputs = out_files,
82        executable = ctx.executable.protoc,
83        arguments = arguments,
84    )
85    cc_files = depset([f for f in out_files if f.path.endswith(".cc")])
86    h_files = depset([f for f in out_files if f.path.endswith(".h")])
87    return [
88        DefaultInfo(files = cc_files),
89        OutputGroupInfo(
90            cc = cc_files,
91            h = h_files,
92        ),
93    ]
94
95
96proto_gen = rule(
97    attrs = {
98        "deps": attr.label_list(
99            mandatory = True,
100            allow_empty = False,
101            providers = [ProtoInfo],
102        ),
103        "plugin": attr.label(
104            executable = True,
105            mandatory = False,
106            cfg = "host",
107        ),
108        "wrapper_namespace": attr.string(
109            mandatory = False,
110            default = ""
111        ),
112        "suffix": attr.string(
113            mandatory = True,
114        ),
115        "protoc": attr.label(
116            executable = True,
117            cfg = "host",
118        ),
119        "root": attr.string(
120            mandatory = False,
121            default = "//",
122        ),
123    },
124    output_to_genfiles = True,
125    implementation = _proto_gen_impl,
126)
127
128
129def _proto_descriptor_gen_impl(ctx):
130    descriptors = [
131        f
132        for dep in ctx.attr.deps
133        for f in dep[ProtoInfo].transitive_descriptor_sets.to_list()
134    ]
135    ctx.actions.run_shell(
136        inputs=descriptors,
137        outputs=ctx.outputs.outs,
138        command='cat %s > %s' % (
139            ' '.join([f.path for f in descriptors]), ctx.outputs.outs[0].path)
140    )
141
142
143proto_descriptor_gen = rule(
144    implementation=_proto_descriptor_gen_impl,
145    attrs = {
146        "deps": attr.label_list(
147            mandatory = True,
148            allow_empty = False,
149            providers = [ProtoInfo],
150        ),
151        "outs": attr.output_list(mandatory=True),
152    }
153)
154