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