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