• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 itertools
29import json
30import os
31import re
32import sys
33
34import gn_utils
35
36from compat import itervalues, iteritems, basestring
37
38# Arguments for the GN output directory.
39# host_os="linux" is to generate the right build files from Mac OS.
40gn_args = ' '.join([
41    'host_os="linux"',
42    'is_debug=false',
43    'is_perfetto_build_generator=true',
44    'monolithic_binaries=true',
45    'target_os="linux"',
46    'enable_perfetto_heapprofd=false',
47    'enable_perfetto_traced_perf=false',
48    'perfetto_force_dcheck="off"',
49    'enable_perfetto_llvm_demangle=true',
50])
51
52# Default targets to translate to the blueprint file.
53
54# These targets will be exported with public visibility in the generated BUILD.
55public_targets = [
56    '//:libperfetto_client_experimental',
57    '//src/perfetto_cmd:perfetto',
58    '//src/traced/probes:traced_probes',
59    '//src/traced/service:traced',
60    '//src/trace_processor:trace_processor_shell',
61    '//src/trace_processor:trace_processor',
62    '//tools/trace_to_text:trace_to_text',
63    '//tools/trace_to_text:libpprofbuilder',
64]
65
66# These targets are required by internal build rules but don't need to be
67# exported publicly.
68default_targets = [
69    '//src/ipc:perfetto_ipc',
70    '//src/ipc/protoc_plugin:ipc_plugin',
71    '//src/protozero:protozero',
72    '//src/protozero/protoc_plugin:protozero_plugin',
73    '//src/protozero/protoc_plugin:cppgen_plugin',
74    '//test:client_api_example',
75    '//tools/proto_filter:proto_filter',
76    '//tools/proto_merger:proto_merger',
77] + public_targets
78
79# Proto target groups which will be made public.
80proto_groups = {
81    'config': ['//protos/perfetto/config:source_set'],
82    'trace': [
83        '//protos/perfetto/trace:non_minimal_source_set',
84        '//protos/perfetto/trace:minimal_source_set'
85    ],
86    'metrics': ['//protos/perfetto/metrics:source_set',],
87    'chromium': ['//protos/third_party/chromium:source_set',],
88    'chrome_metrics': ['//protos/perfetto/metrics/chrome:source_set',],
89}
90
91# Path for the protobuf sources in the standalone build.
92buildtools_protobuf_src = '//buildtools/protobuf/src'
93
94# The directory where the generated perfetto_build_flags.h will be copied into.
95buildflags_dir = 'include/perfetto/base/build_configs/bazel'
96
97# Internal equivalents for third-party libraries that the upstream project
98# depends on.
99external_deps = {
100    '//gn:default_deps': [],
101    '//gn:jsoncpp': ['PERFETTO_CONFIG.deps.jsoncpp'],
102    '//gn:linenoise': ['PERFETTO_CONFIG.deps.linenoise'],
103    '//gn:protobuf_full': ['PERFETTO_CONFIG.deps.protobuf_full'],
104    '//gn:protobuf_lite': ['PERFETTO_CONFIG.deps.protobuf_lite'],
105    '//gn:protoc_lib': ['PERFETTO_CONFIG.deps.protoc_lib'],
106    '//gn:protoc': ['PERFETTO_CONFIG.deps.protoc'],
107    '//gn:sqlite': [
108        'PERFETTO_CONFIG.deps.sqlite',
109        'PERFETTO_CONFIG.deps.sqlite_ext_percentile'
110    ],
111    '//gn:zlib': ['PERFETTO_CONFIG.deps.zlib'],
112    '//gn:llvm_demangle': ['PERFETTO_CONFIG.deps.llvm_demangle'],
113    '//src/trace_processor:demangle': ['PERFETTO_CONFIG.deps.demangle_wrapper'],
114    '//src/trace_processor/metrics/sql:gen_amalgamated_sql_metrics': [[
115        ':cc_amalgamated_sql_metrics'
116    ]],
117    gn_utils.GEN_VERSION_TARGET: ['PERFETTO_CONFIG.deps.version_header'],
118}
119
120
121def gen_amalgamated_sql_metrics(target):
122  label = BazelLabel(get_bazel_label_name(target.name), 'perfetto_genrule')
123  label.srcs += [re.sub('^//', '', x) for x in sorted(target.inputs)]
124  label.outs += target.outputs
125  label.cmd = r'$(location gen_amalgamated_sql_metrics_py) --cpp_out=$@ $(SRCS)'
126  label.exec_tools += [':gen_amalgamated_sql_metrics_py']
127  return [label]
128
129
130def gen_version_header(target):
131  label = BazelLabel(get_bazel_label_name(target.name), 'perfetto_genrule')
132  label.srcs += [re.sub('^//', '', x) for x in sorted(target.inputs)]
133  label.outs += target.outputs
134  label.cmd = r'$(location gen_version_header_py)'
135  label.cmd += r' --cpp_out=$@ --changelog=$(location CHANGELOG)'
136  label.exec_tools += [':gen_version_header_py']
137  return [label]
138
139
140def gen_cc_metrics_descriptor(target):
141  label = BazelLabel(
142      get_bazel_label_name(target.name), 'perfetto_cc_proto_descriptor')
143  label.deps += [':' + get_bazel_label_name(x) for x in target.proto_deps]
144  label.outs += target.outputs
145  return [label]
146
147
148custom_actions = {
149    gn_utils.GEN_VERSION_TARGET:
150        gen_version_header,
151    '//src/trace_processor/metrics/sql:gen_amalgamated_sql_metrics':
152        gen_amalgamated_sql_metrics,
153}
154
155# ------------------------------------------------------------------------------
156# End of configuration.
157# ------------------------------------------------------------------------------
158
159
160class Error(Exception):
161  pass
162
163
164class BazelLabel(object):
165
166  def __init__(self, name, type):
167    self.comment = None
168    self.name = name
169    self.type = type
170    self.visibility = []
171    self.srcs = []
172    self.hdrs = []
173    self.deps = []
174    self.external_deps = []
175    self.tools = []
176    self.exec_tools = []
177    self.outs = []
178    self.exports = []
179
180  def __lt__(self, other):
181    if isinstance(other, self.__class__):
182      return self.name < other.name
183    raise TypeError(
184        '\'<\' not supported between instances of \'%s\' and \'%s\'' %
185        (type(self).__name__, type(other).__name__))
186
187  def __str__(self):
188    """Converts the object into a Bazel Starlark label."""
189    res = ''
190    res += ('# GN target: %s\n' % self.comment) if self.comment else ''
191    res += '%s(\n' % self.type
192    any_deps = len(self.deps) + len(self.external_deps) > 0
193    ORD = [
194        'name', 'srcs', 'hdrs', 'visibility', 'deps', 'outs', 'cmd', 'tools',
195        'exec_tools', 'exports'
196    ]
197    hasher = lambda x: sum((99,) + tuple(ord(c) for c in x))
198    key_sorter = lambda kv: ORD.index(kv[0]) if kv[0] in ORD else hasher(kv[0])
199    for k, v in sorted(iteritems(self.__dict__), key=key_sorter):
200      if k in ('type', 'comment',
201               'external_deps') or v is None or (v == [] and
202                                                 (k != 'deps' or not any_deps)):
203        continue
204      res += '    %s = ' % k
205      if isinstance(v, basestring):
206        if v.startswith('PERFETTO_CONFIG.'):
207          res += '%s,\n' % v
208        else:
209          res += '"%s",\n' % v
210      elif isinstance(v, bool):
211        res += '%s,\n' % v
212      elif isinstance(v, list):
213        res += '[\n'
214        if k == 'deps' and len(self.external_deps) > 1:
215          indent = '           '
216        else:
217          indent = '    '
218        for entry in sorted(v):
219          if entry.startswith('PERFETTO_CONFIG.'):
220            res += '%s    %s,\n' % (indent, entry)
221          else:
222            res += '%s    "%s",\n' % (indent, entry)
223        res += '%s]' % indent
224        if k == 'deps' and self.external_deps:
225          res += ' + %s' % self.external_deps[0]
226          for edep in self.external_deps[1:]:
227            if isinstance(edep, list):
228              res += ' + [\n'
229              for inner_dep in edep:
230                res += '        "%s",\n' % inner_dep
231              res += '    ]'
232            else:
233              res += ' +\n%s%s' % (indent, edep)
234        res += ',\n'
235      else:
236        raise Error('Unsupported value %s', type(v))
237    res += ')\n\n'
238    return res
239
240
241# Public visibility for targets in Bazel.
242PUBLIC_VISIBILITY = 'PERFETTO_CONFIG.public_visibility'
243
244
245def get_bazel_label_name(gn_name):
246  """Converts a GN target name into a Bazel label name.
247
248  If target is in the public target list, returns only the GN target name,
249  e.g.: //src/ipc:perfetto_ipc -> perfetto_ipc
250
251  Otherwise, in the case of an intermediate target, returns a mangled path.
252  e.g.:  //include/perfetto/base:base -> include_perfetto_base_base.
253  """
254  if gn_name in default_targets:
255    return gn_utils.label_without_toolchain(gn_name).split(':')[1]
256  return gn_utils.label_to_target_name_with_path(gn_name)
257
258
259def get_bazel_proto_sources_label(target_name):
260  """Converts a GN target name into a Bazel proto label name."""
261  return re.sub('_(lite|zero|cpp|ipc|source_set|descriptor)$', '',
262                get_bazel_label_name(target_name)) + '_protos'
263
264
265def gen_proto_label(target):
266  """ Generates the xx_proto_library label for proto targets."""
267  assert (target.type == 'proto_library')
268
269  sources_label_name = get_bazel_proto_sources_label(target.name)
270
271  # For 'source_set' plugins, we don't want to generate any plugin-dependent
272  # targets so just return the label of the proto sources only.
273  if target.proto_plugin == 'source_set':
274    sources_label = BazelLabel(sources_label_name, 'perfetto_proto_library')
275    sources_label.comment = target.name
276    assert (all(x.startswith('//') for x in target.sources))
277    assert (all(x.endswith('.proto') for x in target.sources))
278    sources_label.srcs = sorted([x[2:] for x in target.sources])  # Strip //.
279    sources_label.deps = sorted([
280        ':' + get_bazel_proto_sources_label(x)
281        for x in target.transitive_proto_deps
282    ])
283
284    # In Bazel, proto_paths are not a supported concept becauase strong
285    # dependency checking is enabled. Instead, we need to depend on the target
286    # which includes the proto we want to depend on.
287    # For example, we include the proto_path |buildtools_protobuf_src| because
288    # we want to depend on the "google/protobuf/descriptor.proto" proto file.
289    # This will be exposed by the |protobuf_descriptor_proto| dep.
290    if buildtools_protobuf_src in target.proto_paths:
291      sources_label.external_deps = [
292          'PERFETTO_CONFIG.deps.protobuf_descriptor_proto'
293      ]
294
295    sources_label.visibility = ['PERFETTO_CONFIG.proto_library_visibility']
296
297    sources_label.exports = sorted(
298        [':' + get_bazel_proto_sources_label(d) for d in target.proto_exports])
299    return sources_label
300
301  # For all other types of plugins, we need to generate
302  if target.proto_plugin == 'proto':
303    plugin_label_type = 'perfetto_cc_proto_library'
304  elif target.proto_plugin == 'protozero':
305    plugin_label_type = 'perfetto_cc_protozero_library'
306  elif target.proto_plugin == 'cppgen':
307    plugin_label_type = 'perfetto_cc_protocpp_library'
308  elif target.proto_plugin == 'ipc':
309    plugin_label_type = 'perfetto_cc_ipc_library'
310  elif target.proto_plugin == 'descriptor':
311    plugin_label_type = 'perfetto_proto_descriptor'
312  else:
313    raise Error('Unknown proto plugin: %s' % target.proto_plugin)
314  plugin_label_name = get_bazel_label_name(target.name)
315  plugin_label = BazelLabel(plugin_label_name, plugin_label_type)
316  plugin_label.comment = target.name
317
318  # When using the plugins we need to pass down also the transitive deps.
319  # For instance consider foo.proto including common.proto. The generated
320  # foo.cc will #include "common.gen.h". Hence the generated cc_protocpp_library
321  # rule need to pass down the dependency on the target that generates
322  # common.gen.{cc,h}.
323  if target.proto_plugin in ('cppgen', 'ipc', 'protozero'):
324    plugin_label.deps += [
325        ':' + get_bazel_label_name(x) for x in target.transitive_proto_deps
326    ]
327
328  # Add any dependencies on source_set targets (i.e. targets containing proto
329  # files). For descriptors, we will have an explicit edge between the
330  # descriptor and source set wheras for other plugin types, this edge is
331  # implicit.
332  if target.proto_plugin == 'descriptor':
333    plugin_label.deps += [
334        ':' + get_bazel_proto_sources_label(x) for x in target.proto_deps
335    ]
336  else:
337    plugin_label.deps += [':' + sources_label_name]
338
339  # Since the descriptor generates an explicit output file which can be
340  # referenced by other targets, we specify a name for it.
341  if target.proto_plugin == 'descriptor':
342    plugin_label.outs = [plugin_label_name + '.bin']
343
344  return plugin_label
345
346
347def gen_proto_group_target(gn, name, target_names):
348  # Get a recursive list of the proto_library targets rooted here which
349  # have src.
350  deps_set = set(target_names)
351  for target_name in target_names:
352    target = gn.get_target(target_name)
353    deps_set.update(target.transitive_proto_deps)
354
355  # First, create a root source set target which references all the child
356  # source set targets. We publish this as well as depending on this in all
357  # subsequent targets.
358  sources_label = BazelLabel(name + '_proto', 'perfetto_proto_library')
359  sources_label.deps = [
360      ':' + get_bazel_proto_sources_label(name)
361      for name in sorted(list(deps_set))
362  ]
363  sources_label.visibility = PUBLIC_VISIBILITY
364  sources_label.comment = f'''[{', '.join(target_names)}]'''
365
366  cc_label = BazelLabel(name + '_cc_proto', 'perfetto_cc_proto_library')
367  cc_label.deps = [':' + sources_label.name]
368  cc_label.visibility = PUBLIC_VISIBILITY
369  cc_label.comment = sources_label.comment
370
371  java_label = BazelLabel(name + '_java_proto', 'perfetto_java_proto_library')
372  java_label.deps = [':' + sources_label.name]
373  java_label.visibility = PUBLIC_VISIBILITY
374  java_label.comment = sources_label.comment
375
376  lite_name = name + '_java_proto_lite'
377  java_lite_label = BazelLabel(lite_name, 'perfetto_java_lite_proto_library')
378  java_lite_label.deps = [':' + sources_label.name]
379  java_lite_label.visibility = PUBLIC_VISIBILITY
380  java_lite_label.comment = sources_label.comment
381
382  py_label = BazelLabel(name + '_py_pb2', 'perfetto_py_proto_library')
383  py_label.deps = [':' + sources_label.name]
384  py_label.visibility = PUBLIC_VISIBILITY
385  py_label.comment = sources_label.comment
386
387  return [sources_label, cc_label, java_label, java_lite_label, py_label]
388
389
390def gen_target(gn_target):
391  if gn_target.type == 'proto_library':
392    return [gen_proto_label(gn_target)]
393  elif gn_target.type == 'action':
394    if gn_target.name in custom_actions:
395      return custom_actions[gn_target.name](gn_target)
396    elif re.match('.*gen_cc_.*_descriptor$', gn_target.name):
397      return gen_cc_metrics_descriptor(gn_target)
398    return []
399  elif gn_target.type == 'group':
400    return []
401  elif gn_target.type == 'executable':
402    bazel_type = 'perfetto_cc_binary'
403  elif gn_target.type == 'shared_library':
404    bazel_type = 'perfetto_cc_binary'
405    vars['linkshared'] = True
406  elif gn_target.type == 'static_library':
407    bazel_type = 'perfetto_cc_library'
408  elif gn_target.type == 'source_set':
409    bazel_type = 'perfetto_filegroup'
410  else:
411    raise Error('target type not supported: %s' % gn_target.type)
412
413  label = BazelLabel(get_bazel_label_name(gn_target.name), bazel_type)
414  label.comment = gn_target.name
415
416  # Supporting 'public' on source_sets would require not converting them to
417  # filegroups in bazel.
418  if gn_target.public_headers:
419    if bazel_type == 'perfetto_cc_library':
420      label.hdrs += [x[2:] for x in gn_target.public_headers]
421    else:
422      raise Error('%s: \'public\' currently supported only for cc_library' %
423                  gn_target.name)
424
425  raw_srcs = [x[2:] for x in gn_target.sources]
426  if bazel_type == 'perfetto_cc_library':
427    label.srcs += [x for x in raw_srcs if not x.startswith('include')]
428    label.hdrs += [x for x in raw_srcs if x.startswith('include')]
429
430    # Most Perfetto libraries cannot by dynamically linked as they would
431    # cause ODR violations.
432    label.__dict__['linkstatic'] = True
433  else:
434    label.srcs = raw_srcs
435
436  if gn_target.name in public_targets:
437    label.visibility = ['//visibility:public']
438
439  if gn_target.type in gn_utils.LINKER_UNIT_TYPES:
440    # |source_sets| contains the transitive set of source_set deps.
441    for trans_dep in gn_target.source_set_deps:
442      name = ':' + get_bazel_label_name(trans_dep)
443      if name.startswith(
444          ':include_perfetto_') and gn_target.type != 'executable':
445        label.hdrs += [name]
446      else:
447        label.srcs += [name]
448    for dep in sorted(gn_target.deps):
449      if dep.startswith('//gn:'):
450        assert (dep in external_deps), dep
451      if dep in external_deps:
452        assert (isinstance(external_deps[dep], list))
453        label.external_deps += external_deps[dep]
454      else:
455        label.deps += [':' + get_bazel_label_name(dep)]
456    label.deps += [
457        ':' + get_bazel_label_name(x) for x in gn_target.transitive_proto_deps
458    ]
459
460  # All items starting with : need to be sorted to the end of the list.
461  # However, Python makes specifying a comparator function hard so cheat
462  # instead and make everything start with : sort as if it started with |
463  # As | > all other normal ASCII characters, this will lead to all : targets
464  # starting with : to be sorted to the end.
465  label.srcs = sorted(label.srcs, key=lambda x: x.replace(':', '|'))
466
467  label.deps = sorted(label.deps)
468  label.hdrs = sorted(label.hdrs)
469  return [label]
470
471
472def gen_target_str(gn_target):
473  return ''.join(str(x) for x in gen_target(gn_target))
474
475
476def generate_build(gn_desc, targets, extras):
477  gn = gn_utils.GnParser(gn_desc)
478  project_root = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
479  tool_name = os.path.relpath(os.path.abspath(__file__), project_root)
480  res = '''
481# Copyright (C) 2019 The Android Open Source Project
482#
483# Licensed under the Apache License, Version 2.0 (the "License");
484# you may not use this file except in compliance with the License.
485# You may obtain a copy of the License at
486#
487#      http://www.apache.org/licenses/LICENSE-2.0
488#
489# Unless required by applicable law or agreed to in writing, software
490# distributed under the License is distributed on an "AS IS" BASIS,
491# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
492# See the License for the specific language governing permissions and
493# limitations under the License.
494#
495# This file is automatically generated by {}. Do not edit.
496
497load("@perfetto_cfg//:perfetto_cfg.bzl", "PERFETTO_CONFIG")
498load(
499    "@perfetto//bazel:rules.bzl",
500    "perfetto_build_config_cc_library",
501    "perfetto_cc_binary",
502    "perfetto_filegroup",
503    "perfetto_genrule",
504    "perfetto_cc_ipc_library",
505    "perfetto_cc_library",
506    "perfetto_cc_proto_descriptor",
507    "perfetto_cc_proto_library",
508    "perfetto_cc_protocpp_library",
509    "perfetto_cc_protozero_library",
510    "perfetto_java_proto_library",
511    "perfetto_java_lite_proto_library",
512    "perfetto_proto_library",
513    "perfetto_proto_descriptor",
514    "perfetto_py_binary",
515    "perfetto_py_library",
516    "perfetto_py_proto_library",
517    "perfetto_gensignature_internal_only",
518)
519
520package(default_visibility = ["//visibility:private"])
521
522licenses(["notice"])
523
524exports_files(["NOTICE"])
525
526'''.format(tool_name).lstrip()
527
528  # Public targets need to be computed at the beginning (to figure out the
529  # intermediate deps) but printed at the end (because declaration order matters
530  # in Bazel).
531  public_str = ''
532  for target_name in sorted(public_targets):
533    target = gn.get_target(target_name)
534    public_str += gen_target_str(target)
535
536  res += '''
537# ##############################################################################
538# Internal targets
539# ##############################################################################
540
541'''.lstrip()
542  # Generate the other non-public targets.
543  for target_name in sorted(set(default_targets) - set(public_targets)):
544    target = gn.get_target(target_name)
545    res += gen_target_str(target)
546
547  # Generate all the intermediate targets.
548  for target in sorted(itervalues(gn.all_targets)):
549    if target.name in default_targets or target.name in gn.proto_libs:
550      continue
551    res += gen_target_str(target)
552
553  res += '''
554# ##############################################################################
555# Proto libraries
556# ##############################################################################
557
558'''.lstrip()
559  # Generate targets for proto groups.
560  for l_name, t_names in proto_groups.items():
561    res += ''.join(str(x) for x in gen_proto_group_target(gn, l_name, t_names))
562
563  # For any non-source set and non-descriptor targets, ensure the source set
564  # associated to that target is discovered.
565  for target in sorted(itervalues(gn.all_targets)):
566    plugin = target.proto_plugin
567    if plugin is None or plugin == 'source_set' or plugin == 'descriptor':
568      continue
569    gn.get_target(re.sub('(lite|zero|cpp|ipc)$', 'source_set', target.name))
570
571  # Generate targets for the transitive set of proto targets.
572  labels = [
573      l for target in sorted(itervalues(gn.proto_libs))
574      for l in gen_target(target)
575  ]
576  res += ''.join(str(x) for x in sorted(labels))
577
578  res += '''
579# ##############################################################################
580# Public targets
581# ##############################################################################
582
583'''.lstrip()
584  res += public_str
585  res += '# Content from BUILD.extras\n\n'
586  res += extras
587
588  # Check for ODR violations
589  for target_name in default_targets:
590    checker = gn_utils.ODRChecker(gn, target_name)
591
592  return res
593
594
595def main():
596  parser = argparse.ArgumentParser(
597      description='Generate BUILD from a GN description.')
598  parser.add_argument(
599      '--check-only',
600      help='Don\'t keep the generated files',
601      action='store_true')
602  parser.add_argument(
603      '--desc',
604      help='GN description ' +
605      '(e.g., gn desc out --format=json --all-toolchains "//*"')
606  parser.add_argument(
607      '--repo-root',
608      help='Standalone Perfetto repository to generate a GN description',
609      default=gn_utils.repo_root(),
610  )
611  parser.add_argument(
612      '--extras',
613      help='Extra targets to include at the end of the BUILD file',
614      default=os.path.join(gn_utils.repo_root(), 'BUILD.extras'),
615  )
616  parser.add_argument(
617      '--output',
618      help='BUILD file to create',
619      default=os.path.join(gn_utils.repo_root(), 'BUILD'),
620  )
621  parser.add_argument(
622      '--output-proto',
623      help='Proto BUILD file to create',
624      default=os.path.join(gn_utils.repo_root(), 'protos', 'BUILD'),
625  )
626  parser.add_argument(
627      'targets',
628      nargs=argparse.REMAINDER,
629      help='Targets to include in the BUILD file (e.g., "//:perfetto_tests")')
630  args = parser.parse_args()
631
632  if args.desc:
633    with open(args.desc) as f:
634      desc = json.load(f)
635  else:
636    desc = gn_utils.create_build_description(gn_args, args.repo_root)
637
638  out_files = []
639
640  # Generate the main BUILD file.
641  with open(args.extras, 'r') as extra_f:
642    extras = extra_f.read()
643
644  contents = generate_build(desc, args.targets or default_targets, extras)
645  out_files.append(args.output + '.swp')
646  with open(out_files[-1], 'w') as out_f:
647    out_f.write(contents)
648
649  # Generate the build flags file.
650  out_files.append(os.path.join(buildflags_dir, 'perfetto_build_flags.h.swp'))
651  gn_utils.gen_buildflags(gn_args, out_files[-1])
652
653  return gn_utils.check_or_commit_generated_files(out_files, args.check_only)
654
655
656if __name__ == '__main__':
657  sys.exit(main())
658