• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# Copyright (C) 2017 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# Android.bp file for the Android Soong 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 Android.bp
24# build rules. The dependencies for the GN labels are squashed to the generated
25# Android.bp target, except for actions which get their own genrule. Some
26# libraries are also mapped to their Android equivalents -- see |builtin_deps|.
27
28import argparse
29import json
30import os
31import re
32import sys
33from typing import Dict
34from typing import List
35from typing import Sequence
36from typing import Optional
37
38import gn_utils
39from gn_utils import GnParser
40
41from compat import itervalues
42
43ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
44
45# Arguments for the GN output directory.
46gn_args = ' '.join([
47    'is_debug=false',
48    'is_perfetto_build_generator=true',
49    'perfetto_build_with_android=true',
50    'target_cpu="arm"',
51    'target_os="android"',
52])
53
54# Default targets to translate to the blueprint file.
55default_targets = [
56    '//:libperfetto_client_experimental',
57    '//:libperfetto',
58    '//:perfetto_integrationtests',
59    '//:perfetto_unittests',
60    '//protos/perfetto/trace:perfetto_trace_protos',
61    '//protos/perfetto/trace/android:perfetto_winscope_extensions_zero',
62    '//src/android_internal:libperfetto_android_internal',
63    '//src/base:perfetto_base_default_platform',
64    '//src/shared_lib:libperfetto_c',
65    '//src/perfetto_cmd:perfetto',
66    '//src/perfetto_cmd:trigger_perfetto',
67    '//src/profiling/memory:heapprofd_client',
68    '//src/profiling/memory:heapprofd_client_api',
69    '//src/profiling/memory:heapprofd_api_noop',
70    '//src/profiling/memory:heapprofd',
71    '//src/profiling/memory:heapprofd_standalone_client',
72    '//src/profiling/perf:traced_perf',
73    '//src/traced/probes:traced_probes',
74    '//src/traced/service:traced',
75    '//src/traced_relay:traced_relay',
76    '//src/trace_processor:trace_processor_shell',
77    '//src/trace_redaction:trace_redactor',
78    '//src/java_sdk/main:perfetto_java_sdk_app',
79    '//src/java_sdk/test:perfetto_java_sdk_instrumentation_test',
80    '//src/android_sdk/java/main:perfetto_trace_lib',
81    '//src/android_sdk/java/test:perfetto_trace_instrumentation_test',
82    '//test/cts:perfetto_cts_deps',
83    '//test/cts:perfetto_cts_jni_deps',
84    '//test:perfetto_gtest_logcat_printer',
85    '//test:perfetto_end_to_end_integrationtests',
86    '//test/vts:perfetto_vts_deps',
87]
88
89# Host targets
90ipc_plugin = '//src/ipc/protoc_plugin:ipc_plugin(%s)' % gn_utils.HOST_TOOLCHAIN
91protozero_plugin = '//src/protozero/protoc_plugin:protozero_plugin(%s)' % (
92    gn_utils.HOST_TOOLCHAIN)
93cppgen_plugin = '//src/protozero/protoc_plugin:cppgen_plugin(%s)' % (
94    gn_utils.HOST_TOOLCHAIN)
95
96default_targets += [
97    '//src/traceconv:traceconv(%s)' % gn_utils.HOST_TOOLCHAIN,
98    protozero_plugin,
99    ipc_plugin,
100]
101
102# Defines a custom init_rc argument to be applied to the corresponding output
103# blueprint target.
104target_initrc = {
105    '//src/traced/service:traced': {'perfetto.rc'},
106    '//src/profiling/memory:heapprofd': {'heapprofd.rc'},
107    '//src/profiling/perf:traced_perf': {'traced_perf.rc'},
108}
109
110target_host_supported = [
111    '//:libperfetto',
112    '//:libperfetto_client_experimental',
113    '//protos/perfetto/trace:perfetto_trace_protos',
114    '//protos/perfetto/trace/android:perfetto_winscope_extensions_zero',
115    '//src/shared_lib:libperfetto_c',
116    '//src/trace_processor:demangle',
117    '//src/trace_processor:trace_processor_shell',
118    '//src/traced/probes:traced_probes',
119    '//src/traced/service:traced',
120]
121
122target_vendor_available = [
123    '//:libperfetto_client_experimental',
124    '//src/shared_lib:libperfetto_c',
125]
126
127target_product_available = [
128    '//:libperfetto_client_experimental',
129]
130
131# Proto target groups which will be made public.
132proto_groups = {
133    'trace': {
134        'types': ['lite'],
135        'targets': [
136            '//protos/perfetto/trace:non_minimal_source_set',
137            '//protos/perfetto/trace:minimal_source_set',
138        ]
139    },
140    'winscope': {
141        'types': ['filegroup'],
142        'targets': [
143            '//protos/perfetto/trace:non_minimal_source_set',
144            '//protos/perfetto/trace/android:winscope_extensions_source_set',
145        ]
146    },
147    'config': {
148        'types': ['lite', 'filegroup'],
149        'targets': [
150            '//protos/perfetto/config:source_set',
151        ]
152    },
153    'metrics': {
154        'types': ['python'],
155        'targets': [
156            '//protos/perfetto/metrics:source_set',
157        ]
158    },
159    'trace_summary': {
160        'types': ['filegroup'],
161        'targets': [
162            '//protos/perfetto/trace_summary:source_set',
163        ]
164    }
165}
166
167needs_libfts = [
168    '//:perfetto_unittests',
169    '//src/trace_processor:trace_processor_shell',
170    '//src/traceconv:traceconv',
171]
172
173# All module names are prefixed with this string to avoid collisions.
174module_prefix = 'perfetto_'
175
176# Shared libraries which are directly translated to Android system equivalents.
177shared_library_allowlist = [
178    'android',
179    'android.hardware.atrace@1.0',
180    'android.hardware.health@2.0',
181    'android.hardware.health-V2-ndk',
182    'android.hardware.power.stats@1.0',
183    'android.hardware.power.stats-V1-cpp',
184    'android.system.suspend.control.internal-cpp',
185    'base',
186    'binder',
187    'binder_ndk',
188    'cutils',
189    'hidlbase',
190    'hidltransport',
191    'hwbinder',
192    'incident',
193    'log',
194    'services',
195    'statssocket',
196    'tracingproxy',
197    'utils',
198    'statspull',
199]
200
201# Static libraries which are directly translated to Android system equivalents.
202static_library_allowlist = [
203    'statslog_perfetto',
204]
205
206# Name of the module which settings such as compiler flags for all other
207# modules.
208defaults_module = module_prefix + 'defaults'
209
210# Location of the project in the Android source tree.
211tree_path = 'external/perfetto'
212
213# Path for the protobuf sources in the standalone build.
214buildtools_protobuf_src = '//buildtools/protobuf/src'
215
216# Location of the protobuf src dir in the Android source tree.
217android_protobuf_src = 'external/protobuf/src'
218
219# Compiler flags which are passed through to the blueprint.
220cflag_allowlist = r'^-DPERFETTO.*$'
221
222# Compiler defines which are passed through to the blueprint.
223define_allowlist = r'^(GOOGLE_PROTO.*)|(ZLIB_.*)|(USE_MMAP)$'
224
225# The directory where the generated perfetto_build_flags.h will be copied into.
226buildflags_dir = 'include/perfetto/base/build_configs/android_tree'
227
228def enumerate_data_deps():
229  with open(os.path.join(ROOT_DIR, 'tools', 'test_data.txt')) as f:
230    lines = f.readlines()
231  for line in (line.strip() for line in lines if not line.startswith('#')):
232    assert os.path.exists(line), 'file %s should exist' % line
233    if line.startswith('test/data/'):
234      # Skip test data files that require GCS. They are only for benchmarks.
235      # We don't run benchmarks in the android tree.
236      continue
237    if line.endswith('/.'):
238      yield line[:-1] + '**/*'
239    else:
240      yield line
241
242
243# Additional arguments to apply to Android.bp rules.
244additional_args = {
245    'heapprofd_client_api': [
246        ('static_libs', {'libasync_safe'}),
247        # heapprofd_client_api MUST NOT have global constructors. Because it
248        # is loaded in an __attribute__((constructor)) of libc, we cannot
249        # guarantee that the global constructors get run before it is used.
250        ('cflags', {'-Wglobal-constructors', '-Werror=global-constructors'}),
251        ('version_script', 'src/profiling/memory/heapprofd_client_api.map.txt'),
252        ('stubs', {
253            'versions': ['S'],
254            'symbol_file': 'src/profiling/memory/heapprofd_client_api.map.txt',
255        }),
256        ('export_include_dirs', {'src/profiling/memory/include'}),
257    ],
258    'heapprofd_api_noop': [
259        ('version_script', 'src/profiling/memory/heapprofd_client_api.map.txt'),
260        ('stubs', {
261            'versions': ['S'],
262            'symbol_file': 'src/profiling/memory/heapprofd_client_api.map.txt',
263        }),
264        ('export_include_dirs', {'src/profiling/memory/include'}),
265    ],
266    'heapprofd_client': [
267        ('include_dirs', {'bionic/libc'}),
268        ('static_libs', {'libasync_safe'}),
269    ],
270    'heapprofd_standalone_client': [
271        ('static_libs', {'libasync_safe'}),
272        ('version_script', 'src/profiling/memory/heapprofd_client_api.map.txt'),
273        ('export_include_dirs', {'src/profiling/memory/include'}),
274        ('stl', 'libc++_static'),
275    ],
276    'perfetto_unittests': [
277        ('data', set(enumerate_data_deps())),
278        ('include_dirs', {'bionic/libc/kernel'}),
279    ],
280    'perfetto_integrationtests': [
281        ('test_suites', {'general-tests'}),
282        ('test_config', 'PerfettoIntegrationTests.xml'),
283    ],
284    'libperfetto_android_internal': [('static_libs', {'libhealthhalutils'}),],
285    'trace_processor_shell': [
286        ('strip', {
287            'all': True
288        }),
289        ('host', {
290            'stl': 'libc++_static',
291            'dist': {
292                'targets': ['sdk_repo']
293            },
294        }),
295    ],
296    'libperfetto_client_experimental': [
297        ('apex_available', {
298            '//apex_available:platform', '//apex_available:anyapex'
299        }),
300        ('min_sdk_version', '30'),
301        ('shared_libs', {'liblog'}),
302        ('export_include_dirs', {'include', buildflags_dir}),
303    ],
304    'libperfetto_c': [
305        ('min_sdk_version', '30'),
306        ('export_include_dirs', {'include'}),
307        ('cflags', {'-DPERFETTO_SHLIB_SDK_IMPLEMENTATION'}),
308    ],
309    'perfetto_trace_protos': [
310        ('apex_available', {
311            '//apex_available:platform', 'com.android.art',
312            'com.android.art.debug'
313        }),
314        ('min_sdk_version', 'S'),
315    ],
316    'libperfetto': [('export_include_dirs', {'include', buildflags_dir}),],
317    'perfetto': [('required', {'perfetto_persistent_cfg.pbtxt'}),],
318    'trace_redactor': [
319        ('min_sdk_version', '35'),
320        ('apex_available', {
321            '//apex_available:platform',
322            'com.android.profiling'
323        }),
324    ],
325    # TODO(ktimofeev): rename android.bp target
326    'perfetto_src_android_sdk_java_test_perfetto_trace_test_lib': [
327        ('static_libs', {'perfetto_trace_java_protos'})],
328}
329
330
331def enable_base_platform(module):
332  module.srcs.add(':perfetto_base_default_platform')
333
334
335def enable_gtest_and_gmock(module):
336  module.static_libs.add('libgmock')
337  module.static_libs.add('libgtest')
338  if module.name != 'perfetto_gtest_logcat_printer':
339    module.whole_static_libs.add('perfetto_gtest_logcat_printer')
340
341
342def enable_protobuf_full(module):
343  if module.type == 'cc_binary_host':
344    module.static_libs.add('libprotobuf-cpp-full')
345  elif module.host_supported:
346    module.host.static_libs.add('libprotobuf-cpp-full')
347    module.android.shared_libs.add('libprotobuf-cpp-full')
348  else:
349    module.shared_libs.add('libprotobuf-cpp-full')
350
351
352def enable_protobuf_lite(module):
353  module.shared_libs.add('libprotobuf-cpp-lite')
354
355
356def enable_protoc_lib(module):
357  if module.type == 'cc_binary_host':
358    module.static_libs.add('libprotoc')
359  else:
360    module.shared_libs.add('libprotoc')
361
362
363def enable_libunwindstack(module):
364  if module.name != 'heapprofd_standalone_client':
365    module.shared_libs.add('libunwindstack')
366    module.shared_libs.add('libprocinfo')
367    module.shared_libs.add('libbase')
368  else:
369    module.static_libs.add('libunwindstack')
370    module.static_libs.add('libprocinfo')
371    module.static_libs.add('libbase')
372    module.static_libs.add('liblzma')
373    module.static_libs.add('libdexfile_support')
374    module.runtime_libs.add('libdexfile')  # libdexfile_support dependency
375    module.shared_libs.add('libz') # libunwindstack dependency
376
377
378def enable_libunwind(module):
379  # libunwind is disabled on Darwin so we cannot depend on it.
380  pass
381
382
383def enable_sqlite(module):
384  if module.type == 'cc_binary_host':
385    module.static_libs.add('libsqlite_static_noicu')
386    module.static_libs.add('sqlite_ext_percentile')
387  elif module.host_supported:
388    # Copy what the sqlite3 command line tool does.
389    module.android.shared_libs.add('libsqlite')
390    module.android.shared_libs.add('libicu')
391    module.android.shared_libs.add('liblog')
392    module.android.shared_libs.add('libutils')
393    module.android.static_libs.add('sqlite_ext_percentile')
394    module.host.static_libs.add('libsqlite_static_noicu')
395    module.host.static_libs.add('sqlite_ext_percentile')
396  else:
397    module.shared_libs.add('libsqlite')
398    module.shared_libs.add('libicu')
399    module.shared_libs.add('liblog')
400    module.shared_libs.add('libutils')
401    module.static_libs.add('sqlite_ext_percentile')
402
403
404def enable_zlib(module):
405  if module.type == 'cc_binary_host':
406    module.static_libs.add('libz')
407  elif module.host_supported:
408    module.android.shared_libs.add('libz')
409    module.host.static_libs.add('libz')
410  else:
411    module.shared_libs.add('libz')
412
413
414def enable_expat(module):
415  if module.type == 'cc_binary_host':
416    module.static_libs.add('libexpat')
417  elif module.host_supported:
418    module.android.shared_libs.add('libexpat')
419    module.host.static_libs.add('libexpat')
420  else:
421    module.shared_libs.add('libexpat')
422
423
424def enable_uapi_headers(module):
425  module.include_dirs.add('bionic/libc/kernel')
426
427
428def enable_bionic_libc_platform_headers_on_android(module):
429  module.header_libs.add('bionic_libc_platform_headers')
430
431
432def enable_android_test_common(module):
433  module.static_libs.add('junit')
434  module.static_libs.add('truth')
435  module.static_libs.add('androidx.test.runner')
436  module.static_libs.add('androidx.test.ext.junit')
437
438# Android equivalents for third-party libraries that the upstream project
439# depends on.
440builtin_deps = {
441    '//gn:default_deps':
442        lambda x: None,
443    '//gn:gtest_main':
444        lambda x: None,
445    '//gn:protoc':
446        lambda x: None,
447    '//gn:base_platform':
448        enable_base_platform,
449    '//gn:gtest_and_gmock':
450        enable_gtest_and_gmock,
451    '//gn:libunwind':
452        enable_libunwind,
453    '//gn:protobuf_full':
454        enable_protobuf_full,
455    '//gn:protobuf_lite':
456        enable_protobuf_lite,
457    '//gn:protoc_lib':
458        enable_protoc_lib,
459    '//gn:libunwindstack':
460        enable_libunwindstack,
461    '//gn:sqlite':
462        enable_sqlite,
463    '//gn:zlib':
464        enable_zlib,
465    '//gn:expat':
466        enable_expat,
467    '//gn:bionic_kernel_uapi_headers':
468        enable_uapi_headers,
469    '//src/profiling/memory:bionic_libc_platform_headers_on_android':
470        enable_bionic_libc_platform_headers_on_android,
471    '//gn:android_test_common':
472      enable_android_test_common,
473}
474
475# ----------------------------------------------------------------------------
476# End of configuration.
477# ----------------------------------------------------------------------------
478
479
480class Error(Exception):
481  pass
482
483
484class ThrowingArgumentParser(argparse.ArgumentParser):
485
486  def __init__(self, context):
487    super(ThrowingArgumentParser, self).__init__()
488    self.context = context
489
490  def error(self, message):
491    raise Error('%s: %s' % (self.context, message))
492
493
494def write_blueprint_key_value(output, name, value, sort=True):
495  """Writes a Blueprint key-value pair to the output"""
496
497  if isinstance(value, bool):
498    if value:
499      output.append('    %s: true,' % name)
500    else:
501      output.append('    %s: false,' % name)
502    return
503  if not value:
504    return
505  if isinstance(value, set):
506    value = sorted(value)
507  if isinstance(value, list):
508    output.append('    %s: [' % name)
509    for item in sorted(value) if sort else value:
510      output.append('        "%s",' % item)
511    output.append('    ],')
512    return
513  if isinstance(value, Target):
514    value.to_string(output)
515    return
516  if isinstance(value, dict):
517    kv_output = []
518    for k, v in value.items():
519      write_blueprint_key_value(kv_output, k, v)
520
521    output.append('    %s: {' % name)
522    for line in kv_output:
523      output.append('    %s' % line)
524    output.append('    },')
525    return
526  output.append('    %s: "%s",' % (name, value))
527
528
529class Target(object):
530  """A target-scoped part of a module"""
531
532  def __init__(self, name):
533    self.name = name
534    self.shared_libs = set()
535    self.static_libs = set()
536    self.whole_static_libs = set()
537    self.cflags = set()
538    self.dist = dict()
539    self.strip = dict()
540    self.stl = None
541
542  def to_string(self, output):
543    nested_out = []
544    self._output_field(nested_out, 'shared_libs')
545    self._output_field(nested_out, 'static_libs')
546    self._output_field(nested_out, 'whole_static_libs')
547    self._output_field(nested_out, 'cflags')
548    self._output_field(nested_out, 'stl')
549    self._output_field(nested_out, 'dist')
550    self._output_field(nested_out, 'strip')
551
552    if nested_out:
553      output.append('    %s: {' % self.name)
554      for line in nested_out:
555        output.append('    %s' % line)
556      output.append('    },')
557
558  def _output_field(self, output, name, sort=True):
559    value = getattr(self, name)
560    return write_blueprint_key_value(output, name, value, sort)
561
562
563class Module(object):
564  """A single module (e.g., cc_binary, cc_test) in a blueprint."""
565
566  def __init__(self, mod_type, name, gn_target):
567    assert (gn_target)
568    self.type = mod_type
569    self.gn_target = gn_target
570    self.name = name
571    self.srcs = set()
572    self.main: Optional[str] = None
573    self.comment = 'GN: ' + gn_utils.label_without_toolchain(gn_target)
574    self.shared_libs = set()
575    self.static_libs = set()
576    self.whole_static_libs = set()
577    self.runtime_libs = set()
578    self.tools = set()
579    self.cmd: Optional[str] = None
580    self.host_supported = False
581    self.vendor_available = False
582    self.product_available = False
583    self.init_rc = set()
584    self.out = set()
585    self.export_include_dirs = set()
586    self.generated_headers = set()
587    self.export_generated_headers = set()
588    self.defaults = set()
589    self.cflags = set()
590    self.include_dirs = set()
591    self.header_libs = set()
592    self.required = set()
593    self.user_debug_flag = False
594    self.tool_files: Optional[List[str]] = None
595    self.android = Target('android')
596    self.host = Target('host')
597    self.musl = Target('musl')
598    self.lto: Optional[bool] = None
599    self.stl = None
600    self.dist = dict()
601    self.strip = dict()
602    self.data = set()
603    self.apex_available = set()
604    self.sdk_version = None
605    self.min_sdk_version = None
606    self.proto = dict()
607    self.output_extension: Optional[str] = None
608    # The genrule_XXX below are properties that must to be propagated back
609    # on the module(s) that depend on the genrule.
610    self.genrule_headers = set()
611    self.genrule_srcs = set()
612    self.genrule_shared_libs = set()
613    self.version_script = None
614    self.test_suites = set()
615    self.test_config = None
616    self.stubs = {}
617    self.manifest: Optional[str] = None
618    self.resource_dirs: Optional[List[str]] = None
619    self.jni_libs: Optional[List[str]] = None
620    self.jni_uses_platform_apis: Optional[bool] = None
621
622  def to_string(self, output):
623    if self.comment:
624      output.append('// %s' % self.comment)
625    output.append('%s {' % self.type)
626    self._output_field(output, 'name')
627    self._output_field(output, 'srcs')
628    self._output_field(output, 'resource_dirs')
629    self._output_field(output, 'manifest')
630    self._output_field(output, 'shared_libs')
631    self._output_field(output, 'static_libs')
632    self._output_field(output, 'whole_static_libs')
633    self._output_field(output, 'runtime_libs')
634    self._output_field(output, 'tools')
635    self._output_field(output, 'cmd', sort=False)
636    if self.host_supported:
637      self._output_field(output, 'host_supported')
638    if self.vendor_available:
639      self._output_field(output, 'vendor_available')
640    if self.product_available:
641      self._output_field(output, 'product_available')
642    self._output_field(output, 'init_rc')
643    self._output_field(output, 'out')
644    self._output_field(output, 'export_include_dirs')
645    self._output_field(output, 'generated_headers')
646    self._output_field(output, 'export_generated_headers')
647    self._output_field(output, 'defaults')
648    self._output_field(output, 'cflags')
649    self._output_field(output, 'include_dirs')
650    self._output_field(output, 'header_libs')
651    self._output_field(output, 'required')
652    self._output_field(output, 'dist')
653    self._output_field(output, 'strip')
654    self._output_field(output, 'tool_files')
655    self._output_field(output, 'data')
656    self._output_field(output, 'stl')
657    self._output_field(output, 'apex_available')
658    self._output_field(output, 'sdk_version')
659    self._output_field(output, 'min_sdk_version')
660    self._output_field(output, 'version_script')
661    self._output_field(output, 'test_suites')
662    self._output_field(output, 'test_config')
663    self._output_field(output, 'stubs')
664    self._output_field(output, 'proto')
665    self._output_field(output, 'main')
666    self._output_field(output, 'output_extension')
667    self._output_field(output, 'jni_libs')
668    self._output_field(output, 'jni_uses_platform_apis')
669
670    target_out = []
671    self._output_field(target_out, 'android')
672    self._output_field(target_out, 'host')
673    self._output_field(target_out, 'musl')
674    if target_out:
675      output.append('    target: {')
676      for line in target_out:
677        output.append('    %s' % line)
678      output.append('    },')
679
680    if self.user_debug_flag:
681      output.append('    product_variables: {')
682      output.append('        debuggable: {')
683      output.append(
684          '            cflags: ["-DPERFETTO_BUILD_WITH_ANDROID_USERDEBUG"],')
685      output.append('        },')
686      output.append('    },')
687    if self.lto is not None:
688      output.append('    target: {')
689      output.append('        android: {')
690      output.append('            lto: {')
691      output.append('                thin: %s,' %
692                    'true' if self.lto else 'false')
693      output.append('            },')
694      output.append('        },')
695      output.append('    },')
696    output.append('}')
697    output.append('')
698
699  def add_android_static_lib(self, lib):
700    if self.type == 'cc_binary_host':
701      raise Exception('Adding Android static lib for host tool is unsupported')
702    elif self.host_supported:
703      self.android.static_libs.add(lib)
704    else:
705      self.static_libs.add(lib)
706
707  def add_android_shared_lib(self, lib):
708    if self.type == 'cc_binary_host':
709      raise Exception('Adding Android shared lib for host tool is unsupported')
710    elif self.host_supported:
711      self.android.shared_libs.add(lib)
712    else:
713      self.shared_libs.add(lib)
714
715  def _output_field(self, output, name, sort=True):
716    value = getattr(self, name)
717    return write_blueprint_key_value(output, name, value, sort)
718
719
720class Blueprint(object):
721  """In-memory representation of an Android.bp file."""
722
723  def __init__(self):
724    self.modules: Dict[str, Module] = {}
725    self.gn_target_to_module: Dict[str, Module] = {}
726
727  def add_module(self, module: Module):
728    """Adds a new module to the blueprint, replacing any existing module
729        with the same name.
730
731        Args:
732            module: Module instance.
733        """
734    self.modules[module.name] = module
735    self.gn_target_to_module[module.gn_target] = module
736
737  def to_string(self, output):
738    for m in sorted(itervalues(self.modules), key=lambda m: m.name):
739      m.to_string(output)
740
741
742def label_to_module_name(label: str):
743  """Turn a GN label (e.g., //:perfetto_tests) into a module name."""
744  # If the label is explicibly listed in the default target list, don't prefix
745  # its name and return just the target name. This is so tools like
746  # "traceconv" stay as such in the Android tree.
747  label_without_toolchain = gn_utils.label_without_toolchain(label)
748  if label in default_targets or label_without_toolchain in default_targets:
749    return label_without_toolchain.split(':')[-1]
750
751  module = re.sub(r'^//:?', '', label_without_toolchain)
752  module = re.sub(r'[^a-zA-Z0-9_]', '_', module)
753  if not module.startswith(module_prefix):
754    return module_prefix + module
755  return module
756
757
758def is_supported_source_file(name: str):
759  """Returns True if |name| can appear in a 'srcs' list."""
760  return os.path.splitext(name)[1] in ['.c', '.cc', '.java', '.proto']
761
762
763def create_proto_modules(blueprint: Blueprint, gn: GnParser,
764                         target: GnParser.Target):
765  """Generate genrules for a proto GN target.
766
767    GN actions are used to dynamically generate files during the build. The
768    Soong equivalent is a genrule. This function turns a specific kind of
769    genrule which turns .proto files into source and header files into a pair
770    equivalent genrules.
771
772    Args:
773        blueprint: Blueprint instance which is being generated.
774        target: gn_utils.Target object.
775
776    Returns:
777        The source_genrule module.
778    """
779  assert (target.type == 'proto_library')
780
781  # We don't generate any targets for source_set proto modules because
782  # they will be inlined into other modules if required.
783  if target.proto_plugin == 'source_set':
784    return None
785
786  tools = {'aprotoc'}
787  cpp_out_dir = '$(genDir)/%s/' % tree_path
788  target_module_name = label_to_module_name(target.name)
789
790  # In GN builds the proto path is always relative to the output directory
791  # (out/tmp.xxx).
792  cmd = ['mkdir -p %s &&' % cpp_out_dir, '$(location aprotoc)']
793  cmd += ['--proto_path=%s' % tree_path]
794
795  tool_files = set()
796  if buildtools_protobuf_src in target.proto_paths:
797    cmd += ['--proto_path=%s' % android_protobuf_src]
798    # Add `google/protobuf/descriptor.proto` to implicit deps
799    tool_files.add(':libprotobuf-internal-descriptor-proto')
800
801  # Descriptor targets only generate a single target.
802  if target.proto_plugin == 'descriptor':
803    out = '{}.bin'.format(target_module_name)
804
805    cmd += ['--descriptor_set_out=$(out)']
806    cmd += ['$(in)']
807
808    descriptor_module = Module('genrule', target_module_name, target.name)
809    descriptor_module.cmd = ' '.join(cmd)
810    descriptor_module.out.add(out)
811    descriptor_module.tools = tools
812    blueprint.add_module(descriptor_module)
813
814    # Recursively extract the .proto files of all the dependencies and
815    # add them to srcs.
816    descriptor_module.srcs.update(
817        gn_utils.label_to_path(src) for src in target.sources)
818    # Add the tool_files to srcs so that they get copied if this action is
819    # sandboxed in Soong.
820    # Add to `srcs` instead of `tool_files` (the latter will require a
821    # --proto_path that depends on Soong's sandbox implementation.)
822    descriptor_module.srcs.update(
823        src for src in tool_files)
824    for dep in target.transitive_proto_deps():
825      current_target = gn.get_target(dep.name)
826      descriptor_module.srcs.update(
827          gn_utils.label_to_path(src) for src in current_target.sources)
828
829    return descriptor_module
830
831  # We create two genrules for each proto target: one for the headers and
832  # another for the sources. This is because the module that depends on the
833  # generated files needs to declare two different types of dependencies --
834  # source files in 'srcs' and headers in 'generated_headers' -- and it's not
835  # valid to generate .h files from a source dependency and vice versa.
836  #
837  # We create an additional filegroup for .proto
838  # The .proto filegroup will be added to `tool_files` of rdeps so that the
839  # genrules can be sandboxed.
840
841  for proto_dep in target.proto_deps().union(target.transitive_proto_deps()):
842    tool_files.add(":" + label_to_module_name(proto_dep.name))
843
844  filegroup_module = Module('filegroup', target_module_name, target.name)
845  filegroup_module.srcs.update(
846      gn_utils.label_to_path(src) for src in target.sources)
847  blueprint.add_module(filegroup_module)
848
849  source_module_name = target_module_name + '_gen'
850  source_module = Module('genrule', source_module_name, target.name)
851  # Add the "root" .proto filegroup to srcs
852  source_module.srcs = set([':' + target_module_name])
853  # Add the tool_files to srcs so that they get copied if this action is
854  # sandboxed in Soong.
855  # Add to `srcs` instead of `tool_files` (the latter will require a
856  # --proto_path that depends on Soong's sandbox implementation.)
857  source_module.srcs.update(
858      src for src in tool_files)
859  blueprint.add_module(source_module)
860
861  header_module = Module('genrule', source_module_name + '_headers',
862                         target.name)
863  blueprint.add_module(header_module)
864  header_module.srcs = set(source_module.srcs)
865
866  # TODO(primiano): at some point we should remove this. This was introduced
867  # by aosp/1108421 when adding "protos/" to .proto include paths, in order to
868  # avoid doing multi-repo changes and allow old clients in the android tree
869  # to still do the old #include "perfetto/..." rather than
870  # #include "protos/perfetto/...".
871  header_module.export_include_dirs = {'.', 'protos'}
872
873  source_module.genrule_srcs.add(':' + source_module.name)
874  source_module.genrule_headers.add(header_module.name)
875
876  if target.proto_plugin == 'proto':
877    suffixes = ['pb']
878    source_module.genrule_shared_libs.add('libprotobuf-cpp-lite')
879    cmd += ['--cpp_out=lite=true:' + cpp_out_dir]
880  elif target.proto_plugin == 'protozero':
881    suffixes = ['pbzero']
882    plugin = create_modules_from_target(blueprint, gn, protozero_plugin)
883    assert (plugin)
884    tools.add(plugin.name)
885    cmd += ['--plugin=protoc-gen-plugin=$(location %s)' % plugin.name]
886    cmd += ['--plugin_out=wrapper_namespace=pbzero:' + cpp_out_dir]
887  elif target.proto_plugin == 'cppgen':
888    suffixes = ['gen']
889    plugin = create_modules_from_target(blueprint, gn, cppgen_plugin)
890    assert (plugin)
891    tools.add(plugin.name)
892    cmd += ['--plugin=protoc-gen-plugin=$(location %s)' % plugin.name]
893    cmd += ['--plugin_out=wrapper_namespace=gen:' + cpp_out_dir]
894  elif target.proto_plugin == 'ipc':
895    suffixes = ['ipc']
896    plugin = create_modules_from_target(blueprint, gn, ipc_plugin)
897    assert (plugin)
898    tools.add(plugin.name)
899    cmd += ['--plugin=protoc-gen-plugin=$(location %s)' % plugin.name]
900    cmd += ['--plugin_out=wrapper_namespace=gen:' + cpp_out_dir]
901  else:
902    raise Error('Unsupported proto plugin: %s' % target.proto_plugin)
903
904  cmd += ['$(locations :%s)' % target_module_name]
905  source_module.cmd = ' '.join(cmd)
906  header_module.cmd = source_module.cmd
907  source_module.tools = tools
908  header_module.tools = tools
909
910
911  for sfx in suffixes:
912    source_module.out.update('%s/%s' %
913                             (tree_path, src.replace('.proto', '.%s.cc' % sfx))
914                             for src in filegroup_module.srcs)
915    header_module.out.update('%s/%s' %
916                             (tree_path, src.replace('.proto', '.%s.h' % sfx))
917                             for src in filegroup_module.srcs)
918  return source_module
919
920
921def create_tp_tables_module(blueprint: Blueprint, gn: GnParser,
922                            target: GnParser.Target):
923  bp_module_name = label_to_module_name(target.name)
924  bp_binary_module_name = f'{bp_module_name}_binary'
925  srcs = [gn_utils.label_to_path(src) for src in target.sources]
926
927  binary_module = Module('python_binary_host', bp_binary_module_name,
928                         target.name)
929  for dep in target.non_proto_or_source_set_deps():
930    binary_module.srcs.update([
931        gn_utils.label_to_path(src) for src in gn.get_target(dep.name).sources
932    ])
933  binary_module.srcs.update(srcs)
934  binary_module.srcs.add('tools/gen_tp_table_headers.py')
935  binary_module.main = 'tools/gen_tp_table_headers.py'
936  blueprint.add_module(binary_module)
937
938  module = Module('genrule', bp_module_name, target.name)
939  module.tools = set([
940      bp_binary_module_name,
941  ])
942  module.cmd = ' '.join([
943      f'$(location {bp_binary_module_name})',
944      '--gen-dir=$(genDir)',
945      '--relative-input-dir=external/perfetto',
946      '--inputs',
947      '$(in)',
948  ])
949  module.out.update(target.outputs)
950  module.genrule_headers.add(module.name)
951  module.srcs.update(srcs)
952  blueprint.add_module(module)
953
954  return module
955
956
957def create_amalgamated_sql_module(blueprint: Blueprint, gn: GnParser,
958                                  target: GnParser.Target):
959  bp_module_name = label_to_module_name(target.name)
960
961  def find_arg(name):
962    for i, arg in enumerate(target.args):
963      if arg.startswith(f'--{name}'):
964        return target.args[i + 1]
965
966  namespace = find_arg('namespace')
967
968  module = Module('genrule', bp_module_name, target.name)
969  module.tool_files = [
970      'tools/gen_amalgamated_sql.py',
971  ]
972  module.cmd = ' '.join([
973      '$(location tools/gen_amalgamated_sql.py)',
974      f'--namespace={namespace}',
975      '--cpp-out=$(out)',
976      '$(in)',
977  ])
978  module.genrule_headers.add(module.name)
979  module.out.update(target.outputs)
980
981  for dep in target.transitive_deps:
982    # Use globs for the chrome stdlib so the file does not need to be
983    # regenerated in autoroll CLs.
984    if dep.name.startswith(
985          '//src/trace_processor/perfetto_sql/stdlib/chrome:chrome_sql'):
986      module.srcs.add('src/trace_processor/perfetto_sql/stdlib/chrome/**/*.sql')
987      continue
988    module.srcs.update(
989        [gn_utils.label_to_path(src) for src in gn.get_target(dep.name).inputs])
990  blueprint.add_module(module)
991  return module
992
993
994def create_cc_proto_descriptor_module(blueprint: Blueprint,
995                                      target: GnParser.Target):
996  bp_module_name = label_to_module_name(target.name)
997  module = Module('genrule', bp_module_name, target.name)
998  module.tool_files = [
999      'tools/gen_cc_proto_descriptor.py',
1000  ]
1001  module.cmd = ' '.join([
1002      '$(location tools/gen_cc_proto_descriptor.py)', '--gen_dir=$(genDir)',
1003      '--cpp_out=$(out)', '$(in)'
1004  ])
1005  module.genrule_headers.add(module.name)
1006  module.srcs.update(
1007      ':' + label_to_module_name(dep.name) for dep in target.proto_deps())
1008  module.srcs.update(
1009      gn_utils.label_to_path(src)
1010      for src in target.inputs
1011      if "tmp.gn_utils" not in src)
1012  module.out.update(target.outputs)
1013  blueprint.add_module(module)
1014  return module
1015
1016
1017def create_gen_version_module(blueprint: Blueprint, target: GnParser.Target,
1018                              bp_module_name: str):
1019  module = Module('genrule', bp_module_name, gn_utils.GEN_VERSION_TARGET)
1020  script_path = gn_utils.label_to_path(target.script)
1021  module.genrule_headers.add(bp_module_name)
1022  module.tool_files = [script_path]
1023  module.out.update(target.outputs)
1024  module.srcs.update(gn_utils.label_to_path(src) for src in target.inputs)
1025  module.cmd = ' '.join([
1026      'python3 $(location %s)' % script_path, '--no_git',
1027      '--changelog=$(location CHANGELOG)', '--cpp_out=$(out)'
1028  ])
1029  blueprint.add_module(module)
1030  return module
1031
1032
1033def create_proto_group_modules(blueprint, gn: GnParser, module_name: str,
1034                               group):
1035  target_names = group['targets']
1036  module_types = group['types']
1037  module_sources = set()
1038
1039  for name in target_names:
1040    target = gn.get_target(name)
1041    module_sources.update(gn_utils.label_to_path(src) for src in target.sources)
1042    for dep_label in target.transitive_proto_deps():
1043      dep = gn.get_target(dep_label.name)
1044      module_sources.update(gn_utils.label_to_path(src) for src in dep.sources)
1045
1046  for type in module_types:
1047    if type == 'filegroup':
1048      name = label_to_module_name(module_name) + '_filegroup_proto'
1049      module = Module('filegroup', name, name)
1050      module.comment = f'''GN: [{', '.join(target_names)}]'''
1051      module.srcs = module_sources
1052      blueprint.add_module(module)
1053    elif type == 'lite':
1054      name = label_to_module_name(module_name) + '_java_protos'
1055      module = Module('java_library', name, name)
1056      module.comment = f'''GN: [{', '.join(target_names)}]'''
1057      module.proto = {'type': 'lite', 'canonical_path_from_root': False}
1058      module.srcs = module_sources
1059      blueprint.add_module(module)
1060    elif type == 'python':
1061      name = label_to_module_name(module_name) + '_python_protos'
1062      module = Module('python_library_host', name, name)
1063      module.comment = f'''GN: [{', '.join(target_names)}]'''
1064      module.proto = {'canonical_path_from_root': False}
1065      module.srcs = module_sources
1066      blueprint.add_module(module)
1067    else:
1068      raise Error('Unhandled proto group type: {}'.format(group.type))
1069
1070
1071def is_target_android_jni_lib(target: GnParser.Target):
1072  custom_target_type = target.custom_target_type()
1073  return custom_target_type == 'perfetto_android_jni_library'
1074
1075
1076def android_jni_lib_custom_target_name(target: GnParser.Target):
1077  """
1078  Android.bp 'cc_library_shared' rule generates binary with the name equal to
1079  the rule name (plus the ".so" suffix).
1080  So we change the target name to get the JNI library with the expected name.
1081  """
1082  assert is_target_android_jni_lib(target)
1083  return target.binary_name().rstrip('.so')
1084
1085
1086class AndroidJavaSDKModulesGenerator:
1087  """
1088  This code is split into its own class to hide implementation details.
1089  """
1090
1091  def __init__(self, blueprint: Blueprint, gn: GnParser):
1092    self.blueprint = blueprint
1093    self.gn = gn
1094
1095  _SDK_VERSION = 35
1096
1097  def create_android_app_module(self, target: GnParser.Target,
1098      bp_module_name: str):
1099    module = Module('android_app', bp_module_name, target.name)
1100    module.srcs = [gn_utils.label_to_path(src) for src in target.sources]
1101
1102    self._generate_deps_modules_and_add_non_jni_lib_deps(module, target)
1103
1104    module.jni_libs = self._collect_jni_lib_deps([target])
1105
1106    module.manifest = gn_utils.label_to_path(target.manifest)
1107    module.resource_dirs = [
1108        gn_utils.label_to_path(target.resource_files.rstrip('/**/*'))]
1109    module.jni_uses_platform_apis = True
1110    module.sdk_version = self._SDK_VERSION
1111    self.blueprint.add_module(module)
1112    return module
1113
1114  def create_android_library_module(self, target: GnParser.Target,
1115      bp_module_name: str):
1116    module = Module('android_library', bp_module_name, target.name)
1117    module.srcs = [gn_utils.label_to_path(src) for src in target.sources]
1118
1119    # All JNI libs will be added to the 'jni_libs' argument of the 'android_app'
1120    # or 'android_test' rule that depends on this 'android_library' rule.
1121    self._generate_deps_modules_and_add_non_jni_lib_deps(module, target)
1122
1123    module.manifest = gn_utils.label_to_path(target.manifest)
1124
1125    module.sdk_version = self._SDK_VERSION
1126
1127    _add_additional_args(module)
1128
1129    self.blueprint.add_module(module)
1130    return module
1131
1132  def create_android_test_module(self, target: GnParser.Target,
1133      bp_module_name: str):
1134    module = Module('android_test', bp_module_name, target.name)
1135
1136    android_libs = self.gn.get_target(
1137      target.a_i_t_app).non_proto_or_source_set_deps()
1138    test_android_libs = self.gn.get_target(
1139      target.a_i_t_test_app).non_proto_or_source_set_deps()
1140    android_test_deps = android_libs.union(test_android_libs)
1141    for dep in android_test_deps:
1142      assert (dep.custom_action_type == 'perfetto_android_library')
1143      create_modules_from_target(self.blueprint, self.gn, dep.name)
1144      module.static_libs.add(self._bp_module_name_from_gn_target(dep))
1145
1146    module.jni_libs = self._collect_jni_lib_deps(android_test_deps)
1147
1148    module.test_suites = {"general-tests"}
1149    module.test_config = gn_utils.label_to_path(
1150      target.a_i_t_android_bp_test_config)
1151    module.manifest = gn_utils.label_to_path(
1152      target.a_i_t_android_bp_test_manifest)
1153
1154    module.jni_uses_platform_apis = True
1155    module.sdk_version = self._SDK_VERSION
1156    self.blueprint.add_module(module)
1157    return module
1158
1159  def _generate_deps_modules_and_add_non_jni_lib_deps(self, module: Module,
1160      target: GnParser.Target):
1161    for dep in target.non_proto_or_source_set_deps():
1162      if gn_utils.label_without_toolchain(dep.name) in builtin_deps:
1163        builtin_deps[gn_utils.label_without_toolchain(dep.name)](module)
1164        continue
1165      create_modules_from_target(self.blueprint, self.gn, dep.name)
1166      if not is_target_android_jni_lib(dep):
1167        module.static_libs.add(self._bp_module_name_from_gn_target(dep))
1168
1169  def _bp_module_name_from_gn_target(self, target: GnParser.Target):
1170    module = create_modules_from_target(self.blueprint, self.gn, target.name)
1171    return module.name if module else None
1172
1173  def _collect_jni_lib_deps(self, root_targets: Sequence[GnParser.Target]):
1174    maybe_jni_lib_targets = set()
1175    for target in root_targets:
1176      self._collect_all_transitive_deps(target, maybe_jni_lib_targets)
1177
1178    jni_libs = [android_jni_lib_custom_target_name(target) for target in
1179               maybe_jni_lib_targets
1180               if is_target_android_jni_lib(target)]
1181    return jni_libs
1182
1183  def _collect_all_transitive_deps(self, target: GnParser.Target, visited):
1184    if target in visited:
1185      return
1186    visited.add(target)
1187    for d in target.non_proto_or_source_set_deps():
1188      self._collect_all_transitive_deps(d, visited)
1189
1190def _get_cflags(target: GnParser.Target):
1191  cflags = {flag for flag in target.cflags if re.match(cflag_allowlist, flag)}
1192  cflags |= set("-D%s" % define
1193                for define in target.defines
1194                if re.match(define_allowlist, define))
1195  return cflags
1196
1197
1198# Merge in additional hardcoded arguments.
1199def _add_additional_args(module: Module):
1200  for key, add_val in additional_args.get(module.name, []):
1201    curr = getattr(module, key)
1202    if add_val and isinstance(add_val, set) and isinstance(curr, set):
1203      curr.update(add_val)
1204    elif isinstance(add_val, str) and (not curr or isinstance(curr, str)):
1205      setattr(module, key, add_val)
1206    elif isinstance(add_val, bool) and (not curr or isinstance(curr, bool)):
1207      setattr(module, key, add_val)
1208    elif isinstance(add_val, dict) and isinstance(curr, dict):
1209      curr.update(add_val)
1210    elif isinstance(add_val, dict) and isinstance(curr, Target):
1211      curr.__dict__.update(add_val)
1212    else:
1213      raise Error('Unimplemented type %r of additional_args: %r' %
1214                  (type(add_val), key))
1215
1216def create_modules_from_target(blueprint: Blueprint, gn: GnParser,
1217                               gn_target_name: str) -> Optional[Module]:
1218  """Generate module(s) for a given GN target.
1219
1220    Given a GN target name, generate one or more corresponding modules into a
1221    blueprint. The only case when this generates >1 module is proto libraries.
1222
1223    Args:
1224        blueprint: Blueprint instance which is being generated.
1225        gn: gn_utils.GnParser object.
1226        gn_target_name: GN target for module generation.
1227    """
1228  if gn_target_name in blueprint.gn_target_to_module:
1229    return blueprint.gn_target_to_module[gn_target_name]
1230
1231  target = gn.get_target(gn_target_name)
1232  bp_module_name = label_to_module_name(gn_target_name)
1233  name_without_toolchain = gn_utils.label_without_toolchain(target.name)
1234  if target.type == 'executable':
1235    if target.toolchain == gn_utils.HOST_TOOLCHAIN:
1236      module_type = 'cc_binary_host'
1237    elif target.testonly:
1238      module_type = 'cc_test'
1239    else:
1240      module_type = 'cc_binary'
1241    module = Module(module_type, bp_module_name, gn_target_name)
1242  elif target.type == 'static_library':
1243    module = Module('cc_library_static', bp_module_name, gn_target_name)
1244  elif target.type == 'shared_library':
1245    if is_target_android_jni_lib(target):
1246      bp_module_name = android_jni_lib_custom_target_name(target)
1247    module = Module('cc_library_shared', bp_module_name, gn_target_name)
1248  elif target.type == 'source_set':
1249    module = Module('filegroup', bp_module_name, gn_target_name)
1250  elif target.type == 'group':
1251    # "group" targets are resolved recursively by gn_utils.get_target().
1252    # There's nothing we need to do at this level for them.
1253    return None
1254  elif target.type == 'proto_library':
1255    module = create_proto_modules(blueprint, gn, target)
1256    if module is None:
1257      return None
1258  elif target.type == 'action':
1259    if target.custom_action_type == 'sql_amalgamation':
1260      return create_amalgamated_sql_module(blueprint, gn, target)
1261    if target.custom_action_type == 'tp_tables':
1262      return create_tp_tables_module(blueprint, gn, target)
1263    if target.custom_action_type == 'perfetto_android_library':
1264      return AndroidJavaSDKModulesGenerator(blueprint,
1265                                            gn).create_android_library_module(
1266        target, bp_module_name)
1267    if target.custom_action_type == 'perfetto_android_app':
1268      return AndroidJavaSDKModulesGenerator(blueprint,
1269                                            gn).create_android_app_module(
1270        target, bp_module_name)
1271    if target.custom_action_type == 'perfetto_android_instrumentation_test':
1272      return AndroidJavaSDKModulesGenerator(blueprint,
1273                                            gn).create_android_test_module(
1274        target, bp_module_name)
1275
1276    if target.custom_action_type == 'cc_proto_descriptor':
1277      module = create_cc_proto_descriptor_module(blueprint, target)
1278    elif name_without_toolchain == gn_utils.GEN_VERSION_TARGET:
1279      module = create_gen_version_module(blueprint, target, bp_module_name)
1280    else:
1281      raise Error('Unhandled action: {}'.format(target.name))
1282  else:
1283    raise Error('Unknown target %s (%s)' % (target.name, target.type))
1284
1285  blueprint.add_module(module)
1286  module.host_supported = (name_without_toolchain in target_host_supported)
1287  module.vendor_available = (name_without_toolchain in target_vendor_available)
1288  module.product_available = (name_without_toolchain in target_product_available)
1289  module.init_rc.update(target_initrc.get(target.name, []))
1290  if target.type != 'proto_library':
1291    # proto_library embeds a "root" filegroup in its srcs.
1292    # Skip to prevent adding dups
1293    module.srcs.update(
1294      gn_utils.label_to_path(src)
1295      for src in target.sources
1296      if is_supported_source_file(src))
1297
1298  if name_without_toolchain in needs_libfts:
1299    module.musl.static_libs.add('libfts')
1300
1301  if target.type in gn_utils.LINKER_UNIT_TYPES:
1302    module.cflags.update(_get_cflags(target))
1303
1304  module_is_compiled = module.type not in ('genrule', 'filegroup')
1305  if module_is_compiled:
1306    # Don't try to inject library/source dependencies into genrules or
1307    # filegroups because they are not compiled in the traditional sense.
1308    module.defaults.update([defaults_module])
1309    for lib in target.libs:
1310      # Generally library names should be mangled as 'libXXX', unless they
1311      # are HAL libraries (e.g., android.hardware.health@2.0) or AIDL c++ / NDK
1312      # libraries (e.g. "android.hardware.power.stats-V1-cpp")
1313      android_lib = lib if '@' in lib or "-cpp" in lib or "-ndk" in lib \
1314        else 'lib' + lib
1315      if lib in shared_library_allowlist:
1316        module.add_android_shared_lib(android_lib)
1317      if lib in static_library_allowlist:
1318        module.add_android_static_lib(android_lib)
1319
1320  # If the module is a static library, export all the generated headers.
1321  if module.type == 'cc_library_static':
1322    module.export_generated_headers = module.generated_headers
1323
1324  if is_target_android_jni_lib(target):
1325    module.header_libs.add('jni_headers')
1326
1327  _add_additional_args(module)
1328
1329  # dep_name is an unmangled GN target name (e.g. //foo:bar(toolchain)).
1330  all_deps = target.non_proto_or_source_set_deps()
1331  all_deps |= target.transitive_source_set_deps()
1332  all_deps |= target.transitive_proto_deps()
1333  for dep in all_deps:
1334    # If the dependency refers to a library which we can replace with an
1335    # Android equivalent, stop recursing and patch the dependency in.
1336    # Don't recurse into //buildtools, builtin_deps are intercepted at
1337    # the //gn:xxx level.
1338    dep_name = dep.name
1339    if dep_name.startswith('//buildtools'):
1340      continue
1341
1342    # Ignore the dependency on the gen_buildflags genrule. That is run
1343    # separately in this generator and the generated file is copied over
1344    # into the repo (see usage of |buildflags_dir| in this script).
1345    if dep_name.startswith(gn_utils.BUILDFLAGS_TARGET):
1346      continue
1347
1348    dep_module = create_modules_from_target(blueprint, gn, dep_name)
1349
1350    # For filegroups and genrule, recurse but don't apply the deps.
1351    if not module_is_compiled:
1352      continue
1353
1354    # |builtin_deps| override GN deps with Android-specific ones. See the
1355    # config in the top of this file.
1356    if gn_utils.label_without_toolchain(dep_name) in builtin_deps:
1357      builtin_deps[gn_utils.label_without_toolchain(dep_name)](module)
1358      continue
1359
1360    # Don't recurse in any other //gn dep if not handled by builtin_deps.
1361    if dep_name.startswith('//gn:'):
1362      continue
1363
1364    if dep_module is None:
1365      continue
1366    if dep_module.type == 'cc_library_shared':
1367      module.shared_libs.add(dep_module.name)
1368    elif dep_module.type == 'cc_library_static':
1369      module.static_libs.add(dep_module.name)
1370    elif dep_module.type == 'filegroup':
1371      module.srcs.add(':' + dep_module.name)
1372    elif dep_module.type == 'genrule':
1373      module.generated_headers.update(dep_module.genrule_headers)
1374      module.srcs.update(dep_module.genrule_srcs)
1375      module.shared_libs.update(dep_module.genrule_shared_libs)
1376    elif dep_module.type == 'cc_binary':
1377      continue  # Ignore executables deps (used by cmdline integration tests).
1378    else:
1379      raise Error('Unknown dep %s (%s) for target %s' %
1380                  (dep_module.name, dep_module.type, module.name))
1381
1382  return module
1383
1384
1385def create_blueprint_for_targets(gn: GnParser, targets: List[str]):
1386  """Generate a blueprint for a list of GN targets."""
1387  blueprint = Blueprint()
1388
1389  # Default settings used by all modules.
1390  defaults = Module('cc_defaults', defaults_module, '//gn:default_deps')
1391
1392  # We have to use include_dirs passing the path relative to the android tree.
1393  # This is because: (i) perfetto_cc_defaults is used also by
1394  # test/**/Android.bp; (ii) if we use local_include_dirs instead, paths
1395  # become relative to the Android.bp that *uses* cc_defaults (not the one
1396  # that defines it).s
1397  defaults.include_dirs = {
1398      tree_path, tree_path + '/include', tree_path + '/' + buildflags_dir,
1399      tree_path + '/src/profiling/memory/include'
1400  }
1401  defaults.cflags.update([
1402      '-Wno-error=return-type',
1403      '-Wno-sign-compare',
1404      '-Wno-sign-promo',
1405      '-Wno-unused-parameter',
1406      '-fvisibility=hidden',
1407      '-O2',
1408  ])
1409  defaults.user_debug_flag = True
1410  defaults.lto = True
1411
1412  blueprint.add_module(defaults)
1413  for target in targets:
1414    create_modules_from_target(blueprint, gn, target)
1415  return blueprint
1416
1417
1418def main():
1419  parser = argparse.ArgumentParser(
1420      description='Generate Android.bp from a GN description.')
1421  parser.add_argument(
1422      '--check-only',
1423      help='Don\'t keep the generated files',
1424      action='store_true')
1425  parser.add_argument(
1426      '--desc',
1427      help='GN description (e.g., gn desc out --format=json --all-toolchains "//*"'
1428  )
1429  parser.add_argument(
1430      '--extras',
1431      help='Extra targets to include at the end of the Blueprint file',
1432      default=os.path.join(gn_utils.repo_root(), 'Android.bp.extras'),
1433  )
1434  parser.add_argument(
1435      '--output',
1436      help='Blueprint file to create',
1437      default=os.path.join(gn_utils.repo_root(), 'Android.bp'),
1438  )
1439  parser.add_argument(
1440      'targets',
1441      nargs=argparse.REMAINDER,
1442      help='Targets to include in the blueprint (e.g., "//:perfetto_tests")')
1443  args = parser.parse_args()
1444
1445  if args.desc:
1446    with open(args.desc) as f:
1447      desc = json.load(f)
1448  else:
1449    desc = gn_utils.create_build_description(gn_args)
1450
1451  gn = gn_utils.GnParser(desc)
1452  blueprint = create_blueprint_for_targets(gn, args.targets or default_targets)
1453  project_root = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
1454  tool_name = os.path.relpath(os.path.abspath(__file__), project_root)
1455
1456  # TODO(primiano): enable this on Android after the TODO in
1457  # perfetto_component.gni is fixed.
1458  # Check for ODR violations
1459  # for target_name in default_targets:
1460  # checker = gn_utils.ODRChecker(gn, target_name)
1461
1462  # Add any proto groups to the blueprint.
1463  for name, group in proto_groups.items():
1464    create_proto_group_modules(blueprint, gn, name, group)
1465
1466  output = [
1467      """// Copyright (C) 2017 The Android Open Source Project
1468//
1469// Licensed under the Apache License, Version 2.0 (the "License");
1470// you may not use this file except in compliance with the License.
1471// You may obtain a copy of the License at
1472//
1473//      http://www.apache.org/licenses/LICENSE-2.0
1474//
1475// Unless required by applicable law or agreed to in writing, software
1476// distributed under the License is distributed on an "AS IS" BASIS,
1477// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1478// See the License for the specific language governing permissions and
1479// limitations under the License.
1480//
1481// This file is automatically generated by %s. Do not edit.
1482""" % (tool_name)
1483  ]
1484  blueprint.to_string(output)
1485  with open(args.extras, 'r') as r:
1486    for line in r:
1487      output.append(line.rstrip("\n\r"))
1488
1489  out_files = []
1490
1491  # Generate the Android.bp file.
1492  out_files.append(args.output + '.swp')
1493  with open(out_files[-1], 'w') as f:
1494    f.write('\n'.join(output))
1495    # Text files should have a trailing EOL.
1496    f.write('\n')
1497
1498  # Generate the perfetto_build_flags.h file.
1499  out_files.append(os.path.join(buildflags_dir, 'perfetto_build_flags.h.swp'))
1500  gn_utils.gen_buildflags(gn_args, out_files[-1])
1501
1502  # Either check the contents or move the files to their final destination.
1503  return gn_utils.check_or_commit_generated_files(out_files, args.check_only)
1504
1505
1506if __name__ == '__main__':
1507  sys.exit(main())
1508