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//bazel:proto_gen.bzl", "proto_descriptor_gen", "proto_gen") 16load("@perfetto//bazel:run_ait_with_adb.bzl", "android_instrumentation_test") 17load("@perfetto_cfg//:perfetto_cfg.bzl", "PERFETTO_CONFIG") 18load("@rules_android//android:rules.bzl", "android_binary", "android_library") 19 20# +----------------------------------------------------------------------------+ 21# | Base C++ rules. | 22# +----------------------------------------------------------------------------+ 23 24def default_cc_args(): 25 return { 26 "deps": PERFETTO_CONFIG.deps.build_config, 27 "copts": PERFETTO_CONFIG.default_copts + [ 28 "-Wno-pragma-system-header-outside-header", 29 ], 30 "includes": ["include"], 31 "linkopts": select({ 32 "@perfetto//bazel:os_linux": ["-ldl", "-lrt", "-lpthread"], 33 "@perfetto//bazel:os_osx": [], 34 "@perfetto//bazel:os_windows": ["ws2_32.lib"], 35 "//conditions:default": ["-ldl"], 36 }), 37 } 38 39def perfetto_build_config_cc_library(**kwargs): 40 if not _rule_override("cc_library", **kwargs): 41 native.cc_library(**kwargs) 42 43def perfetto_filegroup(**kwargs): 44 if not _rule_override("filegroup", **kwargs): 45 native.filegroup(**kwargs) 46 47def perfetto_genrule(**kwargs): 48 if not _rule_override("genrule", **kwargs): 49 native.genrule(**kwargs) 50 51def perfetto_cc_library(**kwargs): 52 args = _merge_dicts(default_cc_args(), kwargs) 53 if not _rule_override("cc_library", **args): 54 native.cc_library(**args) 55 56def perfetto_cc_binary(**kwargs): 57 args = _merge_dicts(default_cc_args(), kwargs) 58 if not _rule_override("cc_binary", **args): 59 native.cc_binary(**args) 60 61def perfetto_py_binary(**kwargs): 62 if not _rule_override("py_binary", **kwargs): 63 native.py_binary(**kwargs) 64 65def perfetto_py_library(**kwargs): 66 if not _rule_override("py_library", **kwargs): 67 native.py_library(**kwargs) 68 69# +----------------------------------------------------------------------------+ 70# | Proto-related rules | 71# +----------------------------------------------------------------------------+ 72 73def perfetto_proto_library(**kwargs): 74 if not _rule_override("proto_library", **kwargs): 75 native.proto_library(**kwargs) 76 77def perfetto_cc_proto_library(**kwargs): 78 if not _rule_override("cc_proto_library", **kwargs): 79 native.cc_proto_library(**kwargs) 80 81def perfetto_java_proto_library(**kwargs): 82 if not _rule_override("java_proto_library", **kwargs): 83 native.java_proto_library(**kwargs) 84 85def perfetto_java_lite_proto_library(**kwargs): 86 if not _rule_override("java_lite_proto_library", **kwargs): 87 native.java_lite_proto_library(**kwargs) 88 89# Unlike the other rules, this is an noop by default because Bazel does not 90# support Go proto libraries. 91def perfetto_go_proto_library(**kwargs): 92 _rule_override("go_proto_library", **kwargs) 93 94# Unlike the other rules, this is an noop by default because Bazel does not 95# support Python proto libraries. 96def perfetto_py_proto_library(**kwargs): 97 _rule_override("py_proto_library", **kwargs) 98 99# Unlike the other rules, this is an noop by default because Bazel does not 100# support Javascript/Typescript proto libraries. 101def perfetto_jspb_proto_library(**kwargs): 102 _rule_override("jspb_proto_library", **kwargs) 103 104# +----------------------------------------------------------------------------+ 105# | Android-related rules | 106# +----------------------------------------------------------------------------+ 107def perfetto_android_binary(**kwargs): 108 if not _rule_override("android_binary", **kwargs): 109 android_binary(**kwargs) 110 111def perfetto_android_library(**kwargs): 112 if not _rule_override("android_library", **kwargs): 113 android_library(**kwargs) 114 115def perfetto_android_jni_library(**kwargs): 116 if not _rule_override("android_jni_library", **kwargs): 117 _perfetto_android_jni_library(**kwargs) 118 119def _perfetto_android_jni_library( 120 name, 121 binary_name, 122 **input_cc_library_kwargs): 123 # By default 'android_binary' rule merges all native libraries into one, 124 # named '"lib%s.so" % android_binary_target_name'. 125 # This is unsuitable for us: we want our native library to have a 126 # predictable name to be able to load it from Java using 127 # 'System.loadLibrary'. 128 # To workaround this behaviour we wrap the 'cc_library' 129 # target into 'cc_binary' that gives a name to the resulting library. 130 # The same trick is done in 'android_jni_library' macro in google3. 131 # See https://yaqs.corp.google.com/eng/q/1025522191808069632. 132 # 133 # 'binary_name' argument should be of pattern 'lib%s.so', 134 # for this macro to be consistent with android_jni_library from google3. 135 if not binary_name: 136 fail("'binary_name' shouldn't be None") 137 if not (binary_name.startswith("lib") and binary_name.endswith(".so")): 138 fail("'binary_name' should sharts with 'lib' and ends with '.so'" + 139 ", got %s instead" % binary_name) 140 # We strip the name, since `native.cc_binary` adds prefix and suffix 141 # to the generated library name. 142 binary_target_name = binary_name.removeprefix("lib").removesuffix(".so") 143 input_cc_library_name = name + "_input" 144 # We add 'target_compatible_with = ' to all the cc_library targets to 145 # exclude them from being build when invoke `bazel build :all`, 146 # since these targets won't be able to compile anyway, see 147 # https://bazel.build/docs/android-ndk#cclibrary-android. 148 native.cc_library( 149 name = input_cc_library_name, 150 target_compatible_with = ["@platforms//os:android"], 151 **input_cc_library_kwargs 152 ) 153 native.cc_binary( 154 name = binary_target_name, 155 linkshared = True, 156 deps = [input_cc_library_name], 157 target_compatible_with = ["@platforms//os:android"], 158 ) 159 native.cc_library( 160 name = name, 161 srcs = [binary_target_name], 162 target_compatible_with = ["@platforms//os:android"], 163 ) 164 165def perfetto_android_instrumentation_test(**kwargs): 166 if not _rule_override("android_instrumentation_test", **kwargs): 167 android_instrumentation_test(**kwargs) 168 169# +----------------------------------------------------------------------------+ 170# | Misc rules. | 171# +----------------------------------------------------------------------------+ 172 173# Generates .pbzero.{cc,h} from .proto(s). We deliberately do NOT generate 174# conventional .pb.{cc,h} from here as protozero gen sources do not have any 175# dependency on libprotobuf. 176def perfetto_cc_protozero_library(name, deps, **kwargs): 177 if _rule_override( 178 "cc_protozero_library", 179 name = name, 180 deps = deps, 181 **kwargs 182 ): 183 return 184 185 # A perfetto_cc_protozero_library has two types of dependencies: 186 # 1. Exactly one dependency on a proto_library target. This defines the 187 # .proto sources for the target 188 # 2. Zero or more deps on other perfetto_cc_protozero_library targets. This 189 # to deal with the case of foo.proto including common.proto from another 190 # target. 191 _proto_deps = [d for d in deps if d.endswith("_protos")] 192 _cc_deps = [d for d in deps if d not in _proto_deps] 193 if len(_proto_deps) != 1: 194 fail("Too many proto deps for target %s" % name) 195 196 args = { 197 "name": name + "_src", 198 "deps": _proto_deps, 199 "suffix": "pbzero", 200 "plugin": PERFETTO_CONFIG.root + ":protozero_plugin", 201 "wrapper_namespace": "pbzero", 202 "protoc": PERFETTO_CONFIG.deps.protoc[0], 203 "root": PERFETTO_CONFIG.root, 204 } 205 if not _rule_override("proto_gen", **args): 206 proto_gen(**args) 207 208 perfetto_filegroup( 209 name = name + "_h", 210 srcs = [":" + name + "_src"], 211 output_group = "h", 212 ) 213 214 perfetto_cc_library( 215 name = name, 216 srcs = [":" + name + "_src"], 217 hdrs = [":" + name + "_h"], 218 deps = [PERFETTO_CONFIG.root + ":protozero"] + _cc_deps, 219 **kwargs 220 ) 221 222# Generates .ipc.{cc,h} and .pb.{cc.h} from .proto(s). The IPC sources depend 223# on .pb.h so we need to generate also the standard protobuf sources here. 224def perfetto_cc_ipc_library(name, deps, **kwargs): 225 if _rule_override("cc_ipc_library", name = name, deps = deps, **kwargs): 226 return 227 228 # A perfetto_cc_ipc_library has two types of dependencies: 229 # 1. Exactly one dependency on a proto_library target. This defines the 230 # .proto sources for the target 231 # 2. Zero or more deps on other perfetto_cc_protocpp_library targets. This 232 # to deal with the case of foo.proto including common.proto from another 233 # target. 234 _proto_deps = [d for d in deps if d.endswith("_protos")] 235 _cc_deps = [d for d in deps if d not in _proto_deps] 236 if len(_proto_deps) != 1: 237 fail("Too many proto deps for target %s" % name) 238 239 # Generates .ipc.{cc,h}. 240 args = { 241 "name": name + "_src", 242 "deps": _proto_deps, 243 "suffix": "ipc", 244 "plugin": PERFETTO_CONFIG.root + ":ipc_plugin", 245 "wrapper_namespace": "gen", 246 "protoc": PERFETTO_CONFIG.deps.protoc[0], 247 "root": PERFETTO_CONFIG.root, 248 } 249 if not _rule_override("proto_gen", **args): 250 proto_gen(**args) 251 252 perfetto_filegroup( 253 name = name + "_h", 254 srcs = [":" + name + "_src"], 255 output_group = "h", 256 ) 257 258 perfetto_cc_library( 259 name = name, 260 srcs = [":" + name + "_src"], 261 hdrs = [":" + name + "_h"], 262 deps = [ 263 # Generated .ipc.{cc,h} depend on this and protozero. 264 PERFETTO_CONFIG.root + ":perfetto_ipc", 265 PERFETTO_CONFIG.root + ":protozero", 266 ] + _cc_deps, 267 **kwargs 268 ) 269 270# Generates .gen.{cc,h} from .proto(s). 271def perfetto_cc_protocpp_library(name, deps, **kwargs): 272 if _rule_override( 273 "cc_protocpp_library", 274 name = name, 275 deps = deps, 276 **kwargs 277 ): 278 return 279 280 # A perfetto_cc_protocpp_library has two types of dependencies: 281 # 1. Exactly one dependency on a proto_library target. This defines the 282 # .proto sources for the target 283 # 2. Zero or more deps on other perfetto_cc_protocpp_library targets. This 284 # to deal with the case of foo.proto including common.proto from another 285 # target. 286 _proto_deps = [d for d in deps if d.endswith("_protos")] 287 _cc_deps = [d for d in deps if d not in _proto_deps] 288 if len(_proto_deps) != 1: 289 fail("Too many proto deps for target %s" % name) 290 291 args = { 292 "name": name + "_gen", 293 "deps": _proto_deps, 294 "suffix": "gen", 295 "plugin": PERFETTO_CONFIG.root + ":cppgen_plugin", 296 "wrapper_namespace": "gen", 297 "protoc": PERFETTO_CONFIG.deps.protoc[0], 298 "root": PERFETTO_CONFIG.root, 299 } 300 if not _rule_override("proto_gen", **args): 301 proto_gen(**args) 302 303 perfetto_filegroup( 304 name = name + "_gen_h", 305 srcs = [":" + name + "_gen"], 306 output_group = "h", 307 ) 308 309 # The headers from the gen plugin have implicit dependencies 310 # on each other so will fail when compiled independently. Use 311 # textual_hdrs to indicate this to Bazel. 312 perfetto_cc_library( 313 name = name, 314 srcs = [":" + name + "_gen"], 315 textual_hdrs = [":" + name + "_gen_h"], 316 deps = [ 317 PERFETTO_CONFIG.root + ":protozero", 318 ] + _cc_deps, 319 **kwargs 320 ) 321 322def perfetto_proto_descriptor(name, deps, outs, **kwargs): 323 args = { 324 "name": name, 325 "deps": deps, 326 "outs": outs, 327 } 328 if not _rule_override("proto_descriptor_gen", **args): 329 proto_descriptor_gen(**args) 330 331# Generator .descriptor.h from protos 332def perfetto_cc_proto_descriptor(name, deps, outs, **kwargs): 333 cmd = [ 334 "$(location gen_cc_proto_descriptor_py)", 335 "--cpp_out=$@", 336 "--gen_dir=$(GENDIR)", 337 "$<", 338 ] 339 perfetto_genrule( 340 name = name + "_gen", 341 cmd = " ".join(cmd), 342 tools = [ 343 ":gen_cc_proto_descriptor_py", 344 ], 345 srcs = deps, 346 outs = outs, 347 ) 348 349 perfetto_cc_library( 350 name = name, 351 hdrs = [":" + name + "_gen"], 352 **kwargs 353 ) 354 355def perfetto_cc_amalgamated_sql(name, deps, outs, namespace, **kwargs): 356 if PERFETTO_CONFIG.root[:2] != "//": 357 fail("Expected PERFETTO_CONFIG.root to start with //") 358 359 genrule_tool = kwargs.pop("genrule_tool", ":gen_amalgamated_sql_py") 360 cmd = [ 361 "$(location " + genrule_tool + ")", 362 "--namespace", 363 namespace, 364 "--cpp-out=$@", 365 "$(SRCS)", 366 ] 367 368 root_dir = kwargs.pop("root_dir", None) 369 if root_dir: 370 cmd += [ 371 "--root-dir", 372 root_dir, 373 ] 374 375 perfetto_genrule( 376 name = name + "_gen", 377 cmd = " ".join(cmd), 378 tools = [ 379 genrule_tool, 380 ], 381 srcs = deps, 382 outs = outs, 383 ) 384 perfetto_cc_library( 385 name = name, 386 hdrs = [":" + name + "_gen"], 387 **kwargs 388 ) 389 390def perfetto_cc_tp_tables(name, srcs, outs, deps = [], **kwargs): 391 if PERFETTO_CONFIG.root[:2] != "//": 392 fail("Expected PERFETTO_CONFIG.root to start with //") 393 394 if PERFETTO_CONFIG.root == "//": 395 python_path = PERFETTO_CONFIG.root + "python" 396 else: 397 python_path = PERFETTO_CONFIG.root + "/python" 398 399 perfetto_py_library( 400 name = name + "_lib", 401 deps = [ 402 python_path + ":trace_processor_table_generator", 403 ], 404 srcs = srcs, 405 ) 406 407 perfetto_py_binary( 408 name = name + "_tool", 409 deps = [ 410 ":" + name + "_lib", 411 python_path + ":trace_processor_table_generator", 412 ] + [d + "_lib" for d in deps], 413 srcs = [ 414 "tools/gen_tp_table_headers.py", 415 ], 416 main = "tools/gen_tp_table_headers.py", 417 python_version = "PY3", 418 ) 419 420 cmd = ["$(location " + name + "_tool)"] 421 cmd += ["--gen-dir", "$(RULEDIR)"] 422 cmd += ["--inputs", "$(SRCS)"] 423 if PERFETTO_CONFIG.root != "//": 424 cmd += ["--import-prefix", PERFETTO_CONFIG.root[2:]] 425 cmd += ["--relative-input-dir", PERFETTO_CONFIG.root[2:]] 426 427 perfetto_genrule( 428 name = name + "_gen", 429 cmd = " ".join(cmd), 430 tools = [ 431 ":" + name + "_tool", 432 ], 433 srcs = srcs, 434 outs = outs, 435 ) 436 437 perfetto_filegroup( 438 name = name, 439 srcs = [":" + name + "_gen"], 440 **kwargs 441 ) 442 443# +----------------------------------------------------------------------------+ 444# | Misc utility functions | 445# +----------------------------------------------------------------------------+ 446 447def _rule_override(rule_name, **kwargs): 448 overrides = getattr(PERFETTO_CONFIG, "rule_overrides", struct()) 449 overridden_rule = getattr(overrides, rule_name, None) 450 if overridden_rule: 451 overridden_rule(**kwargs) 452 return True 453 return False 454 455def _merge_dicts(*args): 456 res = {} 457 for arg in args: 458 for k, v in arg.items(): 459 if type(v) == "string" or type(v) == "bool": 460 res[k] = v 461 elif type(v) == "list" or type(v) == "select": 462 res[k] = res.get(k, []) + v 463 else: 464 fail("key type not supported: " + type(v)) 465 return res 466