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