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