1#!/usr/bin/env python3 2# Copyright (C) 2018 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16# This tool translates a collection of BUILD.gn files into a mostly equivalent 17# BUILD file for the Bazel build system. The input to the tool is a 18# JSON description of the GN build definition generated with the following 19# command: 20# 21# gn desc out --format=json --all-toolchains "//*" > desc.json 22# 23# The tool is then given a list of GN labels for which to generate Bazel 24# build rules. 25 26from __future__ import print_function 27import argparse 28import json 29import os 30import re 31import sys 32from typing import Any 33from typing import Dict 34from typing import List 35from typing import Optional 36from typing import Union 37 38from gn_utils import GnParser 39import gn_utils 40 41from compat import itervalues, iteritems, basestring 42 43# Visibility option for targets which we want an allowlist of public targets 44# which can depend on it. 45ALLOWLIST_PUBLIC_VISIBILITY = 'PERFETTO_CONFIG.public_visibility' 46 47# Arguments for the GN output directory. 48# host_os="linux" is to generate the right build files from Mac OS. 49gn_args = ' '.join([ 50 'host_os="linux"', 51 'is_debug=false', 52 'is_perfetto_build_generator=true', 53 'monolithic_binaries=true', 54 'target_os="linux"', 55 'enable_perfetto_heapprofd=false', 56 'enable_perfetto_traced_perf=false', 57 'perfetto_force_dcheck="off"', 58 'enable_perfetto_llvm_demangle=true', 59]) 60 61# Default targets to translate to the blueprint file. 62 63# These targets will be exported with public visibility in the generated BUILD. 64public_targets = [ 65 '//:libperfetto_client_experimental', 66 '//src/perfetto_cmd:perfetto', 67 '//src/traced/probes:traced_probes', 68 '//src/traced/service:traced', 69 '//src/trace_processor:trace_processor_shell', 70 '//src/trace_processor:trace_processor', 71 '//src/traceconv:traceconv', 72 '//src/traceconv:libpprofbuilder', 73] 74 75# These targets are required by internal build rules but don't need to be 76# exported publicly. 77default_targets = [ 78 '//src/base:perfetto_base_default_platform', 79 '//src/cloud_trace_processor:cloud_trace_processor', 80 '//src/ipc:perfetto_ipc', 81 '//src/ipc/protoc_plugin:ipc_plugin', 82 '//src/protozero:protozero', 83 '//src/protozero/protoc_plugin:cppgen_plugin', 84 '//src/protozero/protoc_plugin:protozero_plugin', 85 '//src/tools/proto_filter:proto_filter', 86 '//src/tools/proto_merger:proto_merger', 87 '//test:client_api_example', 88] + public_targets 89 90# Proto targets are required by internal build rules but don't need to be 91# exported publicly. 92proto_default_targets = [ 93 '//protos/perfetto/cloud_trace_processor:lite' 94] 95 96# Proto target groups which will be made public. 97proto_groups = { 98 'config': { 99 'sources': ['//protos/perfetto/config:source_set'], 100 'visibility': ['//visibility:public'], 101 }, 102 'trace': { 103 'sources': [ 104 '//protos/perfetto/trace:non_minimal_source_set', 105 '//protos/perfetto/trace:minimal_source_set' 106 ], 107 'visibility': ALLOWLIST_PUBLIC_VISIBILITY, 108 }, 109 'metrics': { 110 'sources': ['//protos/perfetto/metrics:source_set',], 111 'visibility': ['//visibility:public'], 112 }, 113 'chromium': { 114 'sources': ['//protos/third_party/chromium:source_set',], 115 'visibility': ALLOWLIST_PUBLIC_VISIBILITY, 116 }, 117 'chrome_metrics': { 118 'sources': ['//protos/perfetto/metrics/chrome:source_set',], 119 'visibility': ALLOWLIST_PUBLIC_VISIBILITY, 120 }, 121} 122 123# Path for the protobuf sources in the standalone build. 124buildtools_protobuf_src = '//buildtools/protobuf/src' 125 126# The directory where the generated perfetto_build_flags.h will be copied into. 127buildflags_dir = 'include/perfetto/base/build_configs/bazel' 128 129# Internal equivalents for third-party libraries that the upstream project 130# depends on. 131external_deps = { 132 '//gn:default_deps': [], 133 '//gn:base_platform': ['PERFETTO_CONFIG.deps.base_platform'], 134 '//gn:jsoncpp': ['PERFETTO_CONFIG.deps.jsoncpp'], 135 '//gn:linenoise': ['PERFETTO_CONFIG.deps.linenoise'], 136 '//gn:protobuf_full': ['PERFETTO_CONFIG.deps.protobuf_full'], 137 '//gn:protobuf_lite': ['PERFETTO_CONFIG.deps.protobuf_lite'], 138 '//gn:protoc_lib': ['PERFETTO_CONFIG.deps.protoc_lib'], 139 '//gn:protoc': ['PERFETTO_CONFIG.deps.protoc'], 140 '//gn:sqlite': [ 141 'PERFETTO_CONFIG.deps.sqlite', 142 'PERFETTO_CONFIG.deps.sqlite_ext_percentile' 143 ], 144 '//gn:zlib': ['PERFETTO_CONFIG.deps.zlib'], 145 '//gn:llvm_demangle': ['PERFETTO_CONFIG.deps.llvm_demangle'], 146 '//src/trace_processor:demangle': ['PERFETTO_CONFIG.deps.demangle_wrapper'], 147 gn_utils.GEN_VERSION_TARGET: ['PERFETTO_CONFIG.deps.version_header'], 148} 149 150# These are Python targets which are exposed with public visibility. 151public_python_targets = [ 152 '//python:batch_trace_processor', 153 '//python:trace_processor_py', 154] 155 156# These are Python targets which are exposed by default. 157default_python_targets = [ 158 '//python:batch_trace_processor', 159 '//python:experimental_slice_breakdown_bin', 160 '//python:trace_processor_table_generator', 161 '//python:trace_processor_py_example', 162] 163 164# Internal equivalents for third-party Python libraries. 165external_python_deps: Dict[str, List[str]] = { 166 '//gn:pandas_py': ['PERFETTO_CONFIG.deps.pandas_py'], 167 '//gn:protobuf_py': ['PERFETTO_CONFIG.deps.protobuf_py'], 168 '//gn:tp_vendor_py': ['PERFETTO_CONFIG.deps.tp_vendor_py'], 169} 170 171 172def gen_version_header(target): 173 label = BazelLabel(get_bazel_label_name(target.name), 'perfetto_genrule') 174 label.srcs += [gn_utils.label_to_path(x) for x in sorted(target.inputs)] 175 label.outs += target.outputs 176 label.cmd = r'$(location gen_version_header_py)' 177 label.cmd += r' --cpp_out=$@ --changelog=$(location CHANGELOG)' 178 label.tools += [':gen_version_header_py'] 179 return [label] 180 181 182custom_actions = { 183 gn_utils.GEN_VERSION_TARGET: gen_version_header, 184} 185 186# ------------------------------------------------------------------------------ 187# End of configuration. 188# ------------------------------------------------------------------------------ 189 190 191class PythonBuildGenerator: 192 '''Generator of the BUILD file in the python folder. 193 194 This code is split into its own class to avoid polluting 195 the generation of the main build file with Python related 196 content. 197 ''' 198 199 def populate_python_deps(self, target: GnParser.Target, label: 'BazelLabel'): 200 '''Populates deps for a GN target into Bazel Python label.''' 201 for dep in sorted(target.non_proto_or_source_set_deps()): 202 if dep.name in external_python_deps: 203 assert (isinstance(external_python_deps[dep.name], list)) 204 label.external_deps += external_python_deps[dep.name] 205 else: 206 label.deps += [':' + get_bazel_python_label_name(dep.name)] 207 208 def python_label_to_path(self, gn_name: str): 209 """Converts a Python GN path label into a Bazel path.""" 210 return re.sub(r'^python/', '', gn_utils.label_to_path(gn_name)) 211 212 def python_data_to_path(self, gn_name: str): 213 """Converts a Python GN data label into a Bazel data label.""" 214 return re.sub(r'^\.\.(.*)', r'PERFETTO_CONFIG.root + "\1"', gn_name) 215 216 def gen_python_library(self, target: GnParser.Target): 217 """Creates a Bazel target for a Python library GN target.""" 218 label = BazelLabel( 219 get_bazel_python_label_name(target.name), 'perfetto_py_library') 220 label.comment = target.name 221 label.srcs += (self.python_label_to_path(x) for x in target.sources) 222 label.data += (self.python_data_to_path(x) for x in target.data) 223 self.populate_python_deps(target, label) 224 if target.name in public_python_targets: 225 label.visibility = ['//visibility:public'] 226 return [label] 227 228 def gen_python_binary(self, target: GnParser.Target): 229 """Creates a Bazel target for a Python binary GN target.""" 230 label = BazelLabel( 231 get_bazel_python_label_name(target.name), 'perfetto_py_binary') 232 label.comment = target.name 233 label.srcs += (self.python_label_to_path(x) for x in target.sources) 234 label.data += (self.python_data_to_path(x) for x in target.data) 235 label.main = target.python_main 236 label.python_version = 'PY3' 237 if target.name in public_python_targets: 238 label.visibility = ['//visibility:public'] 239 240 self.populate_python_deps(target, label) 241 return [label] 242 243 def gen_target(self, gn_target: GnParser.Target): 244 """Creates a Bazel target for a Python GN target.""" 245 assert (gn_target.type == 'action') 246 if gn_target.name in external_python_deps: 247 return [] 248 if gn_target.custom_action_type == 'python_library': 249 return self.gen_python_library(gn_target) 250 if gn_target.custom_action_type == 'python_binary': 251 return self.gen_python_binary(gn_target) 252 assert (False) 253 254 def gen_target_str(self, gn_target: GnParser.Target): 255 """Creates a Bazel target string for a Python GN target.""" 256 return ''.join(str(x) for x in self.gen_target(gn_target)) 257 258 def generate(self, gn_desc): 259 """Creates a Python BUILD file for the GN description.""" 260 gn = gn_utils.GnParser(gn_desc) 261 project_root = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) 262 tool_name = os.path.relpath(os.path.abspath(__file__), project_root) 263 res = ''' 264# Copyright (C) 2022 The Android Open Source Project 265# 266# Licensed under the Apache License, Version 2.0 (the "License"); 267# you may not use this file except in compliance with the License. 268# You may obtain a copy of the License at 269# 270# http://www.apache.org/licenses/LICENSE-2.0 271# 272# Unless required by applicable law or agreed to in writing, software 273# distributed under the License is distributed on an "AS IS" BASIS, 274# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 275# See the License for the specific language governing permissions and 276# limitations under the License. 277# 278# This file is automatically generated by {}. Do not edit. 279 280load("@perfetto_cfg//:perfetto_cfg.bzl", "PERFETTO_CONFIG") 281load( 282 "@perfetto//bazel:rules.bzl", 283 "perfetto_py_binary", 284 "perfetto_py_library", 285) 286 287licenses(["notice"]) 288 289package(default_visibility = [PERFETTO_CONFIG.root + ":__subpackages__"]) 290 291'''.format(tool_name).lstrip() 292 293 # Find all the targets in the //python folder. 294 for target_name in default_python_targets: 295 target = gn.get_target(target_name) 296 res += self.gen_target_str(target) 297 298 # Generate all the intermediate targets. 299 for target in sorted(itervalues(gn.all_targets)): 300 if target.name in default_python_targets: 301 continue 302 res += self.gen_target_str(target) 303 304 return res 305 306 307class Error(Exception): 308 pass 309 310 311class BazelLabel(object): 312 313 def __init__(self, name, type): 314 self.comment: Optional[str] = None 315 self.name = name 316 self.type = type 317 self.visibility: Union[List[str], str] = [] 318 self.srcs = [] 319 self.hdrs = [] 320 self.data = [] 321 self.deps = [] 322 self.external_deps = [] 323 self.tools = [] 324 self.outs = [] 325 self.exports = [] 326 self.main = None 327 self.cmd: Optional[str] = None 328 self.python_version: Optional[str] = None 329 self.root_dir: Optional[str] = None 330 self.namespace: Optional[str] = None 331 332 def __lt__(self, other): 333 if isinstance(other, self.__class__): 334 return self.name < other.name 335 raise TypeError( 336 '\'<\' not supported between instances of \'%s\' and \'%s\'' % 337 (type(self).__name__, type(other).__name__)) 338 339 def __str__(self): 340 """Converts the object into a Bazel Starlark label.""" 341 res = '' 342 res += ('# GN target: %s\n' % self.comment) if self.comment else '' 343 res += '%s(\n' % self.type 344 any_deps = len(self.deps) + len(self.external_deps) > 0 345 ORD = [ 346 'name', 'srcs', 'hdrs', 'visibility', 'data', 'deps', 'outs', 'cmd', 347 'tools', 'exports', 'main', 'python_version' 348 ] 349 hasher = lambda x: sum((99,) + tuple(ord(c) for c in x)) 350 key_sorter = lambda kv: ORD.index(kv[0]) if kv[0] in ORD else hasher(kv[0]) 351 for k, v in sorted(iteritems(self.__dict__), key=key_sorter): 352 if k in ('type', 'comment', 353 'external_deps') or v is None or (v == [] and 354 (k != 'deps' or not any_deps)): 355 continue 356 res += ' %s = ' % k 357 if isinstance(v, basestring): 358 if v.startswith('PERFETTO_CONFIG.'): 359 res += '%s,\n' % v 360 else: 361 res += '"%s",\n' % v 362 elif isinstance(v, bool): 363 res += '%s,\n' % v 364 elif isinstance(v, list): 365 res += '[\n' 366 if k == 'deps' and len(self.external_deps) > 1: 367 indent = ' ' 368 else: 369 indent = ' ' 370 for entry in sorted(v): 371 if entry.startswith('PERFETTO_CONFIG.'): 372 res += '%s %s,\n' % (indent, entry) 373 else: 374 res += '%s "%s",\n' % (indent, entry) 375 res += '%s]' % indent 376 if k == 'deps' and self.external_deps: 377 res += ' + %s' % self.external_deps[0] 378 for edep in self.external_deps[1:]: 379 if isinstance(edep, list): 380 res += ' + [\n' 381 for inner_dep in edep: 382 res += ' "%s",\n' % inner_dep 383 res += ' ]' 384 else: 385 res += ' +\n%s%s' % (indent, edep) 386 res += ',\n' 387 else: 388 raise Error('Unsupported value %s', type(v)) 389 res += ')\n\n' 390 return res 391 392 393def get_bazel_label_name(gn_name: str): 394 """Converts a GN target name into a Bazel label name. 395 396 If target is in the public target list, returns only the GN target name, 397 e.g.: //src/ipc:perfetto_ipc -> perfetto_ipc 398 399 Otherwise, in the case of an intermediate target, returns a mangled path. 400 e.g.: //include/perfetto/base:base -> include_perfetto_base_base. 401 """ 402 if gn_name in default_targets: 403 return gn_utils.label_without_toolchain(gn_name).split(':')[1] 404 return gn_utils.label_to_target_name_with_path(gn_name) 405 406 407def get_bazel_python_label_name(gn_name: str): 408 """Converts a Python GN label into a Bazel label.""" 409 name = re.sub(r'^//python:?', '', gn_name) 410 return gn_utils.label_to_target_name_with_path(name) 411 412 413def get_bazel_proto_sources_label(target_name: str): 414 """Converts a GN target name into a Bazel proto label name.""" 415 return re.sub('_(lite|zero|cpp|ipc|source_set|descriptor)$', '', 416 get_bazel_label_name(target_name)) + '_protos' 417 418 419def gen_proto_label(target: GnParser.Target): 420 """ Generates the xx_proto_library label for proto targets.""" 421 assert (target.type == 'proto_library') 422 423 sources_label_name = get_bazel_proto_sources_label(target.name) 424 425 # For 'source_set' plugins, we don't want to generate any plugin-dependent 426 # targets so just return the label of the proto sources only. 427 if target.proto_plugin == 'source_set': 428 sources_label = BazelLabel(sources_label_name, 'perfetto_proto_library') 429 sources_label.comment = target.name 430 assert (all(x.startswith('//') for x in target.sources)) 431 assert (all(x.endswith('.proto') for x in target.sources)) 432 sources_label.srcs = sorted([x[2:] for x in target.sources]) # Strip //. 433 sources_label.deps = sorted([ 434 ':' + get_bazel_proto_sources_label(x.name) 435 for x in target.transitive_proto_deps() 436 ]) 437 438 # In Bazel, proto_paths are not a supported concept becauase strong 439 # dependency checking is enabled. Instead, we need to depend on the target 440 # which includes the proto we want to depend on. 441 # For example, we include the proto_path |buildtools_protobuf_src| because 442 # we want to depend on the "google/protobuf/descriptor.proto" proto file. 443 # This will be exposed by the |protobuf_descriptor_proto| dep. 444 if buildtools_protobuf_src in target.proto_paths: 445 sources_label.external_deps = [ 446 'PERFETTO_CONFIG.deps.protobuf_descriptor_proto' 447 ] 448 449 sources_label.visibility = ['PERFETTO_CONFIG.proto_library_visibility'] 450 451 sources_label.exports = sorted( 452 [':' + get_bazel_proto_sources_label(d) for d in target.proto_exports]) 453 return sources_label 454 455 # For all other types of plugins, we need to generate 456 if target.proto_plugin == 'proto': 457 plugin_label_type = 'perfetto_cc_proto_library' 458 elif target.proto_plugin == 'protozero': 459 plugin_label_type = 'perfetto_cc_protozero_library' 460 elif target.proto_plugin == 'cppgen': 461 plugin_label_type = 'perfetto_cc_protocpp_library' 462 elif target.proto_plugin == 'ipc': 463 plugin_label_type = 'perfetto_cc_ipc_library' 464 elif target.proto_plugin == 'descriptor': 465 plugin_label_type = 'perfetto_proto_descriptor' 466 else: 467 raise Error('Unknown proto plugin: %s' % target.proto_plugin) 468 plugin_label_name = get_bazel_label_name(target.name) 469 plugin_label = BazelLabel(plugin_label_name, plugin_label_type) 470 plugin_label.comment = target.name 471 472 # When using the plugins we need to pass down also the transitive deps. 473 # For instance consider foo.proto including common.proto. The generated 474 # foo.cc will #include "common.gen.h". Hence the generated cc_protocpp_library 475 # rule need to pass down the dependency on the target that generates 476 # common.gen.{cc,h}. 477 if target.proto_plugin in ('cppgen', 'ipc', 'protozero'): 478 plugin_label.deps += [ 479 ':' + get_bazel_label_name(x.name) 480 for x in target.transitive_proto_deps() 481 ] 482 483 # Add any dependencies on source_set targets (i.e. targets containing proto 484 # files). For descriptors, we will have an explicit edge between the 485 # descriptor and source set wheras for other plugin types, this edge is 486 # implicit. 487 if target.proto_plugin == 'descriptor': 488 plugin_label.deps += [ 489 ':' + get_bazel_proto_sources_label(x.name) 490 for x in target.proto_deps() 491 ] 492 else: 493 plugin_label.deps += [':' + sources_label_name] 494 495 # Since the descriptor generates an explicit output file which can be 496 # referenced by other targets, we specify a name for it. 497 if target.proto_plugin == 'descriptor': 498 plugin_label.outs = [plugin_label_name + '.bin'] 499 500 return plugin_label 501 502 503def gen_proto_group_target(gn: GnParser, name: str, desc: Dict[str, Any]): 504 # Get a recursive list of the proto_library targets rooted here which 505 # have src. 506 deps_set = set(desc['sources']) 507 for target_name in desc['sources']: 508 target = gn.get_target(target_name) 509 deps_set.update(d.name for d in target.transitive_proto_deps()) 510 511 # First, create a root source set target which references all the child 512 # source set targets. We publish this as well as depending on this in all 513 # subsequent targets. 514 sources_label = BazelLabel(name + '_proto', 'perfetto_proto_library') 515 sources_label.deps = [ 516 ':' + get_bazel_proto_sources_label(name) 517 for name in sorted(list(deps_set)) 518 ] 519 sources_label.visibility = desc['visibility'] 520 sources_label.comment = f'''[{', '.join(desc['sources'])}]''' 521 522 cc_label = BazelLabel(name + '_cc_proto', 'perfetto_cc_proto_library') 523 cc_label.deps = [':' + sources_label.name] 524 cc_label.visibility = desc['visibility'] 525 cc_label.comment = sources_label.comment 526 527 java_label = BazelLabel(name + '_java_proto', 'perfetto_java_proto_library') 528 java_label.deps = [':' + sources_label.name] 529 java_label.visibility = desc['visibility'] 530 java_label.comment = sources_label.comment 531 532 lite_name = name + '_java_proto_lite' 533 java_lite_label = BazelLabel(lite_name, 'perfetto_java_lite_proto_library') 534 java_lite_label.deps = [':' + sources_label.name] 535 java_lite_label.visibility = desc['visibility'] 536 java_lite_label.comment = sources_label.comment 537 538 py_label = BazelLabel(name + '_py_pb2', 'perfetto_py_proto_library') 539 py_label.deps = [':' + sources_label.name] 540 py_label.visibility = desc['visibility'] 541 py_label.comment = sources_label.comment 542 543 return [sources_label, cc_label, java_label, java_lite_label, py_label] 544 545 546def gen_cc_proto_descriptor(target: GnParser.Target): 547 label = BazelLabel( 548 get_bazel_label_name(target.name), 'perfetto_cc_proto_descriptor') 549 label.comment = target.name 550 label.deps += [ 551 ':' + get_bazel_label_name(x.name) for x in target.proto_deps() 552 ] 553 label.deps += [ 554 gn_utils.label_to_path(src) 555 for src in target.inputs 556 if "tmp.gn_utils" not in src 557 ] 558 559 label.outs += target.outputs 560 return [label] 561 562 563def gen_cc_amalgamated_sql(target: GnParser.Target): 564 label = BazelLabel( 565 get_bazel_label_name(target.name), 'perfetto_cc_amalgamated_sql') 566 567 def find_arg(name): 568 for i, arg in enumerate(target.args): 569 if arg.startswith(f'--{name}'): 570 return target.args[i + 1] 571 572 label.comment = target.name 573 label.namespace = find_arg('namespace') 574 575 label.deps += sorted( 576 ':' + get_bazel_label_name(x.name) for x in target.transitive_deps) 577 label.outs += target.outputs 578 return [label] 579 580 581def gen_sql_source_set(target: GnParser.Target): 582 label = BazelLabel(get_bazel_label_name(target.name), 'perfetto_filegroup') 583 label.comment = target.name 584 label.srcs += (gn_utils.label_to_path(x) for x in target.inputs) 585 return [label] 586 587 588def gen_cc_tp_tables(target: GnParser.Target): 589 label = BazelLabel(get_bazel_label_name(target.name), 'perfetto_cc_tp_tables') 590 label.comment = target.name 591 label.srcs += (gn_utils.label_to_path(x) for x in target.sources) 592 label.deps += sorted(':' + get_bazel_label_name(x.name) 593 for x in target.transitive_deps 594 if x.name not in default_python_targets) 595 label.outs += target.outputs 596 return [label] 597 598 599def gen_target(gn_target: GnParser.Target): 600 if gn_target.type == 'proto_library': 601 return [gen_proto_label(gn_target)] 602 elif gn_target.type == 'action': 603 if gn_target.name in custom_actions: 604 return custom_actions[gn_target.name](gn_target) 605 if gn_target.custom_action_type == 'sql_amalgamation': 606 return gen_cc_amalgamated_sql(gn_target) 607 if gn_target.custom_action_type == 'sql_source_set': 608 return gen_sql_source_set(gn_target) 609 if gn_target.custom_action_type == 'cc_proto_descriptor': 610 return gen_cc_proto_descriptor(gn_target) 611 if gn_target.custom_action_type == 'tp_tables': 612 return gen_cc_tp_tables(gn_target) 613 return [] 614 elif gn_target.type == 'group': 615 return [] 616 elif gn_target.type == 'executable': 617 bazel_type = 'perfetto_cc_binary' 618 elif gn_target.type == 'shared_library': 619 bazel_type = 'perfetto_cc_binary' 620 elif gn_target.type == 'static_library': 621 bazel_type = 'perfetto_cc_library' 622 elif gn_target.type == 'source_set': 623 bazel_type = 'perfetto_filegroup' 624 elif gn_target.type == 'generated_file': 625 return [] 626 else: 627 raise Error('target type not supported: %s' % gn_target.type) 628 629 label = BazelLabel(get_bazel_label_name(gn_target.name), bazel_type) 630 label.comment = gn_target.name 631 632 # Supporting 'public' on source_sets would require not converting them to 633 # filegroups in bazel. 634 if gn_target.public_headers: 635 if bazel_type == 'perfetto_cc_library': 636 label.hdrs += [x[2:] for x in gn_target.public_headers] 637 else: 638 raise Error('%s: \'public\' currently supported only for cc_library' % 639 gn_target.name) 640 641 raw_srcs = [x[2:] for x in gn_target.sources] 642 raw_srcs += [x[2:] for x in gn_target.inputs] 643 if bazel_type == 'perfetto_cc_library': 644 label.srcs += [x for x in raw_srcs if not x.startswith('include')] 645 label.hdrs += [x for x in raw_srcs if x.startswith('include')] 646 647 # Most Perfetto libraries cannot by dynamically linked as they would 648 # cause ODR violations. 649 label.__dict__['linkstatic'] = True 650 else: 651 label.srcs = raw_srcs 652 653 if gn_target.name in public_targets: 654 label.visibility = ['//visibility:public'] 655 656 if gn_target.type in gn_utils.LINKER_UNIT_TYPES: 657 # |source_sets| contains the transitive set of source_set deps. 658 for trans_dep in gn_target.transitive_source_set_deps(): 659 name = ':' + get_bazel_label_name(trans_dep.name) 660 if name.startswith( 661 ':include_perfetto_') and gn_target.type != 'executable': 662 label.hdrs += [name] 663 else: 664 label.srcs += [name] 665 for dep in sorted(gn_target.non_proto_or_source_set_deps()): 666 dep_name = dep.name 667 if dep_name.startswith('//gn:'): 668 assert (dep_name in external_deps), dep 669 670 # tp_tables produces a filegroup not a cc_lbirary so should end up srcs 671 # not deps. 672 if dep.custom_action_type == 'tp_tables': 673 label.srcs += [':' + get_bazel_label_name(dep_name)] 674 elif dep_name in external_deps: 675 assert (isinstance(external_deps[dep_name], list)) 676 label.external_deps += external_deps[dep_name] 677 else: 678 label.deps += [':' + get_bazel_label_name(dep_name)] 679 label.deps += [ 680 ':' + get_bazel_label_name(x.name) 681 for x in gn_target.transitive_cpp_proto_deps() 682 ] 683 684 # All items starting with : need to be sorted to the end of the list. 685 # However, Python makes specifying a comparator function hard so cheat 686 # instead and make everything start with : sort as if it started with | 687 # As | > all other normal ASCII characters, this will lead to all : targets 688 # starting with : to be sorted to the end. 689 label.srcs = sorted(label.srcs, key=lambda x: x.replace(':', '|')) 690 691 label.deps = sorted(label.deps) 692 label.hdrs = sorted(label.hdrs) 693 return [label] 694 695 696def gen_target_str(gn_target): 697 return ''.join(str(x) for x in gen_target(gn_target)) 698 699 700def generate_build(gn_desc, targets, extras): 701 gn = gn_utils.GnParser(gn_desc) 702 project_root = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) 703 tool_name = os.path.relpath(os.path.abspath(__file__), project_root) 704 res = ''' 705# Copyright (C) 2019 The Android Open Source Project 706# 707# Licensed under the Apache License, Version 2.0 (the "License"); 708# you may not use this file except in compliance with the License. 709# You may obtain a copy of the License at 710# 711# http://www.apache.org/licenses/LICENSE-2.0 712# 713# Unless required by applicable law or agreed to in writing, software 714# distributed under the License is distributed on an "AS IS" BASIS, 715# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 716# See the License for the specific language governing permissions and 717# limitations under the License. 718# 719# This file is automatically generated by {}. Do not edit. 720 721load("@perfetto_cfg//:perfetto_cfg.bzl", "PERFETTO_CONFIG") 722load( 723 "@perfetto//bazel:rules.bzl", 724 "perfetto_build_config_cc_library", 725 "perfetto_cc_amalgamated_sql", 726 "perfetto_cc_binary", 727 "perfetto_cc_ipc_library", 728 "perfetto_cc_library", 729 "perfetto_cc_proto_descriptor", 730 "perfetto_cc_proto_library", 731 "perfetto_cc_protocpp_library", 732 "perfetto_cc_protozero_library", 733 "perfetto_cc_tp_tables", 734 "perfetto_filegroup", 735 "perfetto_genrule", 736 "perfetto_go_proto_library", 737 "perfetto_java_lite_proto_library", 738 "perfetto_java_proto_library", 739 "perfetto_proto_descriptor", 740 "perfetto_proto_library", 741 "perfetto_py_binary", 742 "perfetto_py_library", 743 "perfetto_py_proto_library", 744) 745 746package(default_visibility = [PERFETTO_CONFIG.root + ":__subpackages__"]) 747 748licenses(["notice"]) 749 750exports_files(["NOTICE"]) 751 752'''.format(tool_name).lstrip() 753 754 # Public targets need to be computed at the beginning (to figure out the 755 # intermediate deps) but printed at the end (because declaration order matters 756 # in Bazel). 757 public_str = '' 758 for target_name in sorted(public_targets): 759 target = gn.get_target(target_name) 760 public_str += gen_target_str(target) 761 762 res += ''' 763# ############################################################################## 764# Internal targets 765# ############################################################################## 766 767'''.lstrip() 768 # Generate the other non-public targets. 769 for target_name in sorted(set(default_targets) - set(public_targets)): 770 target = gn.get_target(target_name) 771 res += gen_target_str(target) 772 773 # Generate all the intermediate targets. 774 for target in sorted(itervalues(gn.all_targets)): 775 if target.name in default_targets or target.name in gn.proto_libs: 776 continue 777 res += gen_target_str(target) 778 779 res += ''' 780# ############################################################################## 781# Proto libraries 782# ############################################################################## 783 784'''.lstrip() 785 # Generate targets for proto groups. 786 for l_name, t_desc in proto_groups.items(): 787 res += ''.join(str(x) for x in gen_proto_group_target(gn, l_name, t_desc)) 788 789 # For any non-source set and non-descriptor targets, ensure the source set 790 # associated to that target is discovered. 791 for target in sorted(itervalues(gn.all_targets)): 792 plugin = target.proto_plugin 793 if plugin is None or plugin == 'source_set' or plugin == 'descriptor': 794 continue 795 gn.get_target(re.sub('(lite|zero|cpp|ipc)$', 'source_set', target.name)) 796 797 # Discover all the default proto targets so it will be generated next. 798 for target in sorted(proto_default_targets): 799 gn.get_target(target) 800 801 # Generate targets for the transitive set of proto targets. 802 labels = [ 803 l for target in sorted(itervalues(gn.proto_libs)) 804 for l in gen_target(target) 805 ] 806 res += ''.join(str(x) for x in sorted(labels)) 807 808 res += ''' 809# ############################################################################## 810# Public targets 811# ############################################################################## 812 813'''.lstrip() 814 res += public_str 815 res += '# Content from BUILD.extras\n\n' 816 res += extras 817 818 # Check for ODR violations 819 for target_name in default_targets: 820 checker = gn_utils.ODRChecker(gn, target_name) 821 822 return res 823 824 825def main(): 826 parser = argparse.ArgumentParser( 827 description='Generate BUILD from a GN description.') 828 parser.add_argument( 829 '--check-only', 830 help='Don\'t keep the generated files', 831 action='store_true') 832 parser.add_argument( 833 '--desc', 834 help='GN description ' + 835 '(e.g., gn desc out --format=json --all-toolchains "//*"') 836 parser.add_argument( 837 '--repo-root', 838 help='Standalone Perfetto repository to generate a GN description', 839 default=gn_utils.repo_root(), 840 ) 841 parser.add_argument( 842 '--extras', 843 help='Extra targets to include at the end of the BUILD file', 844 default=os.path.join(gn_utils.repo_root(), 'BUILD.extras'), 845 ) 846 parser.add_argument( 847 '--output', 848 help='BUILD file to create', 849 default=os.path.join(gn_utils.repo_root(), 'BUILD'), 850 ) 851 parser.add_argument( 852 '--output-python', 853 help='Python BUILD file to create', 854 default=os.path.join(gn_utils.repo_root(), 'python', 'BUILD'), 855 ) 856 parser.add_argument( 857 'targets', 858 nargs=argparse.REMAINDER, 859 help='Targets to include in the BUILD file (e.g., "//:perfetto_tests")') 860 args = parser.parse_args() 861 862 if args.desc: 863 with open(args.desc) as f: 864 desc = json.load(f) 865 else: 866 desc = gn_utils.create_build_description(gn_args, args.repo_root) 867 868 out_files = [] 869 870 # Generate the main BUILD file. 871 with open(args.extras, 'r') as extra_f: 872 extras = extra_f.read() 873 874 contents = generate_build(desc, args.targets or default_targets, extras) 875 out_files.append(args.output + '.swp') 876 with open(out_files[-1], 'w') as out_f: 877 out_f.write(contents) 878 879 # Generate the python BUILD file. 880 python_gen = PythonBuildGenerator() 881 python_contents = python_gen.generate(desc) 882 out_files.append(args.output_python + '.swp') 883 with open(out_files[-1], 'w') as out_f: 884 out_f.write(python_contents) 885 886 # Generate the build flags file. 887 out_files.append(os.path.join(buildflags_dir, 'perfetto_build_flags.h.swp')) 888 gn_utils.gen_buildflags(gn_args, out_files[-1]) 889 890 return gn_utils.check_or_commit_generated_files(out_files, args.check_only) 891 892 893if __name__ == '__main__': 894 sys.exit(main()) 895