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
15load("@perfetto_cfg//:perfetto_cfg.bzl", "PERFETTO_CONFIG")
16load("@perfetto//bazel:proto_gen.bzl", "proto_descriptor_gen", "proto_gen")
17
18# +----------------------------------------------------------------------------+
19# | Base C++ rules.                                                            |
20# +----------------------------------------------------------------------------+
21
22def default_cc_args():
23    return {
24        "deps": PERFETTO_CONFIG.deps.build_config,
25        "copts": [
26            "-Wno-pragma-system-header-outside-header",
27        ],
28        "includes": ["include"],
29        "linkopts": select({
30            "@perfetto//bazel:os_linux": ["-ldl", "-lrt", "-lpthread"],
31            "@perfetto//bazel:os_osx": [],
32            "@perfetto//bazel:os_windows": [],
33            "//conditions:default": ["-ldl"],
34        }),
35    }
36
37def perfetto_cc_library(**kwargs):
38    args = _merge_dicts(default_cc_args(), kwargs)
39    if not _rule_override("cc_library", **args):
40        native.cc_library(**args)
41
42def perfetto_cc_binary(**kwargs):
43    args = _merge_dicts(default_cc_args(), kwargs)
44    if not _rule_override("cc_binary", **args):
45        native.cc_binary(**args)
46
47def perfetto_py_binary(**kwargs):
48    if not _rule_override("py_binary", **kwargs):
49        native.py_binary(**kwargs)
50
51def perfetto_py_library(**kwargs):
52    if not _rule_override("py_library", **kwargs):
53        native.py_library(**kwargs)
54
55# +----------------------------------------------------------------------------+
56# | Proto-related rules                                                        |
57# +----------------------------------------------------------------------------+
58
59def perfetto_proto_library(**kwargs):
60    if not _rule_override("proto_library", **kwargs):
61        native.proto_library(**kwargs)
62
63def perfetto_cc_proto_library(**kwargs):
64    if not _rule_override("cc_proto_library", **kwargs):
65        native.cc_proto_library(**kwargs)
66
67def perfetto_java_proto_library(**kwargs):
68    if not _rule_override("java_proto_library", **kwargs):
69        native.java_proto_library(**kwargs)
70
71def perfetto_java_lite_proto_library(**kwargs):
72    if not _rule_override("java_lite_proto_library", **kwargs):
73        native.java_lite_proto_library(**kwargs)
74
75# +----------------------------------------------------------------------------+
76# | Misc rules.                                                                |
77# +----------------------------------------------------------------------------+
78
79# Unlike all the other rules, this is an noop by default because Bazel does not
80# support gensignature.
81def perfetto_gensignature_internal_only(**kwargs):
82    _rule_override("gensignature_internal_only", **kwargs)
83
84# Generates .pbzero.{cc,h} from .proto(s). We deliberately do NOT generate
85# conventional .pb.{cc,h} from here as protozero gen sources do not have any
86# dependency on libprotobuf.
87def perfetto_cc_protozero_library(name, deps, **kwargs):
88    if _rule_override(
89        "cc_protozero_library",
90        name = name,
91        deps = deps,
92        **kwargs
93    ):
94        return
95
96    # A perfetto_cc_protozero_library has two types of dependencies:
97    # 1. Exactly one dependency on a proto_library target. This defines the
98    #    .proto sources for the target
99    # 2. Zero or more deps on other perfetto_cc_protozero_library targets. This
100    #    to deal with the case of foo.proto including common.proto from another
101    #    target.
102    _proto_deps = [d for d in deps if d.endswith("_protos")]
103    _cc_deps = [d for d in deps if d not in _proto_deps]
104    if len(_proto_deps) != 1:
105        fail("Too many proto deps for target %s" % name)
106
107    proto_gen(
108        name = name + "_src",
109        deps = _proto_deps,
110        suffix = "pbzero",
111        plugin = PERFETTO_CONFIG.root + ":protozero_plugin",
112        wrapper_namespace = "pbzero",
113        protoc = PERFETTO_CONFIG.deps.protoc[0],
114        root = PERFETTO_CONFIG.root,
115    )
116
117    native.filegroup(
118        name = name + "_h",
119        srcs = [":" + name + "_src"],
120        output_group = "h",
121    )
122
123    perfetto_cc_library(
124        name = name,
125        srcs = [":" + name + "_src"],
126        hdrs = [":" + name + "_h"],
127        deps = [PERFETTO_CONFIG.root + ":protozero"] + _cc_deps,
128        **kwargs
129    )
130
131# Generates .ipc.{cc,h} and .pb.{cc.h} from .proto(s). The IPC sources depend
132# on .pb.h so we need to generate also the standard protobuf sources here.
133def perfetto_cc_ipc_library(name, deps, **kwargs):
134    if _rule_override("cc_ipc_library", name = name, deps = deps, **kwargs):
135        return
136
137    # A perfetto_cc_ipc_library has two types of dependencies:
138    # 1. Exactly one dependency on a proto_library target. This defines the
139    #    .proto sources for the target
140    # 2. Zero or more deps on other perfetto_cc_protocpp_library targets. This
141    #    to deal with the case of foo.proto including common.proto from another
142    #    target.
143    _proto_deps = [d for d in deps if d.endswith("_protos")]
144    _cc_deps = [d for d in deps if d not in _proto_deps]
145    if len(_proto_deps) != 1:
146        fail("Too many proto deps for target %s" % name)
147
148    # Generates .ipc.{cc,h}.
149    proto_gen(
150        name = name + "_src",
151        deps = _proto_deps,
152        suffix = "ipc",
153        plugin = PERFETTO_CONFIG.root + ":ipc_plugin",
154        wrapper_namespace = "gen",
155        protoc = PERFETTO_CONFIG.deps.protoc[0],
156        root = PERFETTO_CONFIG.root,
157    )
158
159    native.filegroup(
160        name = name + "_h",
161        srcs = [":" + name + "_src"],
162        output_group = "h",
163    )
164
165    perfetto_cc_library(
166        name = name,
167        srcs = [":" + name + "_src"],
168        hdrs = [":" + name + "_h"],
169        deps = [
170            # Generated .ipc.{cc,h} depend on this and protozero.
171            PERFETTO_CONFIG.root + ":perfetto_ipc",
172            PERFETTO_CONFIG.root + ":protozero",
173        ] + _cc_deps,
174        **kwargs
175    )
176
177# Generates .gen.{cc,h} from .proto(s).
178def perfetto_cc_protocpp_library(name, deps, **kwargs):
179    if _rule_override(
180        "cc_protocpp_library",
181        name = name,
182        deps = deps,
183        **kwargs
184    ):
185        return
186
187    # A perfetto_cc_protocpp_library has two types of dependencies:
188    # 1. Exactly one dependency on a proto_library target. This defines the
189    #    .proto sources for the target
190    # 2. Zero or more deps on other perfetto_cc_protocpp_library targets. This
191    #    to deal with the case of foo.proto including common.proto from another
192    #    target.
193    _proto_deps = [d for d in deps if d.endswith("_protos")]
194    _cc_deps = [d for d in deps if d not in _proto_deps]
195    if len(_proto_deps) != 1:
196        fail("Too many proto deps for target %s" % name)
197
198    proto_gen(
199        name = name + "_gen",
200        deps = _proto_deps,
201        suffix = "gen",
202        plugin = PERFETTO_CONFIG.root + ":cppgen_plugin",
203        wrapper_namespace = "gen",
204        protoc = PERFETTO_CONFIG.deps.protoc[0],
205        root = PERFETTO_CONFIG.root,
206    )
207
208    native.filegroup(
209        name = name + "_gen_h",
210        srcs = [":" + name + "_gen"],
211        output_group = "h",
212    )
213
214    # The headers from the gen plugin have implicit dependencies
215    # on each other so will fail when compiled independently. Use
216    # textual_hdrs to indicate this to Bazel.
217    perfetto_cc_library(
218        name = name,
219        srcs = [":" + name + "_gen"],
220        textual_hdrs = [":" + name + "_gen_h"],
221        deps = [
222            PERFETTO_CONFIG.root + ":protozero",
223        ] + _cc_deps,
224        **kwargs
225    )
226
227def perfetto_proto_descriptor(name, deps, outs, **kwargs):
228    proto_descriptor_gen(
229        name = name,
230        deps = deps,
231        outs = outs,
232    )
233
234# Generator .descriptor.h from protos
235def perfetto_cc_proto_descriptor(name, deps, outs, **kwargs):
236    cmd = [
237        "$(location gen_cc_proto_descriptor_py)",
238        "--cpp_out=$@",
239        "--gen_dir=$(GENDIR)",
240        "$<"
241    ]
242    native.genrule(
243        name = name + "_gen",
244        cmd = " ".join(cmd),
245        exec_tools = [
246            ":gen_cc_proto_descriptor_py",
247        ],
248        srcs = deps,
249        outs = outs,
250    )
251
252    perfetto_cc_library(
253        name = name,
254        hdrs = [":" + name + "_gen"],
255        **kwargs
256    )
257
258# +----------------------------------------------------------------------------+
259# | Misc utility functions                                                     |
260# +----------------------------------------------------------------------------+
261
262def _rule_override(rule_name, **kwargs):
263    overrides = getattr(PERFETTO_CONFIG, "rule_overrides", struct())
264    overridden_rule = getattr(overrides, rule_name, None)
265    if overridden_rule:
266        overridden_rule(**kwargs)
267        return True
268    return False
269
270def _merge_dicts(*args):
271    res = {}
272    for arg in args:
273        for k, v in arg.items():
274            if type(v) == "string" or type(v) == "bool":
275                res[k] = v
276            elif type(v) == "list" or type(v) == "select":
277                res[k] = res.get(k, []) + v
278            else:
279                fail("key type not supported: " + type(v))
280    return res
281