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