| #!/usr/bin/env python3 | 
 | # Copyright (C) 2018 The Android Open Source Project | 
 | # | 
 | # Licensed under the Apache License, Version 2.0 (the "License"); | 
 | # you may not use this file except in compliance with the License. | 
 | # You may obtain a copy of the License at | 
 | # | 
 | #      http://www.apache.org/licenses/LICENSE-2.0 | 
 | # | 
 | # Unless required by applicable law or agreed to in writing, software | 
 | # distributed under the License is distributed on an "AS IS" BASIS, | 
 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | # See the License for the specific language governing permissions and | 
 | # limitations under the License. | 
 |  | 
 | # This tool translates a collection of BUILD.gn files into a mostly equivalent | 
 | # BUILD file for the Bazel build system. The input to the tool is a | 
 | # JSON description of the GN build definition generated with the following | 
 | # command: | 
 | # | 
 | #   gn desc out --format=json --all-toolchains "//*" > desc.json | 
 | # | 
 | # The tool is then given a list of GN labels for which to generate Bazel | 
 | # build rules. | 
 |  | 
 | from __future__ import print_function | 
 | import argparse | 
 | import json | 
 | import os | 
 | import re | 
 | import sys | 
 | from typing import Any | 
 | from typing import Dict | 
 | from typing import List | 
 | from typing import Optional | 
 | from typing import Union | 
 |  | 
 | from gn_utils import GnParser | 
 | import gn_utils | 
 |  | 
 | from compat import itervalues, iteritems, basestring | 
 |  | 
 | # Visibility option for targets which we want an allowlist of public targets | 
 | # which can depend on it. | 
 | ALLOWLIST_PUBLIC_VISIBILITY = 'PERFETTO_CONFIG.public_visibility' | 
 |  | 
 | # Arguments for the GN output directory. | 
 | # host_os="linux" is to generate the right build files from Mac OS. | 
 | gn_args = ' '.join([ | 
 |     'host_os="linux"', | 
 |     'is_debug=false', | 
 |     'is_perfetto_build_generator=true', | 
 |     'monolithic_binaries=true', | 
 |     'target_os="linux"', | 
 |     'enable_perfetto_heapprofd=false', | 
 |     'enable_perfetto_traced_perf=false', | 
 |     'perfetto_force_dcheck="off"', | 
 |     'enable_perfetto_llvm_demangle=true', | 
 | ]) | 
 |  | 
 | # Default targets to translate to the blueprint file. | 
 |  | 
 | # These targets will be exported with public visibility in the generated BUILD. | 
 | public_targets = [ | 
 |     '//:libperfetto_client_experimental', | 
 |     '//src/perfetto_cmd:perfetto', | 
 |     '//src/traced/probes:traced_probes', | 
 |     '//src/traced/service:traced', | 
 |     '//src/trace_processor:trace_processor_shell', | 
 |     '//src/trace_processor:trace_processor', | 
 |     '//src/traceconv:traceconv', | 
 |     '//src/traceconv:libpprofbuilder', | 
 | ] | 
 |  | 
 | # These targets are required by internal build rules but don't need to be | 
 | # exported publicly. | 
 | default_targets = [ | 
 |     '//src/base:perfetto_base_default_platform', | 
 |     '//src/cloud_trace_processor:cloud_trace_processor', | 
 |     '//src/ipc:perfetto_ipc', | 
 |     '//src/ipc/protoc_plugin:ipc_plugin', | 
 |     '//src/protozero:protozero', | 
 |     '//src/protozero/protoc_plugin:cppgen_plugin', | 
 |     '//src/protozero/protoc_plugin:protozero_plugin', | 
 |     '//src/tools/proto_filter:proto_filter', | 
 |     '//src/tools/proto_merger:proto_merger', | 
 |     '//test:client_api_example', | 
 | ] + public_targets | 
 |  | 
 | # Proto targets are required by internal build rules but don't need to be | 
 | # exported publicly. | 
 | proto_default_targets = [ | 
 |   '//protos/perfetto/cloud_trace_processor:lite' | 
 | ] | 
 |  | 
 | # Proto target groups which will be made public. | 
 | proto_groups = { | 
 |     'config': { | 
 |         'sources': ['//protos/perfetto/config:source_set'], | 
 |         'visibility': ['//visibility:public'], | 
 |     }, | 
 |     'trace': { | 
 |         'sources': [ | 
 |             '//protos/perfetto/trace:non_minimal_source_set', | 
 |             '//protos/perfetto/trace:minimal_source_set' | 
 |         ], | 
 |         'visibility': ALLOWLIST_PUBLIC_VISIBILITY, | 
 |     }, | 
 |     'metrics': { | 
 |         'sources': ['//protos/perfetto/metrics:source_set',], | 
 |         'visibility': ['//visibility:public'], | 
 |     }, | 
 |     'chromium': { | 
 |         'sources': ['//protos/third_party/chromium:source_set',], | 
 |         'visibility': ALLOWLIST_PUBLIC_VISIBILITY, | 
 |     }, | 
 |     'chrome_metrics': { | 
 |         'sources': ['//protos/perfetto/metrics/chrome:source_set',], | 
 |         'visibility': ALLOWLIST_PUBLIC_VISIBILITY, | 
 |     }, | 
 | } | 
 |  | 
 | # Path for the protobuf sources in the standalone build. | 
 | buildtools_protobuf_src = '//buildtools/protobuf/src' | 
 |  | 
 | # The directory where the generated perfetto_build_flags.h will be copied into. | 
 | buildflags_dir = 'include/perfetto/base/build_configs/bazel' | 
 |  | 
 | # Internal equivalents for third-party libraries that the upstream project | 
 | # depends on. | 
 | external_deps = { | 
 |     '//gn:default_deps': [], | 
 |     '//gn:base_platform': ['PERFETTO_CONFIG.deps.base_platform'], | 
 |     '//gn:jsoncpp': ['PERFETTO_CONFIG.deps.jsoncpp'], | 
 |     '//gn:linenoise': ['PERFETTO_CONFIG.deps.linenoise'], | 
 |     '//gn:protobuf_full': ['PERFETTO_CONFIG.deps.protobuf_full'], | 
 |     '//gn:protobuf_lite': ['PERFETTO_CONFIG.deps.protobuf_lite'], | 
 |     '//gn:protoc_lib': ['PERFETTO_CONFIG.deps.protoc_lib'], | 
 |     '//gn:protoc': ['PERFETTO_CONFIG.deps.protoc'], | 
 |     '//gn:sqlite': [ | 
 |         'PERFETTO_CONFIG.deps.sqlite', | 
 |         'PERFETTO_CONFIG.deps.sqlite_ext_percentile' | 
 |     ], | 
 |     '//gn:zlib': ['PERFETTO_CONFIG.deps.zlib'], | 
 |     '//gn:llvm_demangle': ['PERFETTO_CONFIG.deps.llvm_demangle'], | 
 |     '//src/trace_processor:demangle': ['PERFETTO_CONFIG.deps.demangle_wrapper'], | 
 |     gn_utils.GEN_VERSION_TARGET: ['PERFETTO_CONFIG.deps.version_header'], | 
 | } | 
 |  | 
 | # These are Python targets which are exposed with public visibility. | 
 | public_python_targets = [ | 
 |     '//python:batch_trace_processor', | 
 |     '//python:trace_processor_py', | 
 | ] | 
 |  | 
 | # These are Python targets which are exposed by default. | 
 | default_python_targets = [ | 
 |     '//python:batch_trace_processor', | 
 |     '//python:experimental_slice_breakdown_bin', | 
 |     '//python:trace_processor_table_generator', | 
 |     '//python:trace_processor_py_example', | 
 | ] | 
 |  | 
 | # Internal equivalents for third-party Python libraries. | 
 | external_python_deps: Dict[str, List[str]] = { | 
 |     '//gn:pandas_py': ['PERFETTO_CONFIG.deps.pandas_py'], | 
 |     '//gn:protobuf_py': ['PERFETTO_CONFIG.deps.protobuf_py'], | 
 |     '//gn:tp_vendor_py': ['PERFETTO_CONFIG.deps.tp_vendor_py'], | 
 | } | 
 |  | 
 |  | 
 | def gen_version_header(target): | 
 |   label = BazelLabel(get_bazel_label_name(target.name), 'perfetto_genrule') | 
 |   label.srcs += [gn_utils.label_to_path(x) for x in sorted(target.inputs)] | 
 |   label.outs += target.outputs | 
 |   label.cmd = r'$(location gen_version_header_py)' | 
 |   label.cmd += r' --cpp_out=$@ --changelog=$(location CHANGELOG)' | 
 |   label.tools += [':gen_version_header_py'] | 
 |   return [label] | 
 |  | 
 |  | 
 | custom_actions = { | 
 |     gn_utils.GEN_VERSION_TARGET: gen_version_header, | 
 | } | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 | # End of configuration. | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 |  | 
 | class PythonBuildGenerator: | 
 |   '''Generator of the BUILD file in the python folder. | 
 |  | 
 |   This code is split into its own class to avoid polluting | 
 |   the generation of the main build file with Python related | 
 |   content. | 
 |   ''' | 
 |  | 
 |   def populate_python_deps(self, target: GnParser.Target, label: 'BazelLabel'): | 
 |     '''Populates deps for a GN target into Bazel Python label.''' | 
 |     for dep in sorted(target.non_proto_or_source_set_deps()): | 
 |       if dep.name in external_python_deps: | 
 |         assert (isinstance(external_python_deps[dep.name], list)) | 
 |         label.external_deps += external_python_deps[dep.name] | 
 |       else: | 
 |         label.deps += [':' + get_bazel_python_label_name(dep.name)] | 
 |  | 
 |   def python_label_to_path(self, gn_name: str): | 
 |     """Converts a Python GN path label into a Bazel path.""" | 
 |     return re.sub(r'^python/', '', gn_utils.label_to_path(gn_name)) | 
 |  | 
 |   def python_data_to_path(self, gn_name: str): | 
 |     """Converts a Python GN data label into a Bazel data label.""" | 
 |     return re.sub(r'^\.\.(.*)', r'PERFETTO_CONFIG.root + "\1"', gn_name) | 
 |  | 
 |   def gen_python_library(self, target: GnParser.Target): | 
 |     """Creates a Bazel target for a Python library GN target.""" | 
 |     label = BazelLabel( | 
 |         get_bazel_python_label_name(target.name), 'perfetto_py_library') | 
 |     label.comment = target.name | 
 |     label.srcs += (self.python_label_to_path(x) for x in target.sources) | 
 |     label.data += (self.python_data_to_path(x) for x in target.data) | 
 |     self.populate_python_deps(target, label) | 
 |     if target.name in public_python_targets: | 
 |       label.visibility = ['//visibility:public'] | 
 |     return [label] | 
 |  | 
 |   def gen_python_binary(self, target: GnParser.Target): | 
 |     """Creates a Bazel target for a Python binary GN target.""" | 
 |     label = BazelLabel( | 
 |         get_bazel_python_label_name(target.name), 'perfetto_py_binary') | 
 |     label.comment = target.name | 
 |     label.srcs += (self.python_label_to_path(x) for x in target.sources) | 
 |     label.data += (self.python_data_to_path(x) for x in target.data) | 
 |     label.main = target.python_main | 
 |     label.python_version = 'PY3' | 
 |     if target.name in public_python_targets: | 
 |       label.visibility = ['//visibility:public'] | 
 |  | 
 |     self.populate_python_deps(target, label) | 
 |     return [label] | 
 |  | 
 |   def gen_target(self, gn_target: GnParser.Target): | 
 |     """Creates a Bazel target for a Python GN target.""" | 
 |     assert (gn_target.type == 'action') | 
 |     if gn_target.name in external_python_deps: | 
 |       return [] | 
 |     if gn_target.custom_action_type == 'python_library': | 
 |       return self.gen_python_library(gn_target) | 
 |     if gn_target.custom_action_type == 'python_binary': | 
 |       return self.gen_python_binary(gn_target) | 
 |     assert (False) | 
 |  | 
 |   def gen_target_str(self, gn_target: GnParser.Target): | 
 |     """Creates a Bazel target string for a Python GN target.""" | 
 |     return ''.join(str(x) for x in self.gen_target(gn_target)) | 
 |  | 
 |   def generate(self, gn_desc): | 
 |     """Creates a Python BUILD file for the GN description.""" | 
 |     gn = gn_utils.GnParser(gn_desc) | 
 |     project_root = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) | 
 |     tool_name = os.path.relpath(os.path.abspath(__file__), project_root) | 
 |     res = ''' | 
 | # Copyright (C) 2022 The Android Open Source Project | 
 | # | 
 | # Licensed under the Apache License, Version 2.0 (the "License"); | 
 | # you may not use this file except in compliance with the License. | 
 | # You may obtain a copy of the License at | 
 | # | 
 | #      http://www.apache.org/licenses/LICENSE-2.0 | 
 | # | 
 | # Unless required by applicable law or agreed to in writing, software | 
 | # distributed under the License is distributed on an "AS IS" BASIS, | 
 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | # See the License for the specific language governing permissions and | 
 | # limitations under the License. | 
 | # | 
 | # This file is automatically generated by {}. Do not edit. | 
 |  | 
 | load("@perfetto_cfg//:perfetto_cfg.bzl", "PERFETTO_CONFIG") | 
 | load( | 
 |     "@perfetto//bazel:rules.bzl", | 
 |     "perfetto_py_binary", | 
 |     "perfetto_py_library", | 
 | ) | 
 |  | 
 | licenses(["notice"]) | 
 |  | 
 | package(default_visibility = [PERFETTO_CONFIG.root + ":__subpackages__"]) | 
 |  | 
 | '''.format(tool_name).lstrip() | 
 |  | 
 |     # Find all the targets in the //python folder. | 
 |     for target_name in default_python_targets: | 
 |       target = gn.get_target(target_name) | 
 |       res += self.gen_target_str(target) | 
 |  | 
 |     # Generate all the intermediate targets. | 
 |     for target in sorted(itervalues(gn.all_targets)): | 
 |       if target.name in default_python_targets: | 
 |         continue | 
 |       res += self.gen_target_str(target) | 
 |  | 
 |     return res | 
 |  | 
 |  | 
 | class Error(Exception): | 
 |   pass | 
 |  | 
 |  | 
 | class BazelLabel(object): | 
 |  | 
 |   def __init__(self, name, type): | 
 |     self.comment: Optional[str] = None | 
 |     self.name = name | 
 |     self.type = type | 
 |     self.visibility: Union[List[str], str] = [] | 
 |     self.srcs = [] | 
 |     self.hdrs = [] | 
 |     self.data = [] | 
 |     self.deps = [] | 
 |     self.external_deps = [] | 
 |     self.tools = [] | 
 |     self.outs = [] | 
 |     self.exports = [] | 
 |     self.main = None | 
 |     self.cmd: Optional[str] = None | 
 |     self.python_version: Optional[str] = None | 
 |     self.root_dir: Optional[str] = None | 
 |     self.namespace: Optional[str] = None | 
 |  | 
 |   def __lt__(self, other): | 
 |     if isinstance(other, self.__class__): | 
 |       return self.name < other.name | 
 |     raise TypeError( | 
 |         '\'<\' not supported between instances of \'%s\' and \'%s\'' % | 
 |         (type(self).__name__, type(other).__name__)) | 
 |  | 
 |   def __str__(self): | 
 |     """Converts the object into a Bazel Starlark label.""" | 
 |     res = '' | 
 |     res += ('# GN target: %s\n' % self.comment) if self.comment else '' | 
 |     res += '%s(\n' % self.type | 
 |     any_deps = len(self.deps) + len(self.external_deps) > 0 | 
 |     ORD = [ | 
 |         'name', 'srcs', 'hdrs', 'visibility', 'data', 'deps', 'outs', 'cmd', | 
 |         'tools', 'exports', 'main', 'python_version' | 
 |     ] | 
 |     hasher = lambda x: sum((99,) + tuple(ord(c) for c in x)) | 
 |     key_sorter = lambda kv: ORD.index(kv[0]) if kv[0] in ORD else hasher(kv[0]) | 
 |     for k, v in sorted(iteritems(self.__dict__), key=key_sorter): | 
 |       if k in ('type', 'comment', | 
 |                'external_deps') or v is None or (v == [] and | 
 |                                                  (k != 'deps' or not any_deps)): | 
 |         continue | 
 |       res += '    %s = ' % k | 
 |       if isinstance(v, basestring): | 
 |         if v.startswith('PERFETTO_CONFIG.'): | 
 |           res += '%s,\n' % v | 
 |         else: | 
 |           res += '"%s",\n' % v | 
 |       elif isinstance(v, bool): | 
 |         res += '%s,\n' % v | 
 |       elif isinstance(v, list): | 
 |         res += '[\n' | 
 |         if k == 'deps' and len(self.external_deps) > 1: | 
 |           indent = '           ' | 
 |         else: | 
 |           indent = '    ' | 
 |         for entry in sorted(v): | 
 |           if entry.startswith('PERFETTO_CONFIG.'): | 
 |             res += '%s    %s,\n' % (indent, entry) | 
 |           else: | 
 |             res += '%s    "%s",\n' % (indent, entry) | 
 |         res += '%s]' % indent | 
 |         if k == 'deps' and self.external_deps: | 
 |           res += ' + %s' % self.external_deps[0] | 
 |           for edep in self.external_deps[1:]: | 
 |             if isinstance(edep, list): | 
 |               res += ' + [\n' | 
 |               for inner_dep in edep: | 
 |                 res += '        "%s",\n' % inner_dep | 
 |               res += '    ]' | 
 |             else: | 
 |               res += ' +\n%s%s' % (indent, edep) | 
 |         res += ',\n' | 
 |       else: | 
 |         raise Error('Unsupported value %s', type(v)) | 
 |     res += ')\n\n' | 
 |     return res | 
 |  | 
 |  | 
 | def get_bazel_label_name(gn_name: str): | 
 |   """Converts a GN target name into a Bazel label name. | 
 |  | 
 |   If target is in the public target list, returns only the GN target name, | 
 |   e.g.: //src/ipc:perfetto_ipc -> perfetto_ipc | 
 |  | 
 |   Otherwise, in the case of an intermediate target, returns a mangled path. | 
 |   e.g.:  //include/perfetto/base:base -> include_perfetto_base_base. | 
 |   """ | 
 |   if gn_name in default_targets: | 
 |     return gn_utils.label_without_toolchain(gn_name).split(':')[1] | 
 |   return gn_utils.label_to_target_name_with_path(gn_name) | 
 |  | 
 |  | 
 | def get_bazel_python_label_name(gn_name: str): | 
 |   """Converts a Python GN label into a Bazel label.""" | 
 |   name = re.sub(r'^//python:?', '', gn_name) | 
 |   return gn_utils.label_to_target_name_with_path(name) | 
 |  | 
 |  | 
 | def get_bazel_proto_sources_label(target_name: str): | 
 |   """Converts a GN target name into a Bazel proto label name.""" | 
 |   return re.sub('_(lite|zero|cpp|ipc|source_set|descriptor)$', '', | 
 |                 get_bazel_label_name(target_name)) + '_protos' | 
 |  | 
 |  | 
 | def gen_proto_label(target: GnParser.Target): | 
 |   """ Generates the xx_proto_library label for proto targets.""" | 
 |   assert (target.type == 'proto_library') | 
 |  | 
 |   sources_label_name = get_bazel_proto_sources_label(target.name) | 
 |  | 
 |   # For 'source_set' plugins, we don't want to generate any plugin-dependent | 
 |   # targets so just return the label of the proto sources only. | 
 |   if target.proto_plugin == 'source_set': | 
 |     sources_label = BazelLabel(sources_label_name, 'perfetto_proto_library') | 
 |     sources_label.comment = target.name | 
 |     assert (all(x.startswith('//') for x in target.sources)) | 
 |     assert (all(x.endswith('.proto') for x in target.sources)) | 
 |     sources_label.srcs = sorted([x[2:] for x in target.sources])  # Strip //. | 
 |     sources_label.deps = sorted([ | 
 |         ':' + get_bazel_proto_sources_label(x.name) | 
 |         for x in target.transitive_proto_deps() | 
 |     ]) | 
 |  | 
 |     # In Bazel, proto_paths are not a supported concept becauase strong | 
 |     # dependency checking is enabled. Instead, we need to depend on the target | 
 |     # which includes the proto we want to depend on. | 
 |     # For example, we include the proto_path |buildtools_protobuf_src| because | 
 |     # we want to depend on the "google/protobuf/descriptor.proto" proto file. | 
 |     # This will be exposed by the |protobuf_descriptor_proto| dep. | 
 |     if buildtools_protobuf_src in target.proto_paths: | 
 |       sources_label.external_deps = [ | 
 |           'PERFETTO_CONFIG.deps.protobuf_descriptor_proto' | 
 |       ] | 
 |  | 
 |     sources_label.visibility = ['PERFETTO_CONFIG.proto_library_visibility'] | 
 |  | 
 |     sources_label.exports = sorted( | 
 |         [':' + get_bazel_proto_sources_label(d) for d in target.proto_exports]) | 
 |     return sources_label | 
 |  | 
 |   # For all other types of plugins, we need to generate | 
 |   if target.proto_plugin == 'proto': | 
 |     plugin_label_type = 'perfetto_cc_proto_library' | 
 |   elif target.proto_plugin == 'protozero': | 
 |     plugin_label_type = 'perfetto_cc_protozero_library' | 
 |   elif target.proto_plugin == 'cppgen': | 
 |     plugin_label_type = 'perfetto_cc_protocpp_library' | 
 |   elif target.proto_plugin == 'ipc': | 
 |     plugin_label_type = 'perfetto_cc_ipc_library' | 
 |   elif target.proto_plugin == 'descriptor': | 
 |     plugin_label_type = 'perfetto_proto_descriptor' | 
 |   else: | 
 |     raise Error('Unknown proto plugin: %s' % target.proto_plugin) | 
 |   plugin_label_name = get_bazel_label_name(target.name) | 
 |   plugin_label = BazelLabel(plugin_label_name, plugin_label_type) | 
 |   plugin_label.comment = target.name | 
 |  | 
 |   # When using the plugins we need to pass down also the transitive deps. | 
 |   # For instance consider foo.proto including common.proto. The generated | 
 |   # foo.cc will #include "common.gen.h". Hence the generated cc_protocpp_library | 
 |   # rule need to pass down the dependency on the target that generates | 
 |   # common.gen.{cc,h}. | 
 |   if target.proto_plugin in ('cppgen', 'ipc', 'protozero'): | 
 |     plugin_label.deps += [ | 
 |         ':' + get_bazel_label_name(x.name) | 
 |         for x in target.transitive_proto_deps() | 
 |     ] | 
 |  | 
 |   # Add any dependencies on source_set targets (i.e. targets containing proto | 
 |   # files). For descriptors, we will have an explicit edge between the | 
 |   # descriptor and source set wheras for other plugin types, this edge is | 
 |   # implicit. | 
 |   if target.proto_plugin == 'descriptor': | 
 |     plugin_label.deps += [ | 
 |         ':' + get_bazel_proto_sources_label(x.name) | 
 |         for x in target.proto_deps() | 
 |     ] | 
 |   else: | 
 |     plugin_label.deps += [':' + sources_label_name] | 
 |  | 
 |   # Since the descriptor generates an explicit output file which can be | 
 |   # referenced by other targets, we specify a name for it. | 
 |   if target.proto_plugin == 'descriptor': | 
 |     plugin_label.outs = [plugin_label_name + '.bin'] | 
 |  | 
 |   return plugin_label | 
 |  | 
 |  | 
 | def gen_proto_group_target(gn: GnParser, name: str, desc: Dict[str, Any]): | 
 |   # Get a recursive list of the proto_library targets rooted here which | 
 |   # have src. | 
 |   deps_set = set(desc['sources']) | 
 |   for target_name in desc['sources']: | 
 |     target = gn.get_target(target_name) | 
 |     deps_set.update(d.name for d in target.transitive_proto_deps()) | 
 |  | 
 |   # First, create a root source set target which references all the child | 
 |   # source set targets. We publish this as well as depending on this in all | 
 |   # subsequent targets. | 
 |   sources_label = BazelLabel(name + '_proto', 'perfetto_proto_library') | 
 |   sources_label.deps = [ | 
 |       ':' + get_bazel_proto_sources_label(name) | 
 |       for name in sorted(list(deps_set)) | 
 |   ] | 
 |   sources_label.visibility = desc['visibility'] | 
 |   sources_label.comment = f'''[{', '.join(desc['sources'])}]''' | 
 |  | 
 |   cc_label = BazelLabel(name + '_cc_proto', 'perfetto_cc_proto_library') | 
 |   cc_label.deps = [':' + sources_label.name] | 
 |   cc_label.visibility = desc['visibility'] | 
 |   cc_label.comment = sources_label.comment | 
 |  | 
 |   java_label = BazelLabel(name + '_java_proto', 'perfetto_java_proto_library') | 
 |   java_label.deps = [':' + sources_label.name] | 
 |   java_label.visibility = desc['visibility'] | 
 |   java_label.comment = sources_label.comment | 
 |  | 
 |   lite_name = name + '_java_proto_lite' | 
 |   java_lite_label = BazelLabel(lite_name, 'perfetto_java_lite_proto_library') | 
 |   java_lite_label.deps = [':' + sources_label.name] | 
 |   java_lite_label.visibility = desc['visibility'] | 
 |   java_lite_label.comment = sources_label.comment | 
 |  | 
 |   py_label = BazelLabel(name + '_py_pb2', 'perfetto_py_proto_library') | 
 |   py_label.deps = [':' + sources_label.name] | 
 |   py_label.visibility = desc['visibility'] | 
 |   py_label.comment = sources_label.comment | 
 |  | 
 |   return [sources_label, cc_label, java_label, java_lite_label, py_label] | 
 |  | 
 |  | 
 | def gen_cc_proto_descriptor(target: GnParser.Target): | 
 |   label = BazelLabel( | 
 |       get_bazel_label_name(target.name), 'perfetto_cc_proto_descriptor') | 
 |   label.comment = target.name | 
 |   label.deps += [ | 
 |       ':' + get_bazel_label_name(x.name) for x in target.proto_deps() | 
 |   ] | 
 |   label.deps += [ | 
 |       gn_utils.label_to_path(src) | 
 |       for src in target.inputs | 
 |       if "tmp.gn_utils" not in src | 
 |   ] | 
 |  | 
 |   label.outs += target.outputs | 
 |   return [label] | 
 |  | 
 |  | 
 | def gen_cc_amalgamated_sql(target: GnParser.Target): | 
 |   label = BazelLabel( | 
 |       get_bazel_label_name(target.name), 'perfetto_cc_amalgamated_sql') | 
 |  | 
 |   def find_arg(name): | 
 |     for i, arg in enumerate(target.args): | 
 |       if arg.startswith(f'--{name}'): | 
 |         return target.args[i + 1] | 
 |  | 
 |   label.comment = target.name | 
 |   label.namespace = find_arg('namespace') | 
 |  | 
 |   label.deps += sorted( | 
 |       ':' + get_bazel_label_name(x.name) for x in target.transitive_deps) | 
 |   label.outs += target.outputs | 
 |   return [label] | 
 |  | 
 |  | 
 | def gen_sql_source_set(target: GnParser.Target): | 
 |   label = BazelLabel(get_bazel_label_name(target.name), 'perfetto_filegroup') | 
 |   label.comment = target.name | 
 |   label.srcs += (gn_utils.label_to_path(x) for x in target.inputs) | 
 |   return [label] | 
 |  | 
 |  | 
 | def gen_cc_tp_tables(target: GnParser.Target): | 
 |   label = BazelLabel(get_bazel_label_name(target.name), 'perfetto_cc_tp_tables') | 
 |   label.comment = target.name | 
 |   label.srcs += (gn_utils.label_to_path(x) for x in target.sources) | 
 |   label.deps += sorted(':' + get_bazel_label_name(x.name) | 
 |                        for x in target.transitive_deps | 
 |                        if x.name not in default_python_targets) | 
 |   label.outs += target.outputs | 
 |   return [label] | 
 |  | 
 |  | 
 | def gen_target(gn_target: GnParser.Target): | 
 |   if gn_target.type == 'proto_library': | 
 |     return [gen_proto_label(gn_target)] | 
 |   elif gn_target.type == 'action': | 
 |     if gn_target.name in custom_actions: | 
 |       return custom_actions[gn_target.name](gn_target) | 
 |     if gn_target.custom_action_type == 'sql_amalgamation': | 
 |       return gen_cc_amalgamated_sql(gn_target) | 
 |     if gn_target.custom_action_type == 'sql_source_set': | 
 |       return gen_sql_source_set(gn_target) | 
 |     if gn_target.custom_action_type == 'cc_proto_descriptor': | 
 |       return gen_cc_proto_descriptor(gn_target) | 
 |     if gn_target.custom_action_type == 'tp_tables': | 
 |       return gen_cc_tp_tables(gn_target) | 
 |     return [] | 
 |   elif gn_target.type == 'group': | 
 |     return [] | 
 |   elif gn_target.type == 'executable': | 
 |     bazel_type = 'perfetto_cc_binary' | 
 |   elif gn_target.type == 'shared_library': | 
 |     bazel_type = 'perfetto_cc_binary' | 
 |   elif gn_target.type == 'static_library': | 
 |     bazel_type = 'perfetto_cc_library' | 
 |   elif gn_target.type == 'source_set': | 
 |     bazel_type = 'perfetto_filegroup' | 
 |   elif gn_target.type == 'generated_file': | 
 |     return [] | 
 |   else: | 
 |     raise Error('target type not supported: %s' % gn_target.type) | 
 |  | 
 |   label = BazelLabel(get_bazel_label_name(gn_target.name), bazel_type) | 
 |   label.comment = gn_target.name | 
 |  | 
 |   # Supporting 'public' on source_sets would require not converting them to | 
 |   # filegroups in bazel. | 
 |   if gn_target.public_headers: | 
 |     if bazel_type == 'perfetto_cc_library': | 
 |       label.hdrs += [x[2:] for x in gn_target.public_headers] | 
 |     else: | 
 |       raise Error('%s: \'public\' currently supported only for cc_library' % | 
 |                   gn_target.name) | 
 |  | 
 |   raw_srcs = [x[2:] for x in gn_target.sources] | 
 |   raw_srcs += [x[2:] for x in gn_target.inputs] | 
 |   if bazel_type == 'perfetto_cc_library': | 
 |     label.srcs += [x for x in raw_srcs if not x.startswith('include')] | 
 |     label.hdrs += [x for x in raw_srcs if x.startswith('include')] | 
 |  | 
 |     # Most Perfetto libraries cannot by dynamically linked as they would | 
 |     # cause ODR violations. | 
 |     label.__dict__['linkstatic'] = True | 
 |   else: | 
 |     label.srcs = raw_srcs | 
 |  | 
 |   if gn_target.name in public_targets: | 
 |     label.visibility = ['//visibility:public'] | 
 |  | 
 |   if gn_target.type in gn_utils.LINKER_UNIT_TYPES: | 
 |     # |source_sets| contains the transitive set of source_set deps. | 
 |     for trans_dep in gn_target.transitive_source_set_deps(): | 
 |       name = ':' + get_bazel_label_name(trans_dep.name) | 
 |       if name.startswith( | 
 |           ':include_perfetto_') and gn_target.type != 'executable': | 
 |         label.hdrs += [name] | 
 |       else: | 
 |         label.srcs += [name] | 
 |     for dep in sorted(gn_target.non_proto_or_source_set_deps()): | 
 |       dep_name = dep.name | 
 |       if dep_name.startswith('//gn:'): | 
 |         assert (dep_name in external_deps), dep | 
 |  | 
 |       # tp_tables produces a filegroup not a cc_lbirary so should end up srcs | 
 |       # not deps. | 
 |       if dep.custom_action_type == 'tp_tables': | 
 |         label.srcs += [':' + get_bazel_label_name(dep_name)] | 
 |       elif dep_name in external_deps: | 
 |         assert (isinstance(external_deps[dep_name], list)) | 
 |         label.external_deps += external_deps[dep_name] | 
 |       else: | 
 |         label.deps += [':' + get_bazel_label_name(dep_name)] | 
 |     label.deps += [ | 
 |         ':' + get_bazel_label_name(x.name) | 
 |         for x in gn_target.transitive_cpp_proto_deps() | 
 |     ] | 
 |  | 
 |   # All items starting with : need to be sorted to the end of the list. | 
 |   # However, Python makes specifying a comparator function hard so cheat | 
 |   # instead and make everything start with : sort as if it started with | | 
 |   # As | > all other normal ASCII characters, this will lead to all : targets | 
 |   # starting with : to be sorted to the end. | 
 |   label.srcs = sorted(label.srcs, key=lambda x: x.replace(':', '|')) | 
 |  | 
 |   label.deps = sorted(label.deps) | 
 |   label.hdrs = sorted(label.hdrs) | 
 |   return [label] | 
 |  | 
 |  | 
 | def gen_target_str(gn_target): | 
 |   return ''.join(str(x) for x in gen_target(gn_target)) | 
 |  | 
 |  | 
 | def generate_build(gn_desc, targets, extras): | 
 |   gn = gn_utils.GnParser(gn_desc) | 
 |   project_root = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) | 
 |   tool_name = os.path.relpath(os.path.abspath(__file__), project_root) | 
 |   res = ''' | 
 | # Copyright (C) 2019 The Android Open Source Project | 
 | # | 
 | # Licensed under the Apache License, Version 2.0 (the "License"); | 
 | # you may not use this file except in compliance with the License. | 
 | # You may obtain a copy of the License at | 
 | # | 
 | #      http://www.apache.org/licenses/LICENSE-2.0 | 
 | # | 
 | # Unless required by applicable law or agreed to in writing, software | 
 | # distributed under the License is distributed on an "AS IS" BASIS, | 
 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | # See the License for the specific language governing permissions and | 
 | # limitations under the License. | 
 | # | 
 | # This file is automatically generated by {}. Do not edit. | 
 |  | 
 | load("@perfetto_cfg//:perfetto_cfg.bzl", "PERFETTO_CONFIG") | 
 | load( | 
 |     "@perfetto//bazel:rules.bzl", | 
 |     "perfetto_build_config_cc_library", | 
 |     "perfetto_cc_amalgamated_sql", | 
 |     "perfetto_cc_binary", | 
 |     "perfetto_cc_ipc_library", | 
 |     "perfetto_cc_library", | 
 |     "perfetto_cc_proto_descriptor", | 
 |     "perfetto_cc_proto_library", | 
 |     "perfetto_cc_protocpp_library", | 
 |     "perfetto_cc_protozero_library", | 
 |     "perfetto_cc_tp_tables", | 
 |     "perfetto_filegroup", | 
 |     "perfetto_genrule", | 
 |     "perfetto_go_proto_library", | 
 |     "perfetto_java_lite_proto_library", | 
 |     "perfetto_java_proto_library", | 
 |     "perfetto_proto_descriptor", | 
 |     "perfetto_proto_library", | 
 |     "perfetto_py_binary", | 
 |     "perfetto_py_library", | 
 |     "perfetto_py_proto_library", | 
 | ) | 
 |  | 
 | package(default_visibility = [PERFETTO_CONFIG.root + ":__subpackages__"]) | 
 |  | 
 | licenses(["notice"]) | 
 |  | 
 | exports_files(["NOTICE"]) | 
 |  | 
 | '''.format(tool_name).lstrip() | 
 |  | 
 |   # Public targets need to be computed at the beginning (to figure out the | 
 |   # intermediate deps) but printed at the end (because declaration order matters | 
 |   # in Bazel). | 
 |   public_str = '' | 
 |   for target_name in sorted(public_targets): | 
 |     target = gn.get_target(target_name) | 
 |     public_str += gen_target_str(target) | 
 |  | 
 |   res += ''' | 
 | # ############################################################################## | 
 | # Internal targets | 
 | # ############################################################################## | 
 |  | 
 | '''.lstrip() | 
 |   # Generate the other non-public targets. | 
 |   for target_name in sorted(set(default_targets) - set(public_targets)): | 
 |     target = gn.get_target(target_name) | 
 |     res += gen_target_str(target) | 
 |  | 
 |   # Generate all the intermediate targets. | 
 |   for target in sorted(itervalues(gn.all_targets)): | 
 |     if target.name in default_targets or target.name in gn.proto_libs: | 
 |       continue | 
 |     res += gen_target_str(target) | 
 |  | 
 |   res += ''' | 
 | # ############################################################################## | 
 | # Proto libraries | 
 | # ############################################################################## | 
 |  | 
 | '''.lstrip() | 
 |   # Generate targets for proto groups. | 
 |   for l_name, t_desc in proto_groups.items(): | 
 |     res += ''.join(str(x) for x in gen_proto_group_target(gn, l_name, t_desc)) | 
 |  | 
 |   # For any non-source set and non-descriptor targets, ensure the source set | 
 |   # associated to that target is discovered. | 
 |   for target in sorted(itervalues(gn.all_targets)): | 
 |     plugin = target.proto_plugin | 
 |     if plugin is None or plugin == 'source_set' or plugin == 'descriptor': | 
 |       continue | 
 |     gn.get_target(re.sub('(lite|zero|cpp|ipc)$', 'source_set', target.name)) | 
 |  | 
 |   # Discover all the default proto targets so it will be generated next. | 
 |   for target in sorted(proto_default_targets): | 
 |     gn.get_target(target) | 
 |  | 
 |   # Generate targets for the transitive set of proto targets. | 
 |   labels = [ | 
 |       l for target in sorted(itervalues(gn.proto_libs)) | 
 |       for l in gen_target(target) | 
 |   ] | 
 |   res += ''.join(str(x) for x in sorted(labels)) | 
 |  | 
 |   res += ''' | 
 | # ############################################################################## | 
 | # Public targets | 
 | # ############################################################################## | 
 |  | 
 | '''.lstrip() | 
 |   res += public_str | 
 |   res += '# Content from BUILD.extras\n\n' | 
 |   res += extras | 
 |  | 
 |   # Check for ODR violations | 
 |   for target_name in default_targets: | 
 |     checker = gn_utils.ODRChecker(gn, target_name) | 
 |  | 
 |   return res | 
 |  | 
 |  | 
 | def main(): | 
 |   parser = argparse.ArgumentParser( | 
 |       description='Generate BUILD from a GN description.') | 
 |   parser.add_argument( | 
 |       '--check-only', | 
 |       help='Don\'t keep the generated files', | 
 |       action='store_true') | 
 |   parser.add_argument( | 
 |       '--desc', | 
 |       help='GN description ' + | 
 |       '(e.g., gn desc out --format=json --all-toolchains "//*"') | 
 |   parser.add_argument( | 
 |       '--repo-root', | 
 |       help='Standalone Perfetto repository to generate a GN description', | 
 |       default=gn_utils.repo_root(), | 
 |   ) | 
 |   parser.add_argument( | 
 |       '--extras', | 
 |       help='Extra targets to include at the end of the BUILD file', | 
 |       default=os.path.join(gn_utils.repo_root(), 'BUILD.extras'), | 
 |   ) | 
 |   parser.add_argument( | 
 |       '--output', | 
 |       help='BUILD file to create', | 
 |       default=os.path.join(gn_utils.repo_root(), 'BUILD'), | 
 |   ) | 
 |   parser.add_argument( | 
 |       '--output-python', | 
 |       help='Python BUILD file to create', | 
 |       default=os.path.join(gn_utils.repo_root(), 'python', 'BUILD'), | 
 |   ) | 
 |   parser.add_argument( | 
 |       'targets', | 
 |       nargs=argparse.REMAINDER, | 
 |       help='Targets to include in the BUILD file (e.g., "//:perfetto_tests")') | 
 |   args = parser.parse_args() | 
 |  | 
 |   if args.desc: | 
 |     with open(args.desc) as f: | 
 |       desc = json.load(f) | 
 |   else: | 
 |     desc = gn_utils.create_build_description(gn_args, args.repo_root) | 
 |  | 
 |   out_files = [] | 
 |  | 
 |   # Generate the main BUILD file. | 
 |   with open(args.extras, 'r') as extra_f: | 
 |     extras = extra_f.read() | 
 |  | 
 |   contents = generate_build(desc, args.targets or default_targets, extras) | 
 |   out_files.append(args.output + '.swp') | 
 |   with open(out_files[-1], 'w') as out_f: | 
 |     out_f.write(contents) | 
 |  | 
 |   # Generate the python BUILD file. | 
 |   python_gen = PythonBuildGenerator() | 
 |   python_contents = python_gen.generate(desc) | 
 |   out_files.append(args.output_python + '.swp') | 
 |   with open(out_files[-1], 'w') as out_f: | 
 |     out_f.write(python_contents) | 
 |  | 
 |   # Generate the build flags file. | 
 |   out_files.append(os.path.join(buildflags_dir, 'perfetto_build_flags.h.swp')) | 
 |   gn_utils.gen_buildflags(gn_args, out_files[-1]) | 
 |  | 
 |   return gn_utils.check_or_commit_generated_files(out_files, args.check_only) | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |   sys.exit(main()) |