| #!/usr/bin/env python | 
 | # 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 functools | 
 | import json | 
 | import os | 
 | import re | 
 | import sys | 
 | import textwrap | 
 |  | 
 | import gn_utils | 
 |  | 
 | # Copyright header for generated code. | 
 | header = """# 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. | 
 | """.format(__file__) | 
 |  | 
 | # Arguments for the GN output directory. | 
 | # host_os="linux" is to generate the right build files from Mac OS. | 
 | gn_args = 'target_os="linux" is_debug=false host_os="linux"' | 
 |  | 
 | # Default targets to translate to the blueprint file. | 
 | default_targets = [ | 
 |   '//:trace_processor_shell', | 
 |   '//src/protozero:libprotozero', | 
 |   '//src/trace_processor:trace_processor', | 
 |   '//tools/trace_to_text:trace_to_text_host(//gn/standalone/toolchain:gcc_like_host)', | 
 |   '//tools/trace_to_text:libpprofbuilder', | 
 |   '//protos/perfetto/config:merged_config_gen', | 
 |   '//protos/perfetto/trace:merged_trace_gen', | 
 |   '//protos/perfetto/trace_processor:lite_gen', | 
 |   '//protos/perfetto/metrics:lite_gen' | 
 | ] | 
 |  | 
 | # Aliases to add to the BUILD file | 
 | alias_targets = { | 
 |   '//src/protozero:libprotozero': 'libprotozero', | 
 |   '//src/trace_processor:trace_processor': 'trace_processor', | 
 |   '//tools/trace_to_text:trace_to_text_host': 'trace_to_text', | 
 | } | 
 |  | 
 |  | 
 | def enable_sqlite(module): | 
 |   module.deps.add(Label('//third_party/sqlite')) | 
 |   module.deps.add(Label('//third_party/sqlite:sqlite_ext_percentile')) | 
 |  | 
 |  | 
 | def enable_jsoncpp(module): | 
 |   module.deps.add(Label('//third_party/perfetto/google:jsoncpp')) | 
 |  | 
 |  | 
 | def enable_linenoise(module): | 
 |   module.deps.add(Label('//third_party/perfetto/google:linenoise')) | 
 |  | 
 |  | 
 | def enable_gtest_prod(module): | 
 |   module.deps.add(Label('//third_party/perfetto/google:gtest_prod')) | 
 |  | 
 |  | 
 | def enable_protobuf_full(module): | 
 |   module.deps.add(Label('//third_party/protobuf:libprotoc_legacy')) | 
 |   module.deps.add(Label('//third_party/protobuf:protobuf_legacy')) | 
 |  | 
 |  | 
 | def enable_perfetto_version(module): | 
 |   module.deps.add(Label('//third_party/perfetto/google:perfetto_version')) | 
 |  | 
 |  | 
 | def enable_zlib(module): | 
 |   module.deps.add(Label('//third_party/zlib:zlibsystem')) | 
 |  | 
 |  | 
 | def disable_module(module): | 
 |   pass | 
 |  | 
 |  | 
 | # Internal equivalents for third-party libraries that the upstream project | 
 | # depends on. | 
 | builtin_deps = { | 
 |     '//buildtools:linenoise': enable_linenoise, | 
 |     '//buildtools:protobuf_full': enable_protobuf_full, | 
 |     '//buildtools:protobuf_lite': disable_module, | 
 |     '//buildtools:protoc': disable_module, | 
 |     '//buildtools:sqlite': enable_sqlite, | 
 |     '//buildtools:zlib': enable_zlib, | 
 |     '//gn:default_deps': disable_module, | 
 |     '//gn:jsoncpp': enable_jsoncpp, | 
 |     '//gn:protoc_lib': enable_protobuf_full, | 
 |     '//gn/standalone:gen_git_revision': enable_perfetto_version, | 
 | } | 
 |  | 
 | # ---------------------------------------------------------------------------- | 
 | # End of configuration. | 
 | # ---------------------------------------------------------------------------- | 
 |  | 
 |  | 
 | class Error(Exception): | 
 |   pass | 
 |  | 
 |  | 
 | def is_public_header(label): | 
 |   """ | 
 |   Returns if this is a c++ header file that is part of the API. | 
 |   Args: | 
 |       label: Label to evaluate | 
 |   """ | 
 |   # TODO(135923303): Remove the pprof builder once the long-term solution lands | 
 |   return label.endswith('.h') and ( | 
 |     label.startswith('//include/perfetto/') or | 
 |     label == '//tools/trace_to_text/pprof_builder.h') | 
 |  | 
 |  | 
 | @functools.total_ordering | 
 | class Label(object): | 
 |   """Represents a label in BUILD file terminology. This class wraps a string | 
 |   label to allow for correct comparision of labels for sorting. | 
 |  | 
 |   Args: | 
 |       label: The string rerepsentation of the label. | 
 |   """ | 
 |  | 
 |   def __init__(self, label): | 
 |     self.label = label | 
 |  | 
 |   def is_absolute(self): | 
 |     return self.label.startswith('//') | 
 |  | 
 |   def dirname(self): | 
 |     return self.label.split(':')[0] if ':' in self.label else self.label | 
 |  | 
 |   def basename(self): | 
 |     return self.label.split(':')[1] if ':' in self.label else '' | 
 |  | 
 |   def __eq__(self, other): | 
 |     return self.label == other.label | 
 |  | 
 |   def __lt__(self, other): | 
 |     return ( | 
 |         self.is_absolute(), | 
 |         self.dirname(), | 
 |         self.basename() | 
 |     ) < ( | 
 |         other.is_absolute(), | 
 |         other.dirname(), | 
 |         other.basename() | 
 |     ) | 
 |  | 
 |   def __str__(self): | 
 |     return self.label | 
 |  | 
 |   def __hash__(self): | 
 |     return hash(self.label) | 
 |  | 
 |  | 
 | class Writer(object): | 
 |   def __init__(self, output, width=79): | 
 |     self.output = output | 
 |     self.width = width | 
 |  | 
 |   def comment(self, text): | 
 |     for line in textwrap.wrap(text, | 
 |                               self.width - 2, | 
 |                               break_long_words=False, | 
 |                               break_on_hyphens=False): | 
 |       self.output.write('# {}\n'.format(line)) | 
 |  | 
 |   def newline(self): | 
 |     self.output.write('\n') | 
 |  | 
 |   def line(self, s, indent=0): | 
 |     self.output.write('    ' * indent + s + '\n') | 
 |  | 
 |   def variable(self, key, value, sort=True): | 
 |     if value is None: | 
 |       return | 
 |     if isinstance(value, set) or isinstance(value, list): | 
 |       if len(value) == 0: | 
 |         return | 
 |       self.line('{} = ['.format(key), indent=1) | 
 |       for v in sorted(list(value)) if sort else value: | 
 |         self.line('"{}",'.format(v), indent=2) | 
 |       self.line('],', indent=1) | 
 |     elif isinstance(value, basestring): | 
 |       self.line('{} = "{}",'.format(key, value), indent=1) | 
 |     else: | 
 |       self.line('{} = {},'.format(key, value), indent=1) | 
 |  | 
 |   def header(self): | 
 |     self.output.write(header) | 
 |  | 
 |  | 
 | class Target(object): | 
 |   """In-memory representation of a BUILD target.""" | 
 |  | 
 |   def __init__(self, type, name, gn_name=None): | 
 |     assert type in ('cc_binary', 'cc_library', 'cc_proto_library', | 
 |                     'proto_library', 'filegroup', 'alias', | 
 |                     'pbzero_cc_proto_library', 'genrule', | 
 |                     'transitive_descriptor_set', 'java_proto_library' ) | 
 |     self.type = type | 
 |     self.name = name | 
 |     self.srcs = set() | 
 |     self.hdrs = set() | 
 |     self.deps = set() | 
 |     self.visibility = set() | 
 |     self.gn_name = gn_name | 
 |     self.cc_proto_fields = False | 
 |     self.src_proto_library = None | 
 |     self.outs = set() | 
 |     self.cmd = None | 
 |     self.tools = set() | 
 |  | 
 |   def write(self, writer): | 
 |     if self.gn_name: | 
 |       writer.comment('GN target: {}'.format(self.gn_name)) | 
 |  | 
 |     writer.line('{}('.format(self.type)) | 
 |     writer.variable('name', self.name) | 
 |     writer.variable('srcs', self.srcs) | 
 |     writer.variable('hdrs', self.hdrs) | 
 |  | 
 |     if self.cc_proto_fields: | 
 |       assert(self.type == 'proto_library') | 
 |       if self.srcs: | 
 |         writer.variable('has_services', 1) | 
 |       writer.variable('cc_api_version', 2) | 
 |       if self.srcs: | 
 |         writer.variable('cc_generic_services', 1) | 
 |  | 
 |     writer.variable('src_proto_library', self.src_proto_library) | 
 |  | 
 |     writer.variable('outs', self.outs) | 
 |     writer.variable('cmd', self.cmd) | 
 |     writer.variable('tools', self.tools) | 
 |  | 
 |     # Keep visibility and deps last. | 
 |     writer.variable('visibility', self.visibility) | 
 |  | 
 |     if type != 'filegroup': | 
 |       writer.variable('deps', self.deps) | 
 |  | 
 |     writer.line(')') | 
 |  | 
 |  | 
 | class Build(object): | 
 |   """In-memory representation of a BUILD file.""" | 
 |  | 
 |   def __init__(self, public, header_lines=[]): | 
 |     self.targets = {} | 
 |     self.public = public | 
 |     self.header_lines = header_lines | 
 |  | 
 |   def add_target(self, target): | 
 |     self.targets[target.name] = target | 
 |  | 
 |   def write(self, writer): | 
 |     writer.header() | 
 |     writer.newline() | 
 |     for line in self.header_lines: | 
 |       writer.line(line) | 
 |     if self.header_lines: | 
 |       writer.newline() | 
 |     if self.public: | 
 |       writer.line( | 
 |           'package(default_visibility = ["//visibility:public"])') | 
 |     else: | 
 |       writer.line( | 
 |           'package(default_visibility = ["//third_party/perfetto:__subpackages__"])') | 
 |     writer.newline() | 
 |     writer.line('licenses(["notice"])  # Apache 2.0') | 
 |     writer.newline() | 
 |     writer.line('exports_files(["LICENSE"])') | 
 |     writer.newline() | 
 |  | 
 |     sorted_targets = sorted( | 
 |         self.targets.itervalues(), key=lambda m: m.name) | 
 |     for target in sorted_targets[:-1]: | 
 |       target.write(writer) | 
 |       writer.newline() | 
 |  | 
 |     # BUILD files shouldn't have a trailing new line. | 
 |     sorted_targets[-1].write(writer) | 
 |  | 
 |  | 
 | class BuildGenerator(object): | 
 |   def __init__(self, desc): | 
 |     self.desc = desc | 
 |     self.action_generated_files = set() | 
 |  | 
 |     for target in self.desc.itervalues(): | 
 |       if target['type'] == 'action': | 
 |         self.action_generated_files.update(target['outputs']) | 
 |  | 
 |  | 
 |   def create_build_for_targets(self, targets): | 
 |     """Generate a BUILD for a list of GN targets and aliases.""" | 
 |     self.build = Build(public=True) | 
 |  | 
 |     proto_cc_import = 'load("//tools/build_defs/proto/cpp:cc_proto_library.bzl", "cc_proto_library")' | 
 |     descriptor_set_import = 'load("//tools/build_defs/proto:descriptor_set.bzl", "transitive_descriptor_set")' | 
 |     pbzero_cc_import = 'load("//third_party/perfetto/google:build_defs.bzl", "pbzero_cc_proto_library")' | 
 |     self.proto_build = Build(public=False, | 
 |                              header_lines=[ | 
 |                                proto_cc_import, | 
 |                                descriptor_set_import, | 
 |                                pbzero_cc_import, | 
 |                              ]) | 
 |  | 
 |     for target in targets: | 
 |       self.create_target(target) | 
 |  | 
 |     return (self.build, self.proto_build) | 
 |  | 
 |  | 
 |   def resolve_dependencies(self, target_name): | 
 |     """Return the set of direct dependent-on targets for a GN target. | 
 |  | 
 |     Args: | 
 |         desc: JSON GN description. | 
 |         target_name: Name of target | 
 |  | 
 |     Returns: | 
 |         A set of transitive dependencies in the form of GN targets. | 
 |     """ | 
 |  | 
 |     if gn_utils.label_without_toolchain(target_name) in builtin_deps: | 
 |       return set() | 
 |     target = self.desc[target_name] | 
 |     resolved_deps = set() | 
 |     for dep in target.get('deps', []): | 
 |       resolved_deps.add(dep) | 
 |     return resolved_deps | 
 |  | 
 |  | 
 |   def apply_module_sources_to_target(self, target, module_desc): | 
 |     """ | 
 |     Args: | 
 |         target: Module to which dependencies should be added. | 
 |         module_desc: JSON GN description of the module. | 
 |         visibility: Whether the module is visible with respect to the target. | 
 |     """ | 
 |     for src in module_desc.get('sources', []): | 
 |       label = Label(gn_utils.label_to_path(src)) | 
 |       if target.type == 'cc_library' and is_public_header(src): | 
 |         target.hdrs.add(label) | 
 |       else: | 
 |         target.srcs.add(label) | 
 |  | 
 |  | 
 |   def apply_module_dependency(self, target, dep_name): | 
 |     """ | 
 |     Args: | 
 |         build: BUILD instance which is being generated. | 
 |         proto_build: BUILD instance which is being generated to hold protos. | 
 |         desc: JSON GN description. | 
 |         target: Module to which dependencies should be added. | 
 |         dep_name: GN target of the dependency. | 
 |     """ | 
 |     # If the dependency refers to a library which we can replace with an internal | 
 |     # equivalent, stop recursing and patch the dependency in. | 
 |     dep_name_no_toolchain = gn_utils.label_without_toolchain(dep_name) | 
 |     if dep_name_no_toolchain in builtin_deps: | 
 |       builtin_deps[dep_name_no_toolchain](target) | 
 |       return | 
 |  | 
 |     dep_desc = self.desc[dep_name] | 
 |     if dep_desc['type'] == 'source_set': | 
 |       for inner_name in self.resolve_dependencies(dep_name): | 
 |         self.apply_module_dependency(target, inner_name) | 
 |  | 
 |       # Any source set which has a source generated by an action doesn't need | 
 |       # to be depended on as we will depend on the action directly. | 
 |       sources = dep_desc.get('sources', []) | 
 |       if any(src in self.action_generated_files for src in sources): | 
 |         return | 
 |  | 
 |       self.apply_module_sources_to_target(target, dep_desc) | 
 |     elif dep_desc['type'] == 'action': | 
 |       args = dep_desc['args'] | 
 |       if "gen_merged_sql_metrics" in dep_name: | 
 |         dep_target = self.create_merged_sql_metrics_target(dep_name) | 
 |         target.deps.add(Label("//third_party/perfetto:" + dep_target.name)) | 
 |  | 
 |         if target.type == 'cc_library' or target.type == 'cc_binary': | 
 |           target.srcs.update(dep_target.outs) | 
 |       elif '/protoc' in args[0]: | 
 |         result = self.create_proto_target(dep_name); | 
 |         if result is None: | 
 |           return | 
 |         (proto_target, cc_target) = result | 
 |         if target.type == 'proto_library': | 
 |           dep_target_name = proto_target.name | 
 |         else: | 
 |           dep_target_name = cc_target.name | 
 |         target.deps.add( | 
 |             Label("//third_party/perfetto/protos:" + dep_target_name)) | 
 |       else: | 
 |         raise Error('Unsupported action in target %s: %s' % (dep_target_name, | 
 |                                                             args)) | 
 |     elif dep_desc['type'] == 'static_library': | 
 |       dep_target = self.create_target(dep_name) | 
 |       target.deps.add(Label("//third_party/perfetto:" + dep_target.name)) | 
 |     elif dep_desc['type'] == 'group': | 
 |       for inner_name in self.resolve_dependencies(dep_name): | 
 |         self.apply_module_dependency(target, inner_name) | 
 |     elif dep_desc['type'] == 'executable': | 
 |       # Just create the dep target but don't add it as a dep because it's an | 
 |       # executable. | 
 |       self.create_target(dep_name) | 
 |     else: | 
 |       raise Error('Unknown target name %s with type: %s' % | 
 |                   (dep_name, dep_desc['type'])) | 
 |  | 
 |   def create_merged_sql_metrics_target(self, gn_target_name): | 
 |     target_desc = self.desc[gn_target_name] | 
 |     gn_target_name_no_toolchain = gn_utils.label_without_toolchain( | 
 |         gn_target_name) | 
 |     target = Target( | 
 |       'genrule', | 
 |       'gen_merged_sql_metrics', | 
 |       gn_name=gn_target_name_no_toolchain, | 
 |     ) | 
 |     target.outs.update( | 
 |       Label(src[src.index('gen/') + len('gen/'):]) | 
 |       for src in target_desc.get('outputs', []) | 
 |     ) | 
 |     target.cmd = '$(location gen_merged_sql_metrics_py) --cpp_out=$@ $(SRCS)' | 
 |     target.tools.update([ | 
 |       'gen_merged_sql_metrics_py', | 
 |     ]) | 
 |     target.srcs.update( | 
 |       Label(gn_utils.label_to_path(src)) | 
 |       for src in target_desc.get('inputs', []) | 
 |       if src not in self.action_generated_files | 
 |     ) | 
 |     self.build.add_target(target) | 
 |     return target | 
 |  | 
 |   def create_proto_target(self, gn_target_name): | 
 |     target_desc = self.desc[gn_target_name] | 
 |     args = target_desc['args'] | 
 |  | 
 |     gn_target_name_no_toolchain = gn_utils.label_without_toolchain( | 
 |         gn_target_name) | 
 |     stripped_path = gn_target_name_no_toolchain.replace("protos/perfetto/", "") | 
 |  | 
 |     is_descriptor = any('descriptor_set_out' in arg for arg in args) | 
 |     if is_descriptor: | 
 |       return None | 
 |  | 
 |     is_pbzero = any("pbzero" in arg for arg in args) | 
 |     is_proto_lite = not is_pbzero and not is_descriptor | 
 |  | 
 |     pretty_target_name = gn_utils.label_to_target_name_with_path(stripped_path) | 
 |     pretty_target_name = pretty_target_name.replace("_lite_gen", "") | 
 |     pretty_target_name = pretty_target_name.replace("_zero_gen", "_zero") | 
 |     proto_target_name = pretty_target_name | 
 |  | 
 |     proto_target = Target( | 
 |       'proto_library', | 
 |       proto_target_name, | 
 |       gn_name=gn_target_name_no_toolchain | 
 |     ) | 
 |     proto_target.cc_proto_fields = is_proto_lite | 
 |     proto_target.srcs.update([ | 
 |       Label(gn_utils.label_to_path(src).replace('protos/', '')) | 
 |       for src in target_desc.get('sources', []) | 
 |     ]) | 
 |     if is_proto_lite: | 
 |       proto_target.visibility.add("//visibility:public") | 
 |     self.proto_build.add_target(proto_target) | 
 |  | 
 |     for dep_name in self.resolve_dependencies(gn_target_name): | 
 |       self.apply_module_dependency(proto_target, dep_name) | 
 |  | 
 |     if is_pbzero: | 
 |       # Remove all the protozero srcs from the proto_library. | 
 |       proto_target.srcs.difference_update( | 
 |           [src for src in proto_target.srcs if not src.label.endswith('.proto')]) | 
 |  | 
 |       # Remove all the non-proto deps from the proto_library and add to the cc | 
 |       # library. | 
 |       cc_deps = [ | 
 |         dep for dep in proto_target.deps | 
 |         if not dep.label.startswith('//third_party/perfetto/protos') | 
 |       ] | 
 |       proto_target.deps.difference_update(cc_deps) | 
 |  | 
 |       cc_target_name = proto_target.name + "_cc_proto" | 
 |       cc_target = Target('pbzero_cc_proto_library', cc_target_name, | 
 |                          gn_name=gn_target_name_no_toolchain) | 
 |  | 
 |       cc_target.deps.add(Label('//third_party/perfetto:libprotozero')) | 
 |       cc_target.deps.update(cc_deps) | 
 |  | 
 |       # Add the proto_library to the cc_target. | 
 |       cc_target.src_proto_library = \ | 
 |           "//third_party/perfetto/protos:" + proto_target.name | 
 |  | 
 |       self.proto_build.add_target(cc_target) | 
 |     else: | 
 |       cc_target_name = proto_target.name + "_cc_proto" | 
 |       cc_target = Target('cc_proto_library', | 
 |                         cc_target_name, gn_name=gn_target_name_no_toolchain) | 
 |       cc_target.visibility.add("//visibility:public") | 
 |       cc_target.deps.add( | 
 |           Label("//third_party/perfetto/protos:" + proto_target.name)) | 
 |       self.proto_build.add_target(cc_target) | 
 |  | 
 |       java_target_name = proto_target.name + "_java_proto" | 
 |       java_target = Target('java_proto_library', | 
 |                            java_target_name, | 
 |                            gn_name=gn_target_name_no_toolchain) | 
 |       java_target.visibility.add("//visibility:public") | 
 |       java_target.deps.add( | 
 |           Label("//third_party/perfetto/protos:" + proto_target.name)) | 
 |       self.proto_build.add_target(java_target) | 
 |  | 
 |     return (proto_target, cc_target) | 
 |  | 
 |  | 
 |   def create_target(self, gn_target_name): | 
 |     """Generate module(s) for a given GN target. | 
 |  | 
 |     Given a GN target name, generate one or more corresponding modules into a | 
 |     build file. | 
 |  | 
 |     Args: | 
 |         build: Build instance which is being generated. | 
 |         desc: JSON GN description. | 
 |         gn_target_name: GN target name for module generation. | 
 |     """ | 
 |  | 
 |     target_desc = self.desc[gn_target_name] | 
 |     if target_desc['type'] == 'action': | 
 |       args = target_desc['args'] | 
 |       if '/protoc' in args[0]: | 
 |         return self.create_proto_target(gn_target_name) | 
 |       else: | 
 |         raise Error('Unsupported action in target %s: %s' % (gn_target_name, | 
 |                                                             args)) | 
 |     elif target_desc['type'] == 'executable': | 
 |       target_type = 'cc_binary' | 
 |     elif target_desc['type'] == 'static_library': | 
 |       target_type = 'cc_library' | 
 |     elif target_desc['type'] == 'source_set': | 
 |       target_type = 'filegroup' | 
 |     else: | 
 |       raise Error('Unknown target type: %s' % target_desc['type']) | 
 |  | 
 |     label_no_toolchain = gn_utils.label_without_toolchain(gn_target_name) | 
 |     target_name_path = gn_utils.label_to_target_name_with_path(label_no_toolchain) | 
 |     target_name = alias_targets.get(label_no_toolchain, target_name_path) | 
 |     target = Target(target_type, target_name, gn_name=label_no_toolchain) | 
 |     target.srcs.update( | 
 |         Label(gn_utils.label_to_path(src)) | 
 |         for src in target_desc.get('sources', []) | 
 |         if src not in self.action_generated_files | 
 |     ) | 
 |  | 
 |     for dep_name in self.resolve_dependencies(gn_target_name): | 
 |       self.apply_module_dependency(target, dep_name) | 
 |  | 
 |     self.build.add_target(target) | 
 |     return target | 
 |  | 
 | def main(): | 
 |   parser = argparse.ArgumentParser( | 
 |       description='Generate BUILD from a GN description.') | 
 |   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-proto', | 
 |       help='Proto BUILD file to create', | 
 |       default=os.path.join(gn_utils.repo_root(), 'protos', '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) | 
 |  | 
 |   build_generator = BuildGenerator(desc) | 
 |   build, proto_build = build_generator.create_build_for_targets( | 
 |       args.targets or default_targets) | 
 |   with open(args.output, 'w') as f: | 
 |     writer = Writer(f) | 
 |     build.write(writer) | 
 |     writer.newline() | 
 |  | 
 |     with open(args.extras, 'r') as r: | 
 |       for line in r: | 
 |         writer.line(line.rstrip("\n\r")) | 
 |  | 
 |   with open(args.output_proto, 'w') as f: | 
 |     proto_build.write(Writer(f)) | 
 |  | 
 |   return 0 | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |   sys.exit(main()) |