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