• 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 collections
30import json
31import os
32import re
33import sys
34
35import gn_utils
36
37from compat import itervalues
38
39ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
40
41# Arguments for the GN output directory.
42gn_args = ' '.join([
43    'is_debug=false',
44    'is_perfetto_build_generator=true',
45    'perfetto_build_with_android=true',
46    'target_cpu="arm"',
47    'target_os="android"',
48])
49
50# Default targets to translate to the blueprint file.
51default_targets = [
52    '//:libperfetto_client_experimental',
53    '//:libperfetto',
54    '//:perfetto_integrationtests',
55    '//:perfetto_unittests',
56    '//protos/perfetto/trace:perfetto_trace_protos',
57    '//src/android_internal:libperfetto_android_internal',
58    '//src/perfetto_cmd:perfetto',
59    '//src/perfetto_cmd:trigger_perfetto',
60    '//src/profiling/memory:heapprofd_client',
61    '//src/profiling/memory:heapprofd_client_api',
62    '//src/profiling/memory:heapprofd_api_noop',
63    '//src/profiling/memory:heapprofd',
64    '//src/profiling/memory:heapprofd_standalone_client',
65    '//src/profiling/perf:traced_perf',
66    '//src/traced/probes:traced_probes',
67    '//src/traced/service:traced',
68    '//src/trace_processor:trace_processor_shell',
69    '//test/cts:perfetto_cts_deps',
70    '//test/cts:perfetto_cts_jni_deps',
71    '//test:perfetto_gtest_logcat_printer',
72]
73
74# Host targets
75ipc_plugin = '//src/ipc/protoc_plugin:ipc_plugin(%s)' % gn_utils.HOST_TOOLCHAIN
76protozero_plugin = '//src/protozero/protoc_plugin:protozero_plugin(%s)' % (
77    gn_utils.HOST_TOOLCHAIN)
78cppgen_plugin = '//src/protozero/protoc_plugin:cppgen_plugin(%s)' % (
79    gn_utils.HOST_TOOLCHAIN)
80
81default_targets += [
82    '//tools/trace_to_text:trace_to_text(%s)' % gn_utils.HOST_TOOLCHAIN,
83    protozero_plugin,
84    ipc_plugin,
85]
86
87# Defines a custom init_rc argument to be applied to the corresponding output
88# blueprint target.
89target_initrc = {
90    '//src/traced/service:traced': {'perfetto.rc'},
91    '//src/profiling/memory:heapprofd': {'heapprofd.rc'},
92    '//src/profiling/perf:traced_perf': {'traced_perf.rc'},
93}
94
95target_host_supported = [
96    '//:libperfetto',
97    '//protos/perfetto/trace:perfetto_trace_protos',
98    '//src/trace_processor:trace_processor_shell',
99]
100
101# All module names are prefixed with this string to avoid collisions.
102module_prefix = 'perfetto_'
103
104# Shared libraries which are directly translated to Android system equivalents.
105shared_library_allowlist = [
106    'android',
107    'android.hardware.atrace@1.0',
108    'android.hardware.health@2.0',
109    'android.hardware.power.stats@1.0',
110    "android.hardware.power.stats-V1-cpp",
111    'base',
112    'binder',
113    'binder_ndk',
114    'cutils',
115    'hidlbase',
116    'hidltransport',
117    'hwbinder',
118    'incident',
119    'log',
120    'services',
121    'statssocket',
122    "tracingproxy",
123    'utils',
124]
125
126# Static libraries which are directly translated to Android system equivalents.
127static_library_allowlist = [
128    'statslog_perfetto',
129]
130
131# Name of the module which settings such as compiler flags for all other
132# modules.
133defaults_module = module_prefix + 'defaults'
134
135# Location of the project in the Android source tree.
136tree_path = 'external/perfetto'
137
138# Path for the protobuf sources in the standalone build.
139buildtools_protobuf_src = '//buildtools/protobuf/src'
140
141# Location of the protobuf src dir in the Android source tree.
142android_protobuf_src = 'external/protobuf/src'
143
144# Compiler flags which are passed through to the blueprint.
145cflag_allowlist = r'^-DPERFETTO.*$'
146
147# Compiler defines which are passed through to the blueprint.
148define_allowlist = r'^(GOOGLE_PROTO.*)|(ZLIB_.*)|(USE_MMAP)|(HAVE_HIDDEN)$'
149
150# Shared libraries which are not in PDK.
151library_not_in_pdk = {
152    'libandroid',
153    'libservices',
154}
155
156# The directory where the generated perfetto_build_flags.h will be copied into.
157buildflags_dir = 'include/perfetto/base/build_configs/android_tree'
158
159
160def enumerate_data_deps():
161  with open(os.path.join(ROOT_DIR, 'tools', 'test_data.txt')) as f:
162    lines = f.readlines()
163  for line in (line.strip() for line in lines if not line.startswith('#')):
164    assert os.path.exists(line), 'file %s should exist' % line
165    if line.startswith('test/data/'):
166        # Skip test data files that require GCS. They are only for benchmarks.
167        # We don't run benchmarks in the android tree.
168        continue
169    if line.endswith('/'):
170      yield line + '**/*'
171    else:
172      yield line
173
174
175# Additional arguments to apply to Android.bp rules.
176additional_args = {
177    'heapprofd_client_api': [
178        ('static_libs', {'libasync_safe'}),
179        # heapprofd_client_api MUST NOT have global constructors. Because it
180        # is loaded in an __attribute__((constructor)) of libc, we cannot
181        # guarantee that the global constructors get run before it is used.
182        ('cflags', {'-Wglobal-constructors', '-Werror=global-constructors'}),
183        ('version_script', 'src/profiling/memory/heapprofd_client_api.map.txt'),
184        ('stubs', {
185          'versions': ['S'],
186          'symbol_file': 'src/profiling/memory/heapprofd_client_api.map.txt',
187        }),
188        ('export_include_dirs', {'src/profiling/memory/include'}),
189    ],
190    'heapprofd_api_noop': [
191        ('version_script', 'src/profiling/memory/heapprofd_client_api.map.txt'),
192        ('stubs', {
193          'versions': ['S'],
194          'symbol_file': 'src/profiling/memory/heapprofd_client_api.map.txt',
195        }),
196        ('export_include_dirs', {'src/profiling/memory/include'}),
197    ],
198    'heapprofd_client': [
199        ('include_dirs', {'bionic/libc'}),
200        ('static_libs', {'libasync_safe'}),
201    ],
202    'heapprofd_standalone_client': [
203        ('static_libs', {'libasync_safe'}),
204        ('version_script', 'src/profiling/memory/heapprofd_client_api.map.txt'),
205        ('export_include_dirs', {'src/profiling/memory/include'}),
206        ('stl', 'libc++_static'),
207    ],
208    'perfetto_unittests': [
209        ('data', set(enumerate_data_deps())),
210        ('include_dirs', {'bionic/libc/kernel'}),
211    ],
212    'perfetto_integrationtests': [
213      ('test_suites', {'general-tests'}),
214      ('test_config', 'PerfettoIntegrationTests.xml'),
215    ],
216    'traced_probes': [
217        ('required', {'libperfetto_android_internal',
218                      'trigger_perfetto',
219                      'traced_perf',
220                      'mm_events'}),
221    ],
222    'libperfetto_android_internal': [('static_libs', {'libhealthhalutils'}),],
223    'trace_processor_shell': [
224      ('strip', {'all': True}),
225      ('host', {
226        'stl': 'libc++_static',
227        'dist': {'targets': ['sdk_repo']},
228      }),
229    ],
230    'libperfetto_client_experimental': [
231      ('apex_available', {
232        '//apex_available:platform',
233        'com.android.art',
234        'com.android.art.debug'}),
235      ('min_sdk_version', 'S'),
236      ('shared_libs', {'liblog'}),
237      ('export_include_dirs', {'include', buildflags_dir}),
238    ],
239    'perfetto_trace_protos': [
240      ('apex_available', {
241        '//apex_available:platform',
242        'com.android.art',
243        'com.android.art.debug'}),
244      ('min_sdk_version', 'S'),
245    ],
246    'libperfetto': [
247      ('export_include_dirs', {'include', buildflags_dir}),
248    ],
249}
250
251
252def enable_gtest_and_gmock(module):
253  module.static_libs.add('libgmock')
254  module.static_libs.add('libgtest')
255  if module.name != 'perfetto_gtest_logcat_printer':
256    module.whole_static_libs.add('perfetto_gtest_logcat_printer')
257
258
259def enable_protobuf_full(module):
260  if module.type == 'cc_binary_host':
261    module.static_libs.add('libprotobuf-cpp-full')
262  elif module.host_supported:
263    module.host.static_libs.add('libprotobuf-cpp-full')
264    module.android.shared_libs.add('libprotobuf-cpp-full')
265  else:
266    module.shared_libs.add('libprotobuf-cpp-full')
267
268
269def enable_protobuf_lite(module):
270  module.shared_libs.add('libprotobuf-cpp-lite')
271
272
273def enable_protoc_lib(module):
274  if module.type == 'cc_binary_host':
275    module.static_libs.add('libprotoc')
276  else:
277    module.shared_libs.add('libprotoc')
278
279
280def enable_libunwindstack(module):
281  if module.name != 'heapprofd_standalone_client':
282    module.shared_libs.add('libunwindstack')
283    module.shared_libs.add('libprocinfo')
284    module.shared_libs.add('libbase')
285  else:
286    module.static_libs.add('libunwindstack')
287    module.static_libs.add('libprocinfo')
288    module.static_libs.add('libbase')
289    module.static_libs.add('liblzma')
290    module.static_libs.add('libdexfile_support')
291
292
293def enable_libunwind(module):
294  # libunwind is disabled on Darwin so we cannot depend on it.
295  pass
296
297
298def enable_sqlite(module):
299  if module.type == 'cc_binary_host':
300    module.static_libs.add('libsqlite')
301  elif module.host_supported:
302    # Copy what the sqlite3 command line tool does.
303    module.android.shared_libs.add('libsqlite')
304    module.android.shared_libs.add('libandroidicu')
305    module.android.shared_libs.add('liblog')
306    module.android.shared_libs.add('libutils')
307    module.host.static_libs.add('libsqlite')
308  else:
309    module.shared_libs.add('libsqlite')
310    module.shared_libs.add('libandroidicu')
311    module.shared_libs.add('liblog')
312    module.shared_libs.add('libutils')
313
314
315def enable_zlib(module):
316  if module.type == 'cc_binary_host':
317    module.static_libs.add('libz')
318  elif module.host_supported:
319    module.android.shared_libs.add('libz')
320    module.host.static_libs.add('libz')
321  else:
322    module.shared_libs.add('libz')
323
324
325def enable_uapi_headers(module):
326  module.include_dirs.add('bionic/libc/kernel')
327
328def enable_bionic_libc_platform_headers_on_android(module):
329  module.header_libs.add('bionic_libc_platform_headers')
330
331
332# Android equivalents for third-party libraries that the upstream project
333# depends on.
334builtin_deps = {
335    '//gn:default_deps': lambda x: None,
336    '//gn:gtest_main': lambda x: None,
337    '//gn:protoc': lambda x: None,
338    '//gn:gtest_and_gmock': enable_gtest_and_gmock,
339    '//gn:libunwind': enable_libunwind,
340    '//gn:protobuf_full': enable_protobuf_full,
341    '//gn:protobuf_lite': enable_protobuf_lite,
342    '//gn:protoc_lib': enable_protoc_lib,
343    '//gn:libunwindstack': enable_libunwindstack,
344    '//gn:sqlite': enable_sqlite,
345    '//gn:zlib': enable_zlib,
346    '//gn:bionic_kernel_uapi_headers' : enable_uapi_headers,
347    '//src/profiling/memory:bionic_libc_platform_headers_on_android':
348      enable_bionic_libc_platform_headers_on_android,
349}
350
351# ----------------------------------------------------------------------------
352# End of configuration.
353# ----------------------------------------------------------------------------
354
355
356class Error(Exception):
357  pass
358
359
360class ThrowingArgumentParser(argparse.ArgumentParser):
361
362  def __init__(self, context):
363    super(ThrowingArgumentParser, self).__init__()
364    self.context = context
365
366  def error(self, message):
367    raise Error('%s: %s' % (self.context, message))
368
369
370def write_blueprint_key_value(output, name, value, sort=True):
371  """Writes a Blueprint key-value pair to the output"""
372
373  if not value:
374    return
375  if isinstance(value, set):
376    value = sorted(value)
377  if isinstance(value, list):
378    output.append('  %s: [' % name)
379    for item in sorted(value) if sort else value:
380      output.append('    "%s",' % item)
381    output.append('  ],')
382    return
383  if isinstance(value, bool):
384    output.append('  %s: true,' % name)
385    return
386  if isinstance(value, Target):
387    value.to_string(output)
388    return
389  if isinstance(value, dict):
390    kv_output = []
391    for k, v in value.items():
392      write_blueprint_key_value(kv_output, k, v)
393
394    output.append('  %s: {' % name)
395    for line in kv_output:
396      output.append('  %s' % line)
397    output.append('  },')
398    return
399  output.append('  %s: "%s",' % (name, value))
400
401
402class Target(object):
403  """A target-scoped part of a module"""
404
405  def __init__(self, name):
406    self.name = name
407    self.shared_libs = set()
408    self.static_libs = set()
409    self.whole_static_libs = set()
410    self.cflags = set()
411    self.dist = dict()
412    self.strip = dict()
413    self.stl = None
414
415  def to_string(self, output):
416    nested_out = []
417    self._output_field(nested_out, 'shared_libs')
418    self._output_field(nested_out, 'static_libs')
419    self._output_field(nested_out, 'whole_static_libs')
420    self._output_field(nested_out, 'cflags')
421    self._output_field(nested_out, 'stl')
422    self._output_field(nested_out, 'dist')
423    self._output_field(nested_out, 'strip')
424
425    if nested_out:
426      output.append('  %s: {' % self.name)
427      for line in nested_out:
428        output.append('  %s' % line)
429      output.append('  },')
430
431  def _output_field(self, output, name, sort=True):
432    value = getattr(self, name)
433    return write_blueprint_key_value(output, name, value, sort)
434
435
436class Module(object):
437  """A single module (e.g., cc_binary, cc_test) in a blueprint."""
438
439  def __init__(self, mod_type, name, gn_target):
440    self.type = mod_type
441    self.gn_target = gn_target
442    self.name = name
443    self.srcs = set()
444    self.comment = 'GN: ' + gn_utils.label_without_toolchain(gn_target)
445    self.shared_libs = set()
446    self.static_libs = set()
447    self.whole_static_libs = set()
448    self.tools = set()
449    self.cmd = None
450    self.host_supported = False
451    self.init_rc = set()
452    self.out = set()
453    self.export_include_dirs = set()
454    self.generated_headers = set()
455    self.export_generated_headers = set()
456    self.defaults = set()
457    self.cflags = set()
458    self.include_dirs = set()
459    self.header_libs = set()
460    self.required = set()
461    self.user_debug_flag = False
462    self.tool_files = None
463    self.android = Target('android')
464    self.host = Target('host')
465    self.lto = None
466    self.stl = None
467    self.dist = dict()
468    self.strip = dict()
469    self.data = set()
470    self.apex_available = set()
471    self.min_sdk_version = None
472    # The genrule_XXX below are properties that must to be propagated back
473    # on the module(s) that depend on the genrule.
474    self.genrule_headers = set()
475    self.genrule_srcs = set()
476    self.genrule_shared_libs = set()
477    self.version_script = None
478    self.test_suites = set()
479    self.test_config = None
480    self.stubs = {}
481
482  def to_string(self, output):
483    if self.comment:
484      output.append('// %s' % self.comment)
485    output.append('%s {' % self.type)
486    self._output_field(output, 'name')
487    self._output_field(output, 'srcs')
488    self._output_field(output, 'shared_libs')
489    self._output_field(output, 'static_libs')
490    self._output_field(output, 'whole_static_libs')
491    self._output_field(output, 'tools')
492    self._output_field(output, 'cmd', sort=False)
493    self._output_field(output, 'host_supported')
494    self._output_field(output, 'init_rc')
495    self._output_field(output, 'out')
496    self._output_field(output, 'export_include_dirs')
497    self._output_field(output, 'generated_headers')
498    self._output_field(output, 'export_generated_headers')
499    self._output_field(output, 'defaults')
500    self._output_field(output, 'cflags')
501    self._output_field(output, 'include_dirs')
502    self._output_field(output, 'header_libs')
503    self._output_field(output, 'required')
504    self._output_field(output, 'dist')
505    self._output_field(output, 'strip')
506    self._output_field(output, 'tool_files')
507    self._output_field(output, 'data')
508    self._output_field(output, 'stl')
509    self._output_field(output, 'apex_available')
510    self._output_field(output, 'min_sdk_version')
511    self._output_field(output, 'version_script')
512    self._output_field(output, 'test_suites')
513    self._output_field(output, 'test_config')
514    self._output_field(output, 'stubs')
515
516    target_out = []
517    self._output_field(target_out, 'android')
518    self._output_field(target_out, 'host')
519    if target_out:
520      output.append('  target: {')
521      for line in target_out:
522        output.append('  %s' % line)
523      output.append('  },')
524
525    disable_pdk = any(name in library_not_in_pdk for name in self.shared_libs)
526    if self.user_debug_flag or disable_pdk:
527      output.append('  product_variables: {')
528      if disable_pdk:
529        output.append('    pdk: {')
530        output.append('      enabled: false,')
531        output.append('    },')
532      if self.user_debug_flag:
533        output.append('    debuggable: {')
534        output.append(
535            '      cflags: ["-DPERFETTO_BUILD_WITH_ANDROID_USERDEBUG"],')
536        output.append('    },')
537      output.append('  },')
538    if self.lto is not None:
539      output.append('  target: {')
540      output.append('    android: {')
541      output.append('      lto: {')
542      output.append('        thin: %s,' % 'true' if self.lto else 'false')
543      output.append('      },')
544      output.append('    },')
545      output.append('  },')
546    output.append('}')
547    output.append('')
548
549
550  def add_android_static_lib(self, lib):
551    if self.type == 'cc_binary_host':
552      raise Exception('Adding Android static lib for host tool is unsupported')
553    elif self.host_supported:
554      self.android.static_libs.add(lib)
555    else:
556      self.static_libs.add(lib)
557
558
559  def add_android_shared_lib(self, lib):
560    if self.type == 'cc_binary_host':
561      raise Exception('Adding Android shared lib for host tool is unsupported')
562    elif self.host_supported:
563      self.android.shared_libs.add(lib)
564    else:
565      self.shared_libs.add(lib)
566
567
568  def _output_field(self, output, name, sort=True):
569    value = getattr(self, name)
570    return write_blueprint_key_value(output, name, value, sort)
571
572
573class Blueprint(object):
574  """In-memory representation of an Android.bp file."""
575
576  def __init__(self):
577    self.modules = {}
578
579  def add_module(self, module):
580    """Adds a new module to the blueprint, replacing any existing module
581        with the same name.
582
583        Args:
584            module: Module instance.
585        """
586    self.modules[module.name] = module
587
588  def to_string(self, output):
589    for m in sorted(itervalues(self.modules), key=lambda m: m.name):
590      m.to_string(output)
591
592
593def label_to_module_name(label):
594  """Turn a GN label (e.g., //:perfetto_tests) into a module name."""
595  # If the label is explicibly listed in the default target list, don't prefix
596  # its name and return just the target name. This is so tools like
597  # "trace_to_text" stay as such in the Android tree.
598  label_without_toolchain = gn_utils.label_without_toolchain(label)
599  if label in default_targets or label_without_toolchain in default_targets:
600    return label_without_toolchain.split(':')[-1]
601
602  module = re.sub(r'^//:?', '', label_without_toolchain)
603  module = re.sub(r'[^a-zA-Z0-9_]', '_', module)
604  if not module.startswith(module_prefix):
605    return module_prefix + module
606  return module
607
608
609def is_supported_source_file(name):
610  """Returns True if |name| can appear in a 'srcs' list."""
611  return os.path.splitext(name)[1] in ['.c', '.cc', '.proto']
612
613
614def create_proto_modules(blueprint, gn, target):
615  """Generate genrules for a proto GN target.
616
617    GN actions are used to dynamically generate files during the build. The
618    Soong equivalent is a genrule. This function turns a specific kind of
619    genrule which turns .proto files into source and header files into a pair
620    equivalent genrules.
621
622    Args:
623        blueprint: Blueprint instance which is being generated.
624        target: gn_utils.Target object.
625
626    Returns:
627        The source_genrule module.
628    """
629  assert (target.type == 'proto_library')
630
631  tools = {'aprotoc'}
632  cpp_out_dir = '$(genDir)/%s/' % tree_path
633  target_module_name = label_to_module_name(target.name)
634
635  # In GN builds the proto path is always relative to the output directory
636  # (out/tmp.xxx).
637  cmd = ['mkdir -p %s &&' % cpp_out_dir, '$(location aprotoc)']
638  cmd += ['--proto_path=%s' % tree_path]
639
640  if buildtools_protobuf_src in target.proto_paths:
641    cmd += ['--proto_path=%s' % android_protobuf_src]
642
643  # We don't generate any targets for source_set proto modules because
644  # they will be inlined into other modules if required.
645  if target.proto_plugin == 'source_set':
646    return None
647
648  # Descriptor targets only generate a single target.
649  if target.proto_plugin == 'descriptor':
650    out = '{}.bin'.format(target_module_name)
651
652    cmd += ['--descriptor_set_out=$(out)']
653    cmd += ['$(in)']
654
655    descriptor_module = Module('genrule', target_module_name, target.name)
656    descriptor_module.cmd = ' '.join(cmd)
657    descriptor_module.out = [out]
658    descriptor_module.tools = tools
659    blueprint.add_module(descriptor_module)
660
661    # Recursively extract the .proto files of all the dependencies and
662    # add them to srcs.
663    target_queue = collections.deque([target.name])
664    seen_targets = set()
665    while target_queue:
666      dep = target_queue.popleft()
667      if dep in seen_targets:
668        continue
669      seen_targets.add(dep)
670
671      current_target = gn.get_target(dep)
672      descriptor_module.srcs.update(
673          gn_utils.label_to_path(src) for src in current_target.sources)
674      target_queue.extend(current_target.proto_deps)
675
676    return descriptor_module
677
678  # We create two genrules for each proto target: one for the headers and
679  # another for the sources. This is because the module that depends on the
680  # generated files needs to declare two different types of dependencies --
681  # source files in 'srcs' and headers in 'generated_headers' -- and it's not
682  # valid to generate .h files from a source dependency and vice versa.
683  source_module_name = target_module_name + '_gen'
684  source_module = Module('genrule', source_module_name, target.name)
685  blueprint.add_module(source_module)
686  source_module.srcs.update(
687      gn_utils.label_to_path(src) for src in target.sources)
688
689  header_module = Module('genrule', source_module_name + '_headers',
690                         target.name)
691  blueprint.add_module(header_module)
692  header_module.srcs = set(source_module.srcs)
693
694  # TODO(primiano): at some point we should remove this. This was introduced
695  # by aosp/1108421 when adding "protos/" to .proto include paths, in order to
696  # avoid doing multi-repo changes and allow old clients in the android tree
697  # to still do the old #include "perfetto/..." rather than
698  # #include "protos/perfetto/...".
699  header_module.export_include_dirs = {'.', 'protos'}
700
701  source_module.genrule_srcs.add(':' + source_module.name)
702  source_module.genrule_headers.add(header_module.name)
703
704  if target.proto_plugin == 'proto':
705    suffixes = ['pb']
706    source_module.genrule_shared_libs.add('libprotobuf-cpp-lite')
707    cmd += ['--cpp_out=lite=true:' + cpp_out_dir]
708  elif target.proto_plugin == 'protozero':
709    suffixes = ['pbzero']
710    plugin = create_modules_from_target(blueprint, gn, protozero_plugin)
711    tools.add(plugin.name)
712    cmd += ['--plugin=protoc-gen-plugin=$(location %s)' % plugin.name]
713    cmd += ['--plugin_out=wrapper_namespace=pbzero:' + cpp_out_dir]
714  elif target.proto_plugin == 'cppgen':
715    suffixes = ['gen']
716    plugin = create_modules_from_target(blueprint, gn, cppgen_plugin)
717    tools.add(plugin.name)
718    cmd += ['--plugin=protoc-gen-plugin=$(location %s)' % plugin.name]
719    cmd += ['--plugin_out=wrapper_namespace=gen:' + cpp_out_dir]
720  elif target.proto_plugin == 'ipc':
721    suffixes = ['ipc']
722    plugin = create_modules_from_target(blueprint, gn, ipc_plugin)
723    tools.add(plugin.name)
724    cmd += ['--plugin=protoc-gen-plugin=$(location %s)' % plugin.name]
725    cmd += ['--plugin_out=wrapper_namespace=gen:' + cpp_out_dir]
726  else:
727    raise Error('Unsupported proto plugin: %s' % target.proto_plugin)
728
729  cmd += ['$(in)']
730  source_module.cmd = ' '.join(cmd)
731  header_module.cmd = source_module.cmd
732  source_module.tools = tools
733  header_module.tools = tools
734
735  for sfx in suffixes:
736    source_module.out.update('%s/%s' %
737                             (tree_path, src.replace('.proto', '.%s.cc' % sfx))
738                             for src in source_module.srcs)
739    header_module.out.update('%s/%s' %
740                             (tree_path, src.replace('.proto', '.%s.h' % sfx))
741                             for src in header_module.srcs)
742  return source_module
743
744
745def create_merged_sql_metrics_module(blueprint, target):
746  bp_module_name = label_to_module_name(target.name)
747  module = Module('genrule', bp_module_name, target.name)
748  module.tool_files = [
749      'tools/gen_merged_sql_metrics.py',
750  ]
751  module.cmd = ' '.join([
752      '$(location tools/gen_merged_sql_metrics.py)',
753      '--cpp_out=$(out)',
754      '$(in)',
755  ])
756  module.genrule_headers.add(module.name)
757  module.out.update(target.outputs)
758  module.srcs.update(gn_utils.label_to_path(src) for src in target.inputs)
759  blueprint.add_module(module)
760  return module
761
762
763def create_cc_proto_descriptor_module(blueprint, target):
764  bp_module_name = label_to_module_name(target.name)
765  module = Module('genrule', bp_module_name, target.name)
766  module.tool_files = [
767      'tools/gen_cc_proto_descriptor.py',
768  ]
769  module.cmd = ' '.join([
770      '$(location tools/gen_cc_proto_descriptor.py)',
771      '--gen_dir=$(genDir)',
772      '--cpp_out=$(out)',
773      '$(in)'
774  ])
775  module.genrule_headers.add(module.name)
776  module.srcs.update(
777      ':' + label_to_module_name(dep) for dep in target.proto_deps)
778  module.out.update(target.outputs)
779  blueprint.add_module(module)
780  return module
781
782
783def create_gen_version_module(blueprint, target, bp_module_name):
784  module = Module('genrule', bp_module_name, gn_utils.GEN_VERSION_TARGET)
785  script_path = gn_utils.label_to_path(target.script)
786  module.genrule_headers.add(bp_module_name)
787  module.tool_files = [ script_path ]
788  module.out.update(target.outputs)
789  module.srcs.update(gn_utils.label_to_path(src) for src in target.inputs)
790  module.cmd = ' '.join([
791        'python3 $(location %s)' % script_path,
792        '--no_git',
793        '--changelog=$(location CHANGELOG)',
794        '--cpp_out=$(out)'
795  ])
796  blueprint.add_module(module)
797  return module
798
799
800def _get_cflags(target):
801  cflags = {flag for flag in target.cflags if re.match(cflag_allowlist, flag)}
802  cflags |= set("-D%s" % define
803                for define in target.defines
804                if re.match(define_allowlist, define))
805  return cflags
806
807
808def create_modules_from_target(blueprint, gn, gn_target_name):
809  """Generate module(s) for a given GN target.
810
811    Given a GN target name, generate one or more corresponding modules into a
812    blueprint. The only case when this generates >1 module is proto libraries.
813
814    Args:
815        blueprint: Blueprint instance which is being generated.
816        gn: gn_utils.GnParser object.
817        gn_target_name: GN target for module generation.
818    """
819  bp_module_name = label_to_module_name(gn_target_name)
820  if bp_module_name in blueprint.modules:
821    return blueprint.modules[bp_module_name]
822  target = gn.get_target(gn_target_name)
823
824  if target.type == 'executable':
825    if target.toolchain == gn_utils.HOST_TOOLCHAIN:
826      module_type = 'cc_binary_host'
827    elif target.testonly:
828      module_type = 'cc_test'
829    else:
830      module_type = 'cc_binary'
831    module = Module(module_type, bp_module_name, gn_target_name)
832  elif target.type == 'static_library':
833    module = Module('cc_library_static', bp_module_name, gn_target_name)
834  elif target.type == 'shared_library':
835    module = Module('cc_library_shared', bp_module_name, gn_target_name)
836  elif target.type == 'source_set':
837    module = Module('filegroup', bp_module_name, gn_target_name)
838  elif target.type == 'group':
839    # "group" targets are resolved recursively by gn_utils.get_target().
840    # There's nothing we need to do at this level for them.
841    return None
842  elif target.type == 'proto_library':
843    module = create_proto_modules(blueprint, gn, target)
844    if module is None:
845      return None
846  elif target.type == 'action':
847    if 'gen_merged_sql_metrics' in target.name:
848      module = create_merged_sql_metrics_module(blueprint, target)
849    elif re.match('.*gen_cc_.*_descriptor$', target.name):
850      module = create_cc_proto_descriptor_module(blueprint, target)
851    elif target.type == 'action' and gn_utils.label_without_toolchain(
852        target.name) == gn_utils.GEN_VERSION_TARGET:
853      module = create_gen_version_module(blueprint, target, bp_module_name)
854    else:
855      raise Error('Unhandled action: {}'.format(target.name))
856  else:
857    raise Error('Unknown target %s (%s)' % (target.name, target.type))
858
859  blueprint.add_module(module)
860  module.host_supported = (gn_utils.label_without_toolchain(target.name) in
861                           target_host_supported)
862  module.init_rc = target_initrc.get(target.name, [])
863  module.srcs.update(
864      gn_utils.label_to_path(src)
865      for src in target.sources
866      if is_supported_source_file(src))
867
868  if target.type in gn_utils.LINKER_UNIT_TYPES:
869    module.cflags.update(_get_cflags(target))
870
871  module_is_compiled = module.type not in ('genrule', 'filegroup')
872  if module_is_compiled:
873    # Don't try to inject library/source dependencies into genrules or
874    # filegroups because they are not compiled in the traditional sense.
875    module.defaults = [defaults_module]
876    for lib in target.libs:
877      # Generally library names should be mangled as 'libXXX', unless they
878      # are HAL libraries (e.g., android.hardware.health@2.0) or AIDL c++
879      # libraries (e.g. "android.hardware.power.stats-V1-cpp")
880      android_lib = lib if '@' in lib or "-cpp" in lib else 'lib' + lib
881      if lib in shared_library_allowlist:
882        module.add_android_shared_lib(android_lib)
883      if lib in static_library_allowlist:
884        module.add_android_static_lib(android_lib)
885
886  # If the module is a static library, export all the generated headers.
887  if module.type == 'cc_library_static':
888    module.export_generated_headers = module.generated_headers
889
890  # Merge in additional hardcoded arguments.
891  for key, add_val in additional_args.get(module.name, []):
892    curr = getattr(module, key)
893    if add_val and isinstance(add_val, set) and isinstance(curr, set):
894      curr.update(add_val)
895    elif isinstance(add_val, str) and (not curr or isinstance(curr, str)):
896      setattr(module, key, add_val)
897    elif isinstance(add_val, bool) and (not curr or isinstance(curr, bool)):
898      setattr(module, key, add_val)
899    elif isinstance(add_val, dict) and isinstance(curr, dict):
900      curr.update(add_val)
901    elif isinstance(add_val, dict) and isinstance(curr, Target):
902      curr.__dict__.update(add_val)
903    else:
904      raise Error('Unimplemented type %r of additional_args: %r' %
905                  (type(add_val), key))
906
907  # dep_name is an unmangled GN target name (e.g. //foo:bar(toolchain)).
908  for dep_name in target.deps | target.source_set_deps | target.proto_deps:
909    # If the dependency refers to a library which we can replace with an
910    # Android equivalent, stop recursing and patch the dependency in.
911    # Don't recurse into //buildtools, builtin_deps are intercepted at
912    # the //gn:xxx level.
913    if dep_name.startswith('//buildtools'):
914      continue
915
916    # Ignore the dependency on the gen_buildflags genrule. That is run
917    # separately in this generator and the generated file is copied over
918    # into the repo (see usage of |buildflags_dir| in this script).
919    if dep_name.startswith(gn_utils.BUILDFLAGS_TARGET):
920      continue
921
922    dep_module = create_modules_from_target(blueprint, gn, dep_name)
923
924    # For filegroups and genrule, recurse but don't apply the deps.
925    if not module_is_compiled:
926      continue
927
928    # |builtin_deps| override GN deps with Android-specific ones. See the
929    # config in the top of this file.
930    if gn_utils.label_without_toolchain(dep_name) in builtin_deps:
931      builtin_deps[gn_utils.label_without_toolchain(dep_name)](module)
932      continue
933
934    # Don't recurse in any other //gn dep if not handled by builtin_deps.
935    if dep_name.startswith('//gn:'):
936      continue
937
938    if dep_module is None:
939      continue
940    if dep_module.type == 'cc_library_shared':
941      module.shared_libs.add(dep_module.name)
942    elif dep_module.type == 'cc_library_static':
943      module.static_libs.add(dep_module.name)
944    elif dep_module.type == 'filegroup':
945      module.srcs.add(':' + dep_module.name)
946    elif dep_module.type == 'genrule':
947      module.generated_headers.update(dep_module.genrule_headers)
948      module.srcs.update(dep_module.genrule_srcs)
949      module.shared_libs.update(dep_module.genrule_shared_libs)
950    elif dep_module.type == 'cc_binary':
951      continue  # Ignore executables deps (used by cmdline integration tests).
952    else:
953      raise Error('Unknown dep %s (%s) for target %s' %
954                  (dep_module.name, dep_module.type, module.name))
955
956  return module
957
958
959def create_blueprint_for_targets(gn, desc, targets):
960  """Generate a blueprint for a list of GN targets."""
961  blueprint = Blueprint()
962
963  # Default settings used by all modules.
964  defaults = Module('cc_defaults', defaults_module, '//gn:default_deps')
965
966  # We have to use include_dirs passing the path relative to the android tree.
967  # This is because: (i) perfetto_cc_defaults is used also by
968  # test/**/Android.bp; (ii) if we use local_include_dirs instead, paths
969  # become relative to the Android.bp that *uses* cc_defaults (not the one
970  # that defines it).s
971  defaults.include_dirs = {
972      tree_path, tree_path + '/include', tree_path + '/' + buildflags_dir,
973      tree_path + '/src/profiling/memory/include'
974  }
975  defaults.cflags = [
976      '-Wno-error=return-type',
977      '-Wno-sign-compare',
978      '-Wno-sign-promo',
979      '-Wno-unused-parameter',
980      '-fvisibility=hidden',
981      '-O2',
982  ]
983  defaults.user_debug_flag = True
984  defaults.lto = True
985
986  blueprint.add_module(defaults)
987  gn = gn_utils.GnParser(desc)
988  for target in targets:
989    create_modules_from_target(blueprint, gn, target)
990  return blueprint
991
992
993def main():
994  parser = argparse.ArgumentParser(
995      description='Generate Android.bp from a GN description.')
996  parser.add_argument(
997      '--check-only',
998      help='Don\'t keep the generated files',
999      action='store_true')
1000  parser.add_argument(
1001      '--desc',
1002      help='GN description (e.g., gn desc out --format=json --all-toolchains "//*"'
1003  )
1004  parser.add_argument(
1005      '--extras',
1006      help='Extra targets to include at the end of the Blueprint file',
1007      default=os.path.join(gn_utils.repo_root(), 'Android.bp.extras'),
1008  )
1009  parser.add_argument(
1010      '--output',
1011      help='Blueprint file to create',
1012      default=os.path.join(gn_utils.repo_root(), 'Android.bp'),
1013  )
1014  parser.add_argument(
1015      'targets',
1016      nargs=argparse.REMAINDER,
1017      help='Targets to include in the blueprint (e.g., "//:perfetto_tests")')
1018  args = parser.parse_args()
1019
1020  if args.desc:
1021    with open(args.desc) as f:
1022      desc = json.load(f)
1023  else:
1024    desc = gn_utils.create_build_description(gn_args)
1025
1026  gn = gn_utils.GnParser(desc)
1027  blueprint = create_blueprint_for_targets(gn, desc, args.targets or
1028                                           default_targets)
1029  project_root = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
1030  tool_name = os.path.relpath(os.path.abspath(__file__), project_root)
1031
1032  # TODO(primiano): enable this on Android after the TODO in
1033  # perfetto_component.gni is fixed.
1034  # Check for ODR violations
1035  # for target_name in default_targets:
1036    # checker = gn_utils.ODRChecker(gn, target_name)
1037
1038  output = [
1039      """// Copyright (C) 2017 The Android Open Source Project
1040//
1041// Licensed under the Apache License, Version 2.0 (the "License");
1042// you may not use this file except in compliance with the License.
1043// You may obtain a copy of the License at
1044//
1045//      http://www.apache.org/licenses/LICENSE-2.0
1046//
1047// Unless required by applicable law or agreed to in writing, software
1048// distributed under the License is distributed on an "AS IS" BASIS,
1049// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1050// See the License for the specific language governing permissions and
1051// limitations under the License.
1052//
1053// This file is automatically generated by %s. Do not edit.
1054""" % (tool_name)
1055  ]
1056  blueprint.to_string(output)
1057  with open(args.extras, 'r') as r:
1058    for line in r:
1059      output.append(line.rstrip("\n\r"))
1060
1061  out_files = []
1062
1063  # Generate the Android.bp file.
1064  out_files.append(args.output + '.swp')
1065  with open(out_files[-1], 'w') as f:
1066    f.write('\n'.join(output))
1067    # Text files should have a trailing EOL.
1068    f.write('\n')
1069
1070  # Generate the perfetto_build_flags.h file.
1071  out_files.append(os.path.join(buildflags_dir, 'perfetto_build_flags.h.swp'))
1072  gn_utils.gen_buildflags(gn_args, out_files[-1])
1073
1074  # Either check the contents or move the files to their final destination.
1075  return gn_utils.check_or_commit_generated_files(out_files, args.check_only)
1076
1077
1078if __name__ == '__main__':
1079  sys.exit(main())
1080