xref: /aosp_15_r20/external/perfetto/bazel/proto_gen.bzl (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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    proto_paths = [
30        f
31        for dep in ctx.attr.deps
32        for f in dep[ProtoInfo].transitive_proto_path.to_list()
33    ]
34
35    proto_path = "."
36
37    out_dir = ctx.bin_dir.path
38    strip_base_path = ""
39    if ctx.attr.root != "//":
40        # This path is hit in Google internal builds, where root is typically
41        # //third_party/perfetto.
42        proto_path = "."
43
44        # The below will likely be //third_party/perfetto/ but may also be any
45        # subdir under //third_party/perfetto.
46        strip_base_path = ctx.label.package + "/"
47    elif ctx.label.workspace_root:
48        # This path is hit when proto targets are built as @perfetto//:xxx
49        # instead of //:xxx. This happens in embedder builds.
50        proto_path = ctx.label.workspace_root
51
52        # We could be using the sibling repository layout, in which case we do nothing.
53        if not ctx.label.workspace_root.startswith("../"):
54            # workspace_root == "external/perfetto" and we need to rebase the paths
55            # passed to protoc.
56            out_dir += "/" + ctx.label.workspace_root
57        strip_base_path = ctx.label.workspace_root + "/"
58
59
60    out_files = []
61    suffix = ctx.attr.suffix
62    for src in proto_src:
63        base_path = src.path[:-len(".proto")]
64        if base_path.startswith(strip_base_path):
65            base_path = base_path[len(strip_base_path):]
66        out_files += [ctx.actions.declare_file(base_path + ".%s.h" % suffix)]
67        out_files += [ctx.actions.declare_file(base_path + ".%s.cc" % suffix)]
68
69    arguments = [
70        "--proto_path=" + proto_path
71        for proto_path in proto_paths
72    ]
73
74    plugin_deps = []
75    if ctx.attr.plugin:
76        wrap_arg = ctx.attr.wrapper_namespace
77        arguments += [
78            "--plugin=protoc-gen-plugin=" + ctx.executable.plugin.path,
79            "--plugin_out=wrapper_namespace=" + wrap_arg + ":" + out_dir,
80        ]
81        plugin_deps += [ctx.executable.plugin]
82    else:
83        arguments += [
84            "--cpp_out=lite=true:" + out_dir,
85        ]
86
87    arguments += [src.path for src in proto_src]
88    ctx.actions.run(
89        inputs = proto_src + includes + plugin_deps,
90        tools = plugin_deps,
91        outputs = out_files,
92        executable = ctx.executable.protoc,
93        arguments = arguments,
94    )
95    cc_files = depset([f for f in out_files if f.path.endswith(".cc")])
96    h_files = depset([f for f in out_files if f.path.endswith(".h")])
97    return [
98        DefaultInfo(files = cc_files),
99        OutputGroupInfo(
100            cc = cc_files,
101            h = h_files,
102        ),
103    ]
104
105
106proto_gen = rule(
107    attrs = {
108        "deps": attr.label_list(
109            mandatory = True,
110            allow_empty = False,
111            providers = [ProtoInfo],
112        ),
113        "plugin": attr.label(
114            executable = True,
115            mandatory = False,
116            cfg = "host",
117        ),
118        "wrapper_namespace": attr.string(
119            mandatory = False,
120            default = ""
121        ),
122        "suffix": attr.string(
123            mandatory = True,
124        ),
125        "protoc": attr.label(
126            executable = True,
127            cfg = "host",
128        ),
129        "root": attr.string(
130            mandatory = False,
131            default = "//",
132        ),
133    },
134    implementation = _proto_gen_impl,
135)
136
137
138def _proto_descriptor_gen_impl(ctx):
139    descriptors = [
140        f
141        for dep in ctx.attr.deps
142        for f in dep[ProtoInfo].transitive_descriptor_sets.to_list()
143    ]
144    ctx.actions.run_shell(
145        inputs=descriptors,
146        outputs=ctx.outputs.outs,
147        command='cat %s > %s' % (
148            ' '.join([f.path for f in descriptors]), ctx.outputs.outs[0].path)
149    )
150
151
152proto_descriptor_gen = rule(
153    implementation=_proto_descriptor_gen_impl,
154    attrs = {
155        "deps": attr.label_list(
156            mandatory = True,
157            allow_empty = False,
158            providers = [ProtoInfo],
159        ),
160        "outs": attr.output_list(mandatory=True),
161    }
162)
163