1#!/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 json 30import logging as log 31import operator 32import os 33import re 34import sys 35import copy 36from pathlib import Path 37 38import gn_utils 39 40ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 41 42CRONET_LICENSE_NAME = "external_cronet_license" 43 44# Default targets to translate to the blueprint file. 45DEFAULT_TARGETS = [ 46 "//components/cronet/android:cronet_api_java", 47 '//components/cronet/android:cronet', 48 '//components/cronet/android:cronet_impl_native_java', 49 '//components/cronet/android:cronet_jni_registration_java', 50] 51 52DEFAULT_TESTS = [ 53 '//components/cronet/android:cronet_unittests_android__library', 54 '//net:net_unittests__library', 55 '//components/cronet/android:cronet_tests', 56 '//components/cronet/android:cronet', 57 '//components/cronet/android:cronet_javatests', 58 '//components/cronet/android:cronet_jni_registration_java', 59 '//components/cronet/android:cronet_tests_jni_registration_java', 60 '//testing/android/native_test:native_test_java', 61 '//net/android:net_test_support_provider_java', 62 '//net/android:net_tests_java', 63 '//third_party/netty-tcnative:netty-tcnative-so', 64 '//third_party/netty4:netty_all_java', 65] 66 67EXTRAS_ANDROID_BP_FILE = "Android.extras.bp" 68 69CRONET_API_MODULE_NAME = "cronet_aml_api_java" 70 71# All module names are prefixed with this string to avoid collisions. 72module_prefix = 'cronet_aml_' 73 74REMOVE_GEN_JNI_JARJAR_RULES_FILE = "android/tools/remove-gen-jni-jarjar-rules.txt" 75# Shared libraries which are directly translated to Android system equivalents. 76shared_library_allowlist = [ 77 'android', 78 'log', 79] 80 81# Include directories that will be removed from all targets. 82local_include_dirs_denylist = [ 83 'third_party/zlib/', 84] 85 86# Name of the module which settings such as compiler flags for all other 87# modules. 88defaults_module = module_prefix + 'defaults' 89 90# Location of the project in the Android source tree. 91tree_path = 'external/cronet' 92 93# Path for the protobuf sources in the standalone build. 94buildtools_protobuf_src = '//buildtools/protobuf/src' 95 96# Location of the protobuf src dir in the Android source tree. 97android_protobuf_src = 'external/protobuf/src' 98 99# put all args on a new line for better diffs. 100NEWLINE = ' " +\n "' 101 102# Compiler flags which are passed through to the blueprint. 103cflag_allowlist = [ 104 # needed for zlib:zlib 105 "-mpclmul", 106 # needed for zlib:zlib 107 "-mssse3", 108 # needed for zlib:zlib 109 "-msse3", 110 # needed for zlib:zlib 111 "-msse4.2", 112 # flags to reduce binary size 113 "-O1", 114 "-O2", 115 "-O3", 116 "-Oz", 117 "-g1", 118 "-g2", 119 "-fdata-sections", 120 "-ffunction-sections", 121 "-fvisibility=hidden", 122 "-fvisibility-inlines-hidden", 123 "-fstack-protector", 124 "-mno-outline", 125 "-mno-outline-atomics", 126 "-fno-asynchronous-unwind-tables", 127 "-fno-unwind-tables", 128] 129 130# Linker flags which are passed through to the blueprint. 131ldflag_allowlist = [ 132 # flags to reduce binary size 133 "-Wl,--as-needed", 134 "-Wl,--gc-sections", 135 "-Wl,--icf=all", 136] 137 138 139def get_linker_script_ldflag(script_path): 140 return f'-Wl,--script,{tree_path}/{script_path}' 141 142 143# Additional arguments to apply to Android.bp rules. 144additional_args = { 145 'cronet_aml_net_third_party_quiche_net_quic_test_tools_proto_gen_headers': 146 [('export_include_dirs', { 147 "net/third_party/quiche/src", 148 })], 149 'cronet_aml_net_third_party_quiche_net_quic_test_tools_proto_gen__testing_headers': 150 [('export_include_dirs', { 151 "net/third_party/quiche/src", 152 })], 153 'cronet_aml_third_party_quic_trace_quic_trace_proto_gen__testing_headers': 154 [('export_include_dirs', { 155 "third_party/quic_trace/src", 156 })], 157 # TODO: fix upstream. Both //base:base and 158 # //base/allocator/partition_allocator:partition_alloc do not create a 159 # dependency on gtest despite using gtest_prod.h. 160 'cronet_aml_base_base': [ 161 ('header_libs', { 162 'libgtest_prod_headers', 163 }), 164 ('export_header_lib_headers', { 165 'libgtest_prod_headers', 166 }), 167 ], 168 'cronet_aml_base_allocator_partition_allocator_partition_alloc': [ 169 ('header_libs', { 170 'libgtest_prod_headers', 171 }), 172 ], 173 # TODO(b/309920629): Remove once upstreamed. 174 'cronet_aml_components_cronet_android_cronet_api_java': [ 175 ('srcs', { 176 'components/cronet/android/api/src/org/chromium/net/UploadDataProviders.java', 177 'components/cronet/android/api/src/org/chromium/net/apihelpers/UploadDataProviders.java', 178 }), 179 ], 180 'cronet_aml_components_cronet_android_cronet_api_java__testing': [ 181 ('srcs', { 182 'components/cronet/android/api/src/org/chromium/net/UploadDataProviders.java', 183 'components/cronet/android/api/src/org/chromium/net/apihelpers/UploadDataProviders.java', 184 }), 185 ], 186 'cronet_aml_components_cronet_android_cronet_javatests__testing': [ 187 # Needed to @SkipPresubmit annotations 188 ('static_libs', { 189 'net-tests-utils', 190 }), 191 # This is necessary because net-tests-utils compiles against private SDK. 192 ('sdk_version', ""), 193 ], 194 'cronet_aml_testing_android_native_test_native_test_java__testing': [ 195 ('srcs', { 196 'testing/android/native_test/java/src/org/chromium/native_test/SignalMaskInfo.java', 197 }), 198 ], 199 'cronet_aml_components_cronet_android_cronet__testing': [ 200 ('target', ('android_riscv64', { 201 'stem': "libmainlinecronet_riscv64" 202 })), 203 ('comment', """TODO: remove stem for riscv64 204// This is essential as there can't be two different modules 205// with the same output. We usually got away with that because 206// the non-testing Cronet is part of the Tethering APEX and the 207// testing Cronet is not part of the Tethering APEX which made them 208// look like two different outputs from the build system perspective. 209// However, Cronet does not ship to Tethering APEX for RISCV64 which 210// raises the conflict. Once we start shipping Cronet for RISCV64, 211// this can be removed."""), 212 ], 213 'cronet_aml_third_party_netty_tcnative_netty_tcnative_so__testing': 214 [('cflags', {"-Wno-error=pointer-bool-conversion"})], 215 'cronet_aml_third_party_apache_portable_runtime_apr__testing': [ 216 ('cflags', { 217 "-Wno-incompatible-pointer-types-discards-qualifiers", 218 }) 219 ] 220} 221 222 223def always_disable(module, arch): 224 return None 225 226 227def enable_zlib(module, arch): 228 # Requires crrev/c/4109079 229 if arch == 'common': 230 module.shared_libs.add('libz') 231 else: 232 module.target[arch].shared_libs.add('libz') 233 234 235def enable_boringssl(module, arch): 236 # Do not add boringssl targets to cc_genrules. This happens, because protobuf targets are 237 # originally static_libraries, but later get converted to a cc_genrule. 238 if module.is_genrule(): return 239 # Lets keep statically linking BoringSSL for testing target for now. This should be fixed. 240 if module.name.endswith(gn_utils.TESTING_SUFFIX): return 241 if arch == 'common': 242 shared_libs = module.shared_libs 243 else: 244 shared_libs = module.target[arch].shared_libs 245 shared_libs.add('//external/cronet/third_party/boringssl:libcrypto') 246 shared_libs.add('//external/cronet/third_party/boringssl:libssl') 247 248 249def add_androidx_experimental_java_deps(module, arch): 250 module.libs.add("androidx.annotation_annotation-experimental-nodeps") 251 252 253def add_androidx_annotation_java_deps(module, arch): 254 module.libs.add("androidx.annotation_annotation-nodeps") 255 256 257def add_protobuf_lite_runtime_java_deps(module, arch): 258 module.static_libs.add("libprotobuf-java-lite") 259 260 261def add_androidx_core_java_deps(module, arch): 262 module.libs.add("androidx.core_core-nodeps") 263 264 265def add_jsr305_java_deps(module, arch): 266 module.libs.add("jsr305") 267 268 269def add_errorprone_annotation_java_deps(module, arch): 270 module.libs.add("error_prone_annotations") 271 272 273def add_androidx_collection_java_deps(module, arch): 274 module.libs.add("androidx.collection_collection-nodeps") 275 276 277def add_junit_java_deps(module, arch): 278 module.static_libs.add("junit") 279 280 281def add_truth_java_deps(module, arch): 282 module.static_libs.add("truth") 283 284 285def add_hamcrest_java_deps(module, arch): 286 module.static_libs.add("hamcrest-library") 287 module.static_libs.add("hamcrest") 288 289 290def add_mockito_java_deps(module, arch): 291 module.static_libs.add("mockito") 292 293 294def add_guava_java_deps(module, arch): 295 module.static_libs.add("guava") 296 297 298def add_androidx_junit_java_deps(module, arch): 299 module.static_libs.add("androidx.test.ext.junit") 300 301 302def add_androidx_test_runner_java_deps(module, arch): 303 module.static_libs.add("androidx.test.runner") 304 305def add_androidx_test_rules_java_deps(module, arch): 306 module.static_libs.add("androidx.test.rules") 307 308 309def add_android_test_base_java_deps(module, arch): 310 module.libs.add("android.test.base") 311 312 313def add_accessibility_test_framework_java_deps(module, arch): 314 module.static_libs.add("accessibility-test-framework") 315 316 317def add_espresso_java_deps(module, arch): 318 module.static_libs.add("androidx.test.espresso.contrib") 319 320 321def add_android_test_mock_java_deps(module, arch): 322 module.libs.add("android.test.mock") 323 324 325def add_androidx_multidex_java_deps(module, arch): 326 # Androidx-multidex is disabled on unbundled branches. 327 pass 328 329 330def add_androidx_test_monitor_java_deps(module, arch): 331 module.libs.add("androidx.test.monitor") 332 333 334def add_androidx_ui_automator_java_deps(module, arch): 335 module.static_libs.add("androidx.test.uiautomator_uiautomator") 336 337 338def add_androidx_test_annotation_java_deps(module, arch): 339 module.static_libs.add("androidx.test.rules") 340 341 342def add_androidx_test_core_java_deps(module, arch): 343 module.static_libs.add("androidx.test.core") 344 345 346def add_androidx_activity_activity(module, arch): 347 module.static_libs.add("androidx.activity_activity") 348 349 350def add_androidx_fragment_fragment(module, arch): 351 module.static_libs.add("androidx.fragment_fragment") 352 353 354# Android equivalents for third-party libraries that the upstream project 355# depends on. This will be applied to normal and testing targets. 356_builtin_deps = { 357 '//buildtools/third_party/libunwind:libunwind': 358 always_disable, 359 '//net/data/ssl/chrome_root_store:gen_root_store_inc': 360 always_disable, 361 '//net/tools/root_store_tool:root_store_tool': 362 always_disable, 363 '//third_party/zlib:zlib': 364 enable_zlib, 365 '//third_party/androidx:androidx_annotation_annotation_java': 366 add_androidx_annotation_java_deps, 367 '//third_party/android_deps:protobuf_lite_runtime_java': 368 add_protobuf_lite_runtime_java_deps, 369 '//third_party/androidx:androidx_annotation_annotation_experimental_java': 370 add_androidx_experimental_java_deps, 371 '//third_party/androidx:androidx_core_core_java': 372 add_androidx_core_java_deps, 373 '//third_party/android_deps:com_google_code_findbugs_jsr305_java': 374 add_jsr305_java_deps, 375 '//third_party/android_deps:com_google_errorprone_error_prone_annotations_java': 376 add_errorprone_annotation_java_deps, 377 '//third_party/androidx:androidx_collection_collection_java': 378 add_androidx_collection_java_deps, 379 '//third_party/junit:junit': 380 add_junit_java_deps, 381 '//third_party/google-truth:google_truth_java': 382 add_truth_java_deps, 383 '//third_party/hamcrest:hamcrest_core_java': 384 add_hamcrest_java_deps, 385 '//third_party/mockito:mockito_java': 386 add_mockito_java_deps, 387 '//third_party/android_deps:guava_android_java': 388 add_guava_java_deps, 389 '//third_party/androidx:androidx_test_ext_junit_java': 390 add_androidx_junit_java_deps, 391 '//third_party/androidx:androidx_test_runner_java': 392 add_androidx_test_runner_java_deps, 393 '//third_party/android_sdk:android_test_base_java': 394 add_android_test_base_java_deps, 395 '//third_party/accessibility_test_framework:accessibility_test_framework_java': 396 add_accessibility_test_framework_java_deps, 397 '//third_party/accessibility_test_framework:accessibility_core_java': 398 add_accessibility_test_framework_java_deps, 399 '//third_party/android_deps:espresso_java': 400 add_espresso_java_deps, 401 '//third_party/android_sdk:android_test_mock_java': 402 add_android_test_mock_java_deps, 403 '//third_party/androidx:androidx_multidex_multidex_java': 404 add_androidx_multidex_java_deps, 405 '//third_party/androidx:androidx_test_monitor_java': 406 add_androidx_test_monitor_java_deps, 407 '//third_party/androidx:androidx_test_annotation_java': 408 add_androidx_test_annotation_java_deps, 409 '//third_party/androidx:androidx_test_core_java': 410 add_androidx_test_core_java_deps, 411 '//third_party/androidx:androidx_test_uiautomator_uiautomator_java': 412 add_androidx_ui_automator_java_deps, 413 '//third_party/hamcrest:hamcrest_java': 414 add_hamcrest_java_deps, 415 '//third_party/androidx:androidx_activity_activity_java': 416 add_androidx_activity_activity, 417 '//third_party/androidx:androidx_fragment_fragment_java': 418 add_androidx_fragment_fragment, 419 '//third_party/androidx:androidx_test_rules_java': 420 add_androidx_test_rules_java_deps, 421} 422builtin_deps = { 423 "{}{}".format(key, suffix): value 424 for key, value in _builtin_deps.items() 425 for suffix in ["", gn_utils.TESTING_SUFFIX] 426} 427 428# Same as _builtin_deps but will only apply what is explicitly specified. 429builtin_deps.update({ 430 '//third_party/boringssl:boringssl': enable_boringssl, 431 '//third_party/boringssl:boringssl_asm': 432 # Due to FIPS requirements, downstream BoringSSL has a different "shape" than upstream's. 433 # We're guaranteed that if X depends on :boringssl it will also depend on :boringssl_asm. 434 # Hence, always drop :boringssl_asm and handle the translation entirely in :boringssl. 435 always_disable, 436}) 437 438# Name of tethering apex module 439tethering_apex = "com.android.tethering" 440 441# Name of cronet api target 442java_api_target_name = "//components/cronet/android:cronet_api_java" 443 444# Visibility set for package default 445package_default_visibility = ":__subpackages__" 446 447# Visibility set for modules used from Connectivity and within external/cronet 448root_modules_visibility = { 449 "//packages/modules/Connectivity:__subpackages__", 450 "//external/cronet:__subpackages__" 451} 452 453# ---------------------------------------------------------------------------- 454# End of configuration. 455# ---------------------------------------------------------------------------- 456 457 458def write_blueprint_key_value(output, name, value, sort=True): 459 """Writes a Blueprint key-value pair to the output""" 460 461 if isinstance(value, bool): 462 if value: 463 output.append(' %s: true,' % name) 464 else: 465 output.append(' %s: false,' % name) 466 return 467 if not value: 468 return 469 if isinstance(value, set): 470 value = sorted(value) 471 if isinstance(value, list): 472 output.append(' %s: [' % name) 473 for item in sorted(value) if sort else value: 474 output.append(' "%s",' % item) 475 output.append(' ],') 476 return 477 if isinstance(value, Module.Target): 478 value.to_string(output) 479 return 480 if isinstance(value, dict): 481 kv_output = [] 482 for k, v in value.items(): 483 write_blueprint_key_value(kv_output, k, v) 484 485 output.append(' %s: {' % name) 486 for line in kv_output: 487 output.append(' %s' % line) 488 output.append(' },') 489 return 490 output.append(' %s: "%s",' % (name, value)) 491 492 493class Module(object): 494 """A single module (e.g., cc_binary, cc_test) in a blueprint.""" 495 496 class Target(object): 497 """A target-scoped part of a module""" 498 499 def __init__(self, name): 500 self.name = name 501 self.srcs = set() 502 self.shared_libs = set() 503 self.static_libs = set() 504 self.whole_static_libs = set() 505 self.header_libs = set() 506 self.cflags = set() 507 self.stl = None 508 self.cppflags = set() 509 self.local_include_dirs = set() 510 self.generated_headers = set() 511 self.export_generated_headers = set() 512 self.ldflags = set() 513 self.compile_multilib = None 514 self.stem = "" 515 if name == 'host': 516 self.compile_multilib = '64' 517 518 def to_string(self, output): 519 nested_out = [] 520 self._output_field(nested_out, 'srcs') 521 self._output_field(nested_out, 'shared_libs') 522 self._output_field(nested_out, 'static_libs') 523 self._output_field(nested_out, 'whole_static_libs') 524 self._output_field(nested_out, 'header_libs') 525 self._output_field(nested_out, 'cflags') 526 self._output_field(nested_out, 'stl') 527 self._output_field(nested_out, 'cppflags') 528 self._output_field(nested_out, 'local_include_dirs') 529 self._output_field(nested_out, 'generated_headers') 530 self._output_field(nested_out, 'export_generated_headers') 531 self._output_field(nested_out, 'ldflags') 532 self._output_field(nested_out, 'stem') 533 534 if nested_out: 535 # This is added here to make sure it doesn't add a `host` arch-specific module just for 536 # `compile_multilib` flag. 537 self._output_field(nested_out, 'compile_multilib') 538 output.append(' %s: {' % self.name) 539 for line in nested_out: 540 output.append(' %s' % line) 541 output.append(' },') 542 543 def _output_field(self, output, name, sort=True): 544 value = getattr(self, name) 545 return write_blueprint_key_value(output, name, value, sort) 546 547 def __init__(self, mod_type, name, gn_target): 548 self.type = mod_type 549 self.gn_target = gn_target 550 self.name = name 551 self.srcs = set() 552 self.comment = 'GN: ' + gn_target 553 self.shared_libs = set() 554 self.static_libs = set() 555 self.whole_static_libs = set() 556 self.tools = set() 557 self.cmd = None 558 self.host_supported = False 559 self.device_supported = True 560 self.init_rc = set() 561 self.out = set() 562 self.export_include_dirs = set() 563 self.generated_headers = set() 564 self.export_generated_headers = set() 565 self.export_static_lib_headers = set() 566 self.export_header_lib_headers = set() 567 self.defaults = set() 568 self.cflags = set() 569 self.include_dirs = set() 570 self.local_include_dirs = set() 571 self.header_libs = set() 572 self.tool_files = set() 573 # target contains a dict of Targets indexed by os_arch. 574 # example: { 'android_x86': Target('android_x86') 575 self.target = dict() 576 self.target['android'] = self.Target('android') 577 self.target['android_x86'] = self.Target('android_x86') 578 self.target['android_x86_64'] = self.Target('android_x86_64') 579 self.target['android_arm'] = self.Target('android_arm') 580 self.target['android_arm64'] = self.Target('android_arm64') 581 self.target['android_riscv64'] = self.Target('android_riscv64') 582 self.target['host'] = self.Target('host') 583 self.target['glibc'] = self.Target('glibc') 584 self.stl = None 585 self.cpp_std = None 586 self.strip = dict() 587 self.data = set() 588 self.apex_available = set() 589 self.min_sdk_version = None 590 self.proto = dict() 591 self.linker_scripts = set() 592 self.ldflags = set() 593 # The genrule_XXX below are properties that must to be propagated back 594 # on the module(s) that depend on the genrule. 595 self.genrule_headers = set() 596 self.genrule_srcs = set() 597 self.genrule_shared_libs = set() 598 self.genrule_header_libs = set() 599 self.version_script = None 600 self.test_suites = set() 601 self.test_config = None 602 self.cppflags = set() 603 self.rtti = False 604 # Name of the output. Used for setting .so file name for libcronet 605 self.libs = set() 606 self.stem = None 607 self.compile_multilib = None 608 self.aidl = dict() 609 self.plugins = set() 610 self.processor_class = None 611 self.sdk_version = None 612 self.javacflags = set() 613 self.c_std = None 614 self.default_applicable_licenses = set() 615 self.default_visibility = [] 616 self.visibility = set() 617 self.gn_type = None 618 self.jarjar_rules = "" 619 self.jars = set() 620 621 def to_string(self, output): 622 if self.comment: 623 output.append('// %s' % self.comment) 624 output.append('%s {' % self.type) 625 self._output_field(output, 'name') 626 self._output_field(output, 'srcs') 627 self._output_field(output, 'shared_libs') 628 self._output_field(output, 'static_libs') 629 self._output_field(output, 'whole_static_libs') 630 self._output_field(output, 'tools') 631 self._output_field(output, 'cmd', sort=False) 632 if self.host_supported: 633 self._output_field(output, 'host_supported') 634 if not self.device_supported: 635 self._output_field(output, 'device_supported') 636 self._output_field(output, 'init_rc') 637 self._output_field(output, 'out') 638 self._output_field(output, 'export_include_dirs') 639 self._output_field(output, 'generated_headers') 640 self._output_field(output, 'export_generated_headers') 641 self._output_field(output, 'export_static_lib_headers') 642 self._output_field(output, 'export_header_lib_headers') 643 self._output_field(output, 'defaults') 644 self._output_field(output, 'cflags') 645 self._output_field(output, 'include_dirs') 646 self._output_field(output, 'local_include_dirs') 647 self._output_field(output, 'header_libs') 648 self._output_field(output, 'strip') 649 self._output_field(output, 'tool_files') 650 self._output_field(output, 'data') 651 self._output_field(output, 'stl') 652 self._output_field(output, 'cpp_std') 653 self._output_field(output, 'apex_available') 654 self._output_field(output, 'min_sdk_version') 655 self._output_field(output, 'version_script') 656 self._output_field(output, 'test_suites') 657 self._output_field(output, 'test_config') 658 self._output_field(output, 'proto') 659 self._output_field(output, 'linker_scripts') 660 self._output_field(output, 'ldflags') 661 self._output_field(output, 'cppflags') 662 self._output_field(output, 'libs') 663 self._output_field(output, 'stem') 664 self._output_field(output, 'compile_multilib') 665 self._output_field(output, 'aidl') 666 self._output_field(output, 'plugins') 667 self._output_field(output, 'processor_class') 668 self._output_field(output, 'sdk_version') 669 self._output_field(output, 'javacflags') 670 self._output_field(output, 'c_std') 671 self._output_field(output, 'default_applicable_licenses') 672 self._output_field(output, 'default_visibility') 673 self._output_field(output, 'visibility') 674 self._output_field(output, 'jarjar_rules') 675 self._output_field(output, 'jars') 676 if self.rtti: 677 self._output_field(output, 'rtti') 678 679 target_out = [] 680 for arch, target in sorted(self.target.items()): 681 # _output_field calls getattr(self, arch). 682 setattr(self, arch, target) 683 self._output_field(target_out, arch) 684 685 if target_out: 686 output.append(' target: {') 687 for line in target_out: 688 output.append(' %s' % line) 689 output.append(' },') 690 691 output.append('}') 692 output.append('') 693 694 def add_android_shared_lib(self, lib): 695 if self.type == 'cc_binary_host': 696 raise Exception( 697 'Adding Android shared lib for host tool is unsupported') 698 elif self.host_supported: 699 self.target['android'].shared_libs.add(lib) 700 else: 701 self.shared_libs.add(lib) 702 703 def is_test(self): 704 if gn_utils.TESTING_SUFFIX in self.name: 705 name_without_prefix = self.name[:self.name.find(gn_utils. 706 TESTING_SUFFIX)] 707 return any([ 708 name_without_prefix == label_to_module_name(target) 709 for target in DEFAULT_TESTS 710 ]) 711 return False 712 713 def _output_field(self, output, name, sort=True): 714 value = getattr(self, name) 715 return write_blueprint_key_value(output, name, value, sort) 716 717 def is_compiled(self): 718 return self.type not in ('cc_genrule', 'filegroup', 'java_genrule') 719 720 def is_genrule(self): 721 return self.type == "cc_genrule" 722 723 def has_input_files(self): 724 if self.type in ["java_library", "java_import"]: 725 return True 726 if len(self.srcs) > 0: 727 return True 728 if any([len(target.srcs) > 0 for target in self.target.values()]): 729 return True 730 # Allow cc_static_library with export_generated_headers as those are crucial for 731 # the depending modules 732 return len(self.export_generated_headers) > 0 733 734 735class Blueprint(object): 736 """In-memory representation of an Android.bp file.""" 737 738 def __init__(self): 739 self.modules = {} 740 741 def add_module(self, module): 742 """Adds a new module to the blueprint, replacing any existing module 743 with the same name. 744 745 Args: 746 module: Module instance. 747 """ 748 self.modules[module.name] = module 749 750 def to_string(self, output): 751 for m in sorted(self.modules.values(), key=lambda m: m.name): 752 if m.type != "cc_library_static" or m.has_input_files(): 753 # Don't print cc_library_static with empty srcs. These attributes are already 754 # propagated up the tree. Printing them messes the presubmits because 755 # every module is compiled while those targets are not reachable in 756 # a normal compilation path. 757 m.to_string(output) 758 759 760def label_to_module_name(label): 761 """Turn a GN label (e.g., //:perfetto_tests) into a module name.""" 762 module = re.sub(r'^//:?', '', label) 763 module = re.sub(r'[^a-zA-Z0-9_]', '_', module) 764 765 if not module.startswith(module_prefix): 766 return module_prefix + module 767 return module 768 769 770def is_supported_source_file(name): 771 """Returns True if |name| can appear in a 'srcs' list.""" 772 return os.path.splitext(name)[1] in [ 773 '.c', '.cc', '.cpp', '.java', '.proto', '.S', '.aidl' 774 ] 775 776 777def get_protoc_module_name(gn): 778 protoc_gn_target_name = gn.get_target('//third_party/protobuf:protoc').name 779 return label_to_module_name(protoc_gn_target_name) 780 781 782def create_proto_modules(blueprint, gn, target): 783 """Generate genrules for a proto GN target. 784 785 GN actions are used to dynamically generate files during the build. The 786 Soong equivalent is a genrule. This function turns a specific kind of 787 genrule which turns .proto files into source and header files into a pair 788 equivalent genrules. 789 790 Args: 791 blueprint: Blueprint instance which is being generated. 792 target: gn_utils.Target object. 793 794 Returns: 795 The source_genrule module. 796 """ 797 assert (target.type == 'proto_library') 798 799 protoc_module_name = get_protoc_module_name(gn) 800 tools = {protoc_module_name} 801 cpp_out_dir = '$(genDir)/%s/%s/' % (tree_path, target.proto_in_dir) 802 target_module_name = label_to_module_name(target.name) 803 804 # In GN builds the proto path is always relative to the output directory 805 # (out/tmp.xxx). 806 cmd = ['$(location %s)' % protoc_module_name] 807 cmd += ['--proto_path=%s/%s' % (tree_path, target.proto_in_dir)] 808 809 for proto_path in target.proto_paths: 810 cmd += [f'--proto_path={tree_path}/{proto_path}'] 811 if buildtools_protobuf_src in target.proto_paths: 812 cmd += ['--proto_path=%s' % android_protobuf_src] 813 814 # We don't generate any targets for source_set proto modules because 815 # they will be inlined into other modules if required. 816 if target.proto_plugin == 'source_set': 817 return None 818 819 # Descriptor targets only generate a single target. 820 if target.proto_plugin == 'descriptor': 821 out = '{}.bin'.format(target_module_name) 822 823 cmd += ['--descriptor_set_out=$(out)'] 824 cmd += ['$(in)'] 825 826 descriptor_module = Module('cc_genrule', target_module_name, 827 target.name) 828 descriptor_module.cmd = ' '.join(cmd) 829 descriptor_module.out = [out] 830 descriptor_module.tools = tools 831 blueprint.add_module(descriptor_module) 832 833 # Recursively extract the .proto files of all the dependencies and 834 # add them to srcs. 835 descriptor_module.srcs.update( 836 gn_utils.label_to_path(src) for src in target.sources) 837 for dep in target.proto_deps: 838 current_target = gn.get_target(dep) 839 descriptor_module.srcs.update( 840 gn_utils.label_to_path(src) for src in current_target.sources) 841 842 return descriptor_module 843 844 # We create two genrules for each proto target: one for the headers and 845 # another for the sources. This is because the module that depends on the 846 # generated files needs to declare two different types of dependencies -- 847 # source files in 'srcs' and headers in 'generated_headers' -- and it's not 848 # valid to generate .h files from a source dependency and vice versa. 849 source_module_name = target_module_name 850 source_module = Module('cc_genrule', source_module_name, target.name) 851 blueprint.add_module(source_module) 852 source_module.srcs.update( 853 gn_utils.label_to_path(src) for src in target.sources) 854 855 header_module = Module('cc_genrule', source_module_name + '_headers', 856 target.name) 857 blueprint.add_module(header_module) 858 header_module.srcs = set(source_module.srcs) 859 860 # TODO(primiano): at some point we should remove this. This was introduced 861 # by aosp/1108421 when adding "protos/" to .proto include paths, in order to 862 # avoid doing multi-repo changes and allow old clients in the android tree 863 # to still do the old #include "perfetto/..." rather than 864 # #include "protos/perfetto/...". 865 header_module.export_include_dirs = {'.', 'protos'} 866 # Since the .cc file and .h get created by a different gerule target, they 867 # are not put in the same intermediate path, so local includes do not work 868 # without explictily exporting the include dir. 869 header_module.export_include_dirs.add(target.proto_in_dir) 870 871 # This function does not return header_module so setting apex_available attribute here. 872 header_module.apex_available.add(tethering_apex) 873 874 source_module.genrule_srcs.add(':' + source_module.name) 875 source_module.genrule_headers.add(header_module.name) 876 877 if target.proto_plugin == 'proto': 878 suffixes = ['pb'] 879 source_module.genrule_shared_libs.add('libprotobuf-cpp-lite') 880 cmd += ['--cpp_out=lite=true:' + cpp_out_dir] 881 else: 882 raise Exception('Unsupported proto plugin: %s' % target.proto_plugin) 883 884 cmd += ['$(in)'] 885 source_module.cmd = ' '.join(cmd) 886 header_module.cmd = source_module.cmd 887 source_module.tools = tools 888 header_module.tools = tools 889 890 for sfx in suffixes: 891 source_module.out.update( 892 '%s/%s' % (tree_path, src.replace('.proto', '.%s.cc' % sfx)) 893 for src in source_module.srcs) 894 header_module.out.update( 895 '%s/%s' % (tree_path, src.replace('.proto', '.%s.h' % sfx)) 896 for src in header_module.srcs) 897 # This has proto files that will be used for reference resolution 898 # but not compiled into cpp files. These additional sources has no output. 899 proto_data_sources = sorted([ 900 gn_utils.label_to_path(proto_src) for proto_src in target.inputs 901 if proto_src.endswith(".proto") 902 ]) 903 source_module.srcs.update(proto_data_sources) 904 header_module.srcs.update(proto_data_sources) 905 return source_module 906 907 908def create_gcc_preprocess_modules(blueprint, target): 909 # gcc_preprocess.py internally execute host gcc which is not allowed in genrule. 910 # So, this function create multiple modules and realize equivalent processing 911 # TODO: Consider to support gcc_preprocess.py in different way 912 # It's not great to have genrule and cc_object in the dependency from java_library 913 assert (len(target.sources) == 1) 914 source = list(target.sources)[0] 915 assert (Path(source).suffix == '.template') 916 stem = Path(source).stem 917 918 bp_module_name = label_to_module_name(target.name) 919 920 # Rename .template to .cc since cc_object does not accept .template file as srcs 921 rename_module = Module('genrule', bp_module_name + '_rename', target.name) 922 rename_module.srcs.add(gn_utils.label_to_path(source)) 923 rename_module.out.add(stem + '.cc') 924 rename_module.cmd = 'cp $(in) $(out)' 925 blueprint.add_module(rename_module) 926 927 # Preprocess template file and generates java file 928 preprocess_module = Module('cc_object', bp_module_name + '_preprocess', 929 target.name) 930 # -E: stop after preprocessing. 931 # -P: disable line markers, i.e. '#line 309' 932 preprocess_module.cflags.update(['-E', '-P', '-DANDROID']) 933 preprocess_module.srcs.add(':' + rename_module.name) 934 defines = [ 935 '-D' + target.args[i + 1] for i, arg in enumerate(target.args) 936 if arg == '--define' 937 ] 938 preprocess_module.cflags.update(defines) 939 # HACK: Specifying compile_multilib to build cc_object only once. 940 # Without this, soong complain to genrule that depends on cc_object when built for 64bit target. 941 # It seems this is because cc object is a module with per-architecture variants and genrule is a 942 # module with default variant. For 64bit target, cc_object is built multiple times for 32/64bit 943 # modes and genrule doesn't know which one to depend on. 944 preprocess_module.compile_multilib = 'first' 945 blueprint.add_module(preprocess_module) 946 947 # Generates srcjar using soong_zip 948 module = Module('genrule', bp_module_name, target.name) 949 module.srcs.add(':' + preprocess_module.name) 950 module.out.add(stem + '.srcjar') 951 module.cmd = NEWLINE.join([ 952 f'cp $(in) $(genDir)/{stem}.java &&', 953 f'$(location soong_zip) -o $(out) -srcjar -C $(genDir) -f $(genDir)/{stem}.java' 954 ]) 955 module.tools.add('soong_zip') 956 blueprint.add_module(module) 957 return module 958 959 960class BaseActionSanitizer(): 961 962 def __init__(self, target, arch): 963 # Just to be on the safe side, create a deep-copy. 964 self.target = copy.deepcopy(target) 965 if arch: 966 # Merge arch specific attributes 967 self.target.sources |= arch.sources 968 self.target.inputs |= arch.inputs 969 self.target.outputs |= arch.outputs 970 self.target.script = self.target.script or arch.script 971 self.target.args = self.target.args or arch.args 972 self.target.response_file_contents = \ 973 self.target.response_file_contents or arch.response_file_contents 974 self.target.args = self._normalize_args() 975 976 def get_name(self): 977 return label_to_module_name(self.target.name) 978 979 def _normalize_args(self): 980 # Convert ['--param=value'] to ['--param', 'value'] for consistency. 981 # Escape quotations. 982 normalized_args = [] 983 for arg in self.target.args: 984 arg = arg.replace('"', r'\"') 985 if arg.startswith('-'): 986 normalized_args.extend(arg.split('=')) 987 else: 988 normalized_args.append(arg) 989 return normalized_args 990 991 # There are three types of args: 992 # - flags (--flag) 993 # - value args (--arg value) 994 # - list args (--arg value1 --arg value2) 995 # value args have exactly one arg value pair and list args have one or more arg value pairs. 996 # Note that the set of list args contains the set of value args. 997 # This is because list and value args are identical when the list args has only one arg value pair 998 # Some functions provide special implementations for each type, while others 999 # work on all of them. 1000 def _has_arg(self, arg): 1001 return arg in self.target.args 1002 1003 def _get_arg_indices(self, target_arg): 1004 return [ 1005 i for i, arg in enumerate(self.target.args) if arg == target_arg 1006 ] 1007 1008 # Whether an arg value pair appears once or more times 1009 def _is_list_arg(self, arg): 1010 indices = self._get_arg_indices(arg) 1011 return len(indices) > 0 and all( 1012 [not self.target.args[i + 1].startswith('--') for i in indices]) 1013 1014 def _update_list_arg(self, arg, func, throw_if_absent=True): 1015 if self._should_fail_silently(arg, throw_if_absent): 1016 return 1017 assert (self._is_list_arg(arg)) 1018 indices = self._get_arg_indices(arg) 1019 for i in indices: 1020 self._set_arg_at(i + 1, func(self.target.args[i + 1])) 1021 1022 # Whether an arg value pair appears exactly once 1023 def _is_value_arg(self, arg): 1024 return operator.countOf(self.target.args, 1025 arg) == 1 and self._is_list_arg(arg) 1026 1027 def _get_value_arg(self, arg): 1028 assert (self._is_value_arg(arg)) 1029 i = self.target.args.index(arg) 1030 return self.target.args[i + 1] 1031 1032 # used to check whether a function call should cause an error when an arg is 1033 # missing. 1034 def _should_fail_silently(self, arg, throw_if_absent): 1035 return not throw_if_absent and not self._has_arg(arg) 1036 1037 def _set_value_arg(self, arg, value, throw_if_absent=True): 1038 if self._should_fail_silently(arg, throw_if_absent): 1039 return 1040 assert (self._is_value_arg(arg)) 1041 i = self.target.args.index(arg) 1042 self.target.args[i + 1] = value 1043 1044 def _update_value_arg(self, arg, func, throw_if_absent=True): 1045 if self._should_fail_silently(arg, throw_if_absent): 1046 return 1047 self._set_value_arg(arg, func(self._get_value_arg(arg))) 1048 1049 def _set_arg_at(self, position, value): 1050 self.target.args[position] = value 1051 1052 def _update_arg_at(self, position, func): 1053 self.target.args[position] = func(self.target.args[position]) 1054 1055 def _delete_value_arg(self, arg, throw_if_absent=True): 1056 if self._should_fail_silently(arg, throw_if_absent): 1057 return 1058 assert (self._is_value_arg(arg)) 1059 i = self.target.args.index(arg) 1060 self.target.args.pop(i) 1061 self.target.args.pop(i) 1062 1063 def _append_arg(self, arg, value): 1064 self.target.args.append(arg) 1065 self.target.args.append(value) 1066 1067 def _sanitize_filepath_with_location_tag(self, arg): 1068 if arg.startswith('../../'): 1069 arg = self._sanitize_filepath(arg) 1070 arg = self._add_location_tag(arg) 1071 return arg 1072 1073 # wrap filename in location tag. 1074 def _add_location_tag(self, filename): 1075 return '$(location %s)' % filename 1076 1077 # applies common directory transformation that *should* be universally applicable. 1078 # TODO: verify if it actually *is* universally applicable. 1079 def _sanitize_filepath(self, filepath): 1080 # Careful, order matters! 1081 # delete all leading ../ 1082 filepath = re.sub('^(\.\./)+', '', filepath) 1083 filepath = re.sub('^gen/jni_headers', '$(genDir)', filepath) 1084 filepath = re.sub('^gen', '$(genDir)', filepath) 1085 return filepath 1086 1087 # Iterate through all the args and apply function 1088 def _update_all_args(self, func): 1089 self.target.args = [func(arg) for arg in self.target.args] 1090 1091 def get_pre_cmd(self): 1092 pre_cmd = [] 1093 out_dirs = [ 1094 out[:out.rfind("/")] for out in self.target.outputs if "/" in out 1095 ] 1096 # Sort the list to make the output deterministic. 1097 for out_dir in sorted(set(out_dirs)): 1098 pre_cmd.append("mkdir -p $(genDir)/{} && ".format(out_dir)) 1099 return NEWLINE.join(pre_cmd) 1100 1101 def get_base_cmd(self): 1102 arg_string = NEWLINE.join(self.target.args) 1103 cmd = '$(location %s) %s' % (gn_utils.label_to_path( 1104 self.target.script), arg_string) 1105 1106 if self.use_response_file: 1107 # Pipe response file contents into script 1108 cmd = 'echo \'%s\' |%s%s' % (self.target.response_file_contents, 1109 NEWLINE, cmd) 1110 return cmd 1111 1112 def get_cmd(self): 1113 return self.get_pre_cmd() + self.get_base_cmd() 1114 1115 def get_outputs(self): 1116 return self.target.outputs 1117 1118 def get_srcs(self): 1119 # gn treats inputs and sources for actions equally. 1120 # soong only supports source files inside srcs, non-source files are added as 1121 # tool_files dependency. 1122 files = self.target.sources.union(self.target.inputs) 1123 return { 1124 gn_utils.label_to_path(file) 1125 for file in files if is_supported_source_file(file) 1126 } 1127 1128 def get_tools(self): 1129 return set() 1130 1131 def get_tool_files(self): 1132 # gn treats inputs and sources for actions equally. 1133 # soong only supports source files inside srcs, non-source files are added as 1134 # tool_files dependency. 1135 files = self.target.sources.union(self.target.inputs) 1136 tool_files = { 1137 gn_utils.label_to_path(file) 1138 for file in files if not is_supported_source_file(file) 1139 } 1140 tool_files.add(gn_utils.label_to_path(self.target.script)) 1141 return tool_files 1142 1143 def _sanitize_args(self): 1144 # Handle passing parameters via response file by piping them into the script 1145 # and reading them from /dev/stdin. 1146 1147 self.use_response_file = gn_utils.RESPONSE_FILE in self.target.args 1148 if self.use_response_file: 1149 # Replace {{response_file_contents}} with /dev/stdin 1150 self.target.args = [ 1151 '/dev/stdin' if it == gn_utils.RESPONSE_FILE else it 1152 for it in self.target.args 1153 ] 1154 1155 def _sanitize_inputs(self): 1156 pass 1157 1158 def get_deps(self): 1159 return self.target.deps 1160 1161 def sanitize(self): 1162 self._sanitize_args() 1163 self._sanitize_inputs() 1164 1165 # Whether this target generates header files 1166 def is_header_generated(self): 1167 return any( 1168 os.path.splitext(it)[1] == '.h' for it in self.target.outputs) 1169 1170 1171class WriteBuildDateHeaderSanitizer(BaseActionSanitizer): 1172 1173 def _sanitize_args(self): 1174 self._set_arg_at(0, '$(out)') 1175 super()._sanitize_args() 1176 1177 1178class WriteBuildFlagHeaderSanitizer(BaseActionSanitizer): 1179 1180 def _sanitize_args(self): 1181 self._set_value_arg('--gen-dir', '.') 1182 self._set_value_arg('--output', '$(out)') 1183 super()._sanitize_args() 1184 1185 1186class GnRunBinarySanitizer(BaseActionSanitizer): 1187 1188 def __init__(self, target, arch): 1189 super().__init__(target, arch) 1190 self.binary_to_target = { 1191 "clang_x64/transport_security_state_generator": 1192 "cronet_aml_net_tools_transport_security_state_generator_transport_security_state_generator__testing", 1193 } 1194 self.binary = self.binary_to_target[self.target.args[0]] 1195 1196 def _replace_gen_with_location_tag(self, arg): 1197 if arg.startswith("gen/"): 1198 return "$(location %s)" % arg.replace("gen/", "") 1199 return arg 1200 1201 def _replace_binary(self, arg): 1202 if arg in self.binary_to_target: 1203 return '$(location %s)' % self.binary 1204 return arg 1205 1206 def _remove_python_args(self): 1207 self.target.args = [ 1208 arg for arg in self.target.args if "python3" not in arg 1209 ] 1210 1211 def _sanitize_args(self): 1212 self._update_all_args(self._sanitize_filepath_with_location_tag) 1213 self._update_all_args(self._replace_gen_with_location_tag) 1214 self._update_all_args(self._replace_binary) 1215 self._remove_python_args() 1216 super()._sanitize_args() 1217 1218 def get_tools(self): 1219 tools = super().get_tools() 1220 tools.add(self.binary) 1221 return tools 1222 1223 def get_cmd(self): 1224 # Remove the script and use the binary right away 1225 return self.get_pre_cmd() + NEWLINE.join(self.target.args) 1226 1227 1228class JniGeneratorSanitizer(BaseActionSanitizer): 1229 1230 def __init__(self, target, arch, is_test_target): 1231 self.is_test_target = is_test_target 1232 super().__init__(target, arch) 1233 1234 def get_srcs(self): 1235 all_srcs = super().get_srcs() 1236 all_srcs.update({ 1237 gn_utils.label_to_path(file) 1238 for file in self.target.transitive_jni_java_sources 1239 if is_supported_source_file(file) 1240 }) 1241 return set(src for src in all_srcs if src.endswith(".java")) 1242 1243 def _add_location_tag_to_filepath(self, arg): 1244 if not arg.endswith('.class'): 1245 # --input_file supports both .class specifiers or source files as arguments. 1246 # Only source files need to be wrapped inside a $(location <label>) tag. 1247 arg = self._add_location_tag(arg) 1248 return arg 1249 1250 def _sanitize_args(self): 1251 self._set_value_arg('--jar-file', '$(location :current_android_jar)', 1252 False) 1253 if self._has_arg('--jar-file'): 1254 self._set_value_arg('--javap', '$(location :javap)') 1255 self._update_value_arg('--srcjar-path', self._sanitize_filepath, False) 1256 self._update_value_arg('--output-dir', self._sanitize_filepath) 1257 self._update_value_arg('--extra-include', self._sanitize_filepath, 1258 False) 1259 self._update_list_arg('--input-file', self._sanitize_filepath) 1260 self._update_list_arg('--input-file', 1261 self._add_location_tag_to_filepath) 1262 if not self.is_test_target and not self._has_arg('--jar-file'): 1263 # Don't jarjar classes that already exists within the java SDK. The headers generated 1264 # from those genrule can simply call into the original class as it exists outside 1265 # of cronet's jar. 1266 # Only jarjar platform code 1267 self._append_arg('--package-prefix', 'android.net.connectivity') 1268 super()._sanitize_args() 1269 1270 def get_outputs(self): 1271 # fix target.output directory to match #include statements. 1272 return { 1273 re.sub('^jni_headers/', '', out) 1274 for out in super().get_outputs() 1275 } 1276 1277 def get_tool_files(self): 1278 tool_files = super().get_tool_files() 1279 1280 # Filter android.jar and add :current_android_jar 1281 tool_files = { 1282 file 1283 if not file.endswith('android.jar') else ':current_android_jar' 1284 for file in tool_files 1285 } 1286 # Filter bin/javap 1287 tool_files = { 1288 file 1289 for file in tool_files if not file.endswith('bin/javap') 1290 } 1291 return tool_files 1292 1293 def get_tools(self): 1294 tools = super().get_tools() 1295 if self._has_arg('--jar-file'): 1296 tools.add(":javap") 1297 return tools 1298 1299 1300class JavaJniGeneratorSanitizer(JniGeneratorSanitizer): 1301 1302 def __init__(self, target, arch, is_test_target): 1303 self.is_test_target = is_test_target 1304 super().__init__(target, arch, is_test_target) 1305 1306 def get_outputs(self): 1307 # fix target.output directory to match #include statements. 1308 outputs = { 1309 re.sub('^jni_headers/', '', out) 1310 for out in super().get_outputs() 1311 } 1312 self.target.outputs = [ 1313 out for out in outputs if out.endswith(".srcjar") 1314 ] 1315 return outputs 1316 1317 def get_deps(self): 1318 return {} 1319 1320 def get_name(self): 1321 name = super().get_name() + "__java" 1322 return name 1323 1324 1325class JniRegistrationGeneratorSanitizer(BaseActionSanitizer): 1326 1327 def __init__(self, target, arch, is_test_target): 1328 self.is_test_target = is_test_target 1329 super().__init__(target, arch) 1330 1331 def get_srcs(self): 1332 all_srcs = super().get_srcs() 1333 all_srcs.update({ 1334 gn_utils.label_to_path(file) 1335 for file in self.target.transitive_jni_java_sources 1336 if is_supported_source_file(file) 1337 }) 1338 return set(src for src in all_srcs if src.endswith(".java")) 1339 1340 def _sanitize_inputs(self): 1341 self.target.inputs = [ 1342 file for file in self.target.inputs 1343 if not file.startswith('//out/') 1344 ] 1345 1346 def get_outputs(self): 1347 return { 1348 re.sub('^jni_headers/', '', out) 1349 for out in super().get_outputs() 1350 } 1351 1352 def _sanitize_args(self): 1353 self._update_value_arg('--depfile', self._sanitize_filepath) 1354 self._update_value_arg('--srcjar-path', self._sanitize_filepath) 1355 self._update_value_arg('--header-path', self._sanitize_filepath) 1356 self._delete_value_arg('--depfile', False) 1357 self._set_value_arg('--java-sources-file', '$(genDir)/java.sources') 1358 if not self.is_test_target: 1359 # Only jarjar platform code 1360 self._append_arg('--package-prefix', 'android.net.connectivity') 1361 super()._sanitize_args() 1362 1363 def get_cmd(self): 1364 # jni_registration_generator.py doesn't work with python2 1365 cmd = "python3 " + super().get_base_cmd() 1366 # Path in the original sources file does not work in genrule. 1367 # So creating sources file in cmd based on the srcs of this target. 1368 # Adding ../$(current_dir)/ to the head because jni_registration_generator.py uses the files 1369 # whose path startswith(..) 1370 commands = [ 1371 "current_dir=`basename \\\`pwd\\\``;", "for f in $(in);", "do", 1372 "echo \\\"../$$current_dir/$$f\\\" >> $(genDir)/java.sources;", 1373 "done;", cmd 1374 ] 1375 1376 return self.get_pre_cmd() + NEWLINE.join(commands) 1377 1378 1379class JavaJniRegistrationGeneratorSanitizer(JniRegistrationGeneratorSanitizer): 1380 1381 def get_name(self): 1382 name = super().get_name() + "__java" 1383 return name 1384 1385 def get_outputs(self): 1386 return [ 1387 out for out in super().get_outputs() if out.endswith(".srcjar") 1388 ] 1389 1390 def get_deps(self): 1391 return {} 1392 1393 1394class VersionSanitizer(BaseActionSanitizer): 1395 1396 def _sanitize_args(self): 1397 self._set_value_arg('-o', '$(out)') 1398 # args for the version.py contain file path without leading --arg key. So apply sanitize 1399 # function for all the args. 1400 self._update_all_args(self._sanitize_filepath_with_location_tag) 1401 for (i, arg) in enumerate(self.target.args): 1402 if arg == '-e': 1403 self.target.args[i + 1] = "'%s'" % self.target.args[i + 1] 1404 super()._sanitize_args() 1405 1406 def get_tool_files(self): 1407 tool_files = super().get_tool_files() 1408 # android_chrome_version.py is not specified in anywhere but version.py imports this file 1409 tool_files.add('build/util/android_chrome_version.py') 1410 return tool_files 1411 1412 1413class JavaCppEnumSanitizer(BaseActionSanitizer): 1414 1415 def _sanitize_args(self): 1416 self._update_all_args(self._sanitize_filepath_with_location_tag) 1417 self._set_value_arg('--srcjar', '$(out)') 1418 super()._sanitize_args() 1419 1420 1421class MakeDafsaSanitizer(BaseActionSanitizer): 1422 1423 def is_header_generated(self): 1424 # This script generates .cc files but they are #included by other sources 1425 # (e.g. registry_controlled_domain.cc) 1426 return True 1427 1428 1429class JavaCppFeatureSanitizer(BaseActionSanitizer): 1430 1431 def _sanitize_args(self): 1432 self._update_all_args(self._sanitize_filepath_with_location_tag) 1433 self._set_value_arg('--srcjar', '$(out)') 1434 super()._sanitize_args() 1435 1436 1437class JavaCppStringSanitizer(BaseActionSanitizer): 1438 1439 def _sanitize_args(self): 1440 self._update_all_args(self._sanitize_filepath_with_location_tag) 1441 self._set_value_arg('--srcjar', '$(out)') 1442 super()._sanitize_args() 1443 1444 1445class WriteNativeLibrariesJavaSanitizer(BaseActionSanitizer): 1446 1447 def _sanitize_args(self): 1448 self._set_value_arg('--output', '$(out)') 1449 super()._sanitize_args() 1450 1451 1452class ProtocJavaSanitizer(BaseActionSanitizer): 1453 1454 def __init__(self, target, arch, gn): 1455 super().__init__(target, arch) 1456 self._protoc = get_protoc_module_name(gn) 1457 1458 def _sanitize_proto_path(self, arg): 1459 arg = self._sanitize_filepath(arg) 1460 return tree_path + '/' + arg 1461 1462 def _sanitize_args(self): 1463 super()._sanitize_args() 1464 self._delete_value_arg('--depfile') 1465 self._set_value_arg('--protoc', '$(location %s)' % self._protoc) 1466 self._update_value_arg('--proto-path', self._sanitize_proto_path) 1467 self._set_value_arg('--srcjar', '$(out)') 1468 self._update_arg_at(-1, self._sanitize_filepath_with_location_tag) 1469 1470 def get_tools(self): 1471 tools = super().get_tools() 1472 tools.add(self._protoc) 1473 return tools 1474 1475 1476def get_action_sanitizer(gn, target, type, arch, is_test_target): 1477 if target.script == "//build/write_buildflag_header.py": 1478 return WriteBuildFlagHeaderSanitizer(target, arch) 1479 elif target.script == "//base/write_build_date_header.py": 1480 return WriteBuildDateHeaderSanitizer(target, arch) 1481 elif target.script == "//build/util/version.py": 1482 return VersionSanitizer(target, arch) 1483 elif target.script == "//build/android/gyp/java_cpp_enum.py": 1484 return JavaCppEnumSanitizer(target, arch) 1485 elif target.script == "//net/tools/dafsa/make_dafsa.py": 1486 return MakeDafsaSanitizer(target, arch) 1487 elif target.script == '//build/android/gyp/java_cpp_features.py': 1488 return JavaCppFeatureSanitizer(target, arch) 1489 elif target.script == '//build/android/gyp/java_cpp_strings.py': 1490 return JavaCppStringSanitizer(target, arch) 1491 elif target.script == '//build/android/gyp/write_native_libraries_java.py': 1492 return WriteNativeLibrariesJavaSanitizer(target, arch) 1493 elif target.script == '//build/gn_run_binary.py': 1494 return GnRunBinarySanitizer(target, arch) 1495 elif target.script == '//build/protoc_java.py': 1496 return ProtocJavaSanitizer(target, arch, gn) 1497 elif target.script == '//third_party/jni_zero/jni_zero.py': 1498 if target.args[0] == 'generate-final': 1499 if type == 'java_genrule': 1500 # Fill up the sources of the target for JniRegistrationGenerator 1501 # actions with all the java sources found under targets of type 1502 # `generate_jni`. Note 1: Only do this for the java part in order to 1503 # generate a complete GEN_JNI. The C++ part MUST only include java 1504 # source files that are listed explicitly in `generate_jni` targets 1505 # in the transitive dependency, this is handled inside the action 1506 # sanitizer itself (See `get_srcs`). Adding java sources that are not 1507 # listed to the C++ version of JniRegistrationGenerator will result 1508 # in undefined symbols as the C++ part generates declarations that 1509 # would have no definitions. Note 2: This is only done for the 1510 # testing targets because their JniRegistration is not complete, 1511 # Chromium generates Jni files for testing targets implicitly (See 1512 # https://source.chromium.org/chromium/chromium/src/+/main:testing 1513 # /test.gni;l=422;bpv=1;bpt=0;drc 1514 # =02820c1b362c3a00f426d7c4eab18703d89cda03) to avoid having to 1515 # replicate the same setup, just fill up the java JniRegistration 1516 # with all java sources found under `generate_jni` targets and fill 1517 # the C++ version with the exact files. 1518 if is_test_target: 1519 target.sources.update(gn.jni_java_sources) 1520 return JavaJniRegistrationGeneratorSanitizer( 1521 target, arch, is_test_target) 1522 else: 1523 return JniRegistrationGeneratorSanitizer( 1524 target, arch, is_test_target) 1525 else: 1526 if type == 'cc_genrule': 1527 return JniGeneratorSanitizer(target, arch, is_test_target) 1528 else: 1529 return JavaJniGeneratorSanitizer(target, arch, is_test_target) 1530 else: 1531 raise Exception('Unsupported action %s from %s' % 1532 (target.script, target.name)) 1533 1534 1535def create_action_foreach_modules(blueprint, gn, target, is_test_target): 1536 """ The following assumes that rebase_path exists in the args. 1537 The args of an action_foreach contains hints about which output files are generated 1538 by which source files. 1539 This is copied directly from the args 1540 "gen/net/base/registry_controlled_domains/{{source_name_part}}-reversed-inc.cc" 1541 So each source file will generate an output whose name is the {source_name-reversed-inc.cc} 1542 """ 1543 new_args = [] 1544 for i, src in enumerate(sorted(target.sources)): 1545 # don't add script arg for the first source -- create_action_module 1546 # already does this. 1547 if i != 0: 1548 new_args.append('&&') 1549 new_args.append('python3 $(location %s)' % 1550 gn_utils.label_to_path(target.script)) 1551 for arg in target.args: 1552 if '{{source}}' in arg: 1553 new_args.append('$(location %s)' % 1554 (gn_utils.label_to_path(src))) 1555 elif '{{source_name_part}}' in arg: 1556 source_name_part = src.split("/")[-1] # Get the file name only 1557 source_name_part = source_name_part.split(".")[ 1558 0] # Remove the extension (Ex: .cc) 1559 file_name = arg.replace('{{source_name_part}}', 1560 source_name_part).split("/")[-1] 1561 # file_name represent the output file name. But we need the whole path 1562 # This can be found from target.outputs. 1563 for out in target.outputs: 1564 if out.endswith(file_name): 1565 new_args.append('$(location %s)' % out) 1566 1567 for file in (target.sources | target.inputs): 1568 if file.endswith(file_name): 1569 new_args.append('$(location %s)' % 1570 gn_utils.label_to_path(file)) 1571 else: 1572 new_args.append(arg) 1573 1574 target.args = new_args 1575 return create_action_module(blueprint, gn, target, 'cc_genrule', 1576 is_test_target) 1577 1578 1579def create_action_module_internal(gn, 1580 target, 1581 type, 1582 is_test_target, 1583 blueprint, 1584 arch=None): 1585 if target.script == '//build/android/gyp/gcc_preprocess.py': 1586 return create_gcc_preprocess_modules(blueprint, target) 1587 sanitizer = get_action_sanitizer(gn, target, type, arch, is_test_target) 1588 sanitizer.sanitize() 1589 1590 module = Module(type, sanitizer.get_name(), target.name) 1591 module.cmd = sanitizer.get_cmd() 1592 module.out = sanitizer.get_outputs() 1593 if sanitizer.is_header_generated(): 1594 module.genrule_headers.add(module.name) 1595 module.srcs = sanitizer.get_srcs() 1596 module.tool_files = sanitizer.get_tool_files() 1597 module.tools = sanitizer.get_tools() 1598 target.deps = sanitizer.get_deps() 1599 1600 return module 1601 1602 1603def get_cmd_condition(arch): 1604 ''' 1605 :param arch: archtecture name e.g. android_x86_64, android_arm64 1606 :return: condition that can be used in cc_genrule cmd to switch the behavior based on arch 1607 ''' 1608 if arch == "android_x86_64": 1609 return "( $$CC_ARCH == 'x86_64' && $$CC_OS == 'android' )" 1610 elif arch == "android_x86": 1611 return "( $$CC_ARCH == 'x86' && $$CC_OS == 'android' )" 1612 elif arch == "android_arm": 1613 return "( $$CC_ARCH == 'arm' && $$CC_OS == 'android' )" 1614 elif arch == "android_arm64": 1615 return "( $$CC_ARCH == 'arm64' && $$CC_OS == 'android' )" 1616 elif arch == "host": 1617 return "$$CC_OS != 'android'" 1618 else: 1619 raise Exception(f'Unknown architecture type {arch}') 1620 1621 1622def merge_cmd(modules, genrule_type): 1623 ''' 1624 :param modules: dictionary whose key is arch name and value is module 1625 :param genrule_type: cc_genrule or java_genrule 1626 :return: merged command or common command if all the archs have the same command. 1627 ''' 1628 commands = list({module.cmd for module in modules.values()}) 1629 if len(commands) == 1: 1630 # If all the archs have the same command, return the command 1631 return commands[0] 1632 1633 if genrule_type != 'cc_genrule': 1634 raise Exception( 1635 f'{genrule_type} can not have different cmd between archs') 1636 1637 merged_cmd = [] 1638 for arch, module in modules.items(): 1639 merged_cmd.append(f'if [[ {get_cmd_condition(arch)} ]];') 1640 merged_cmd.append('then') 1641 merged_cmd.append(module.cmd + ';') 1642 merged_cmd.append('fi;') 1643 return NEWLINE.join(merged_cmd) 1644 1645 1646def merge_modules(modules, genrule_type): 1647 ''' 1648 :param modules: dictionary whose key is arch name and value is module 1649 :param genrule_type: cc_genrule or java_genrule 1650 :return: merged module of input modules 1651 ''' 1652 merged_module = list(modules.values())[0] 1653 1654 # Following attributes must be the same between archs 1655 for key in ('out', 'genrule_headers', 'srcs', 'tool_files'): 1656 if any([ 1657 getattr(merged_module, key) != getattr(module, key) 1658 for module in modules.values() 1659 ]): 1660 raise Exception( 1661 f'{merged_module.name} has different values for {key} between archs' 1662 ) 1663 1664 merged_module.cmd = merge_cmd(modules, genrule_type) 1665 return merged_module 1666 1667 1668def create_action_module(blueprint, gn, target, genrule_type, is_test_target): 1669 ''' 1670 Create module for action target and add to the blueprint. If target has arch specific attributes 1671 this function merge them and create a single module. 1672 :param blueprint: 1673 :param target: target which is converted to the module. 1674 :param genrule_type: cc_genrule or java_genrule 1675 :return: created module 1676 ''' 1677 # TODO: Handle this target correctly, this target generates java_genrule but this target has 1678 # different value for cpu-family arg between archs 1679 if re.match('//build/android:native_libraries_gen(__testing)?$', 1680 target.name): 1681 module = create_action_module_internal(gn, target, genrule_type, 1682 is_test_target, blueprint, 1683 target.arch['android_arm']) 1684 blueprint.add_module(module) 1685 return module 1686 1687 modules = { 1688 arch_name: 1689 create_action_module_internal(gn, target, genrule_type, is_test_target, 1690 blueprint, arch) 1691 for arch_name, arch in target.get_archs().items() 1692 } 1693 module = merge_modules(modules, genrule_type) 1694 blueprint.add_module(module) 1695 return module 1696 1697 1698def _get_cflags(cflags, defines): 1699 cflags = {flag for flag in cflags if flag in cflag_allowlist} 1700 # Consider proper allowlist or denylist if needed 1701 cflags |= set("-D%s" % define.replace("\"", "\\\"") for define in defines) 1702 return cflags 1703 1704 1705def _set_linker_script(module, libs): 1706 for lib in libs: 1707 if lib.endswith(".lds"): 1708 module.ldflags.add( 1709 get_linker_script_ldflag(gn_utils.label_to_path(lib))) 1710 1711 1712def set_module_flags(module, module_type, cflags, defines, ldflags, libs): 1713 module.cflags.update(_get_cflags(cflags, defines)) 1714 module.ldflags.update({ 1715 flag 1716 for flag in ldflags 1717 if flag in ldflag_allowlist or flag.startswith("-Wl,-wrap,") 1718 }) 1719 _set_linker_script(module, libs) 1720 # TODO: implement proper cflag parsing. 1721 for flag in cflags: 1722 if '-std=' in flag: 1723 module.cpp_std = flag[len('-std='):] 1724 if '-fexceptions' in flag: 1725 module.cppflags.add('-fexceptions') 1726 1727 1728def set_module_include_dirs(module, cflags, include_dirs): 1729 for flag in cflags: 1730 if '-isystem' in flag: 1731 module.local_include_dirs.add(flag[len('-isystem../../'):]) 1732 1733 # Adding local_include_dirs is necessary due to source_sets / filegroups 1734 # which do not properly propagate include directories. 1735 # Filter any directory inside //out as a) this directory does not exist for 1736 # aosp / soong builds and b) the include directory should already be 1737 # configured via library dependency. 1738 module.local_include_dirs.update([ 1739 gn_utils.label_to_path(d) for d in include_dirs 1740 if not d.startswith('//out') 1741 ]) 1742 # Remove prohibited include directories 1743 module.local_include_dirs = [ 1744 d for d in module.local_include_dirs 1745 if d not in local_include_dirs_denylist 1746 ] 1747 1748 1749def create_modules_from_target(blueprint, gn, gn_target_name, 1750 is_descendant_of_java, is_test_target): 1751 """Generate module(s) for a given GN target. 1752 1753 Given a GN target name, generate one or more corresponding modules into a 1754 blueprint. The only case when this generates >1 module is proto libraries. 1755 1756 Args: 1757 blueprint: Blueprint instance which is being generated. 1758 gn: gn_utils.GnParser object. 1759 gn_target_name: GN target for module generation. 1760 """ 1761 bp_module_name = label_to_module_name(gn_target_name) 1762 target = gn.get_target(gn_target_name) 1763 is_descendant_of_java = is_descendant_of_java or target.type == "java_library" 1764 1765 # Append __java suffix to actions reachable from java_library. This is necessary 1766 # to differentiate them from cc actions. 1767 # This means that a GN action of name X will be translated to two different modules of names 1768 # X and X__java(only if X is reachable from a java target). 1769 if target.type == "action" and is_descendant_of_java: 1770 bp_module_name += "__java" 1771 1772 if bp_module_name in blueprint.modules: 1773 return blueprint.modules[bp_module_name] 1774 1775 log.info('create modules for %s (%s)', target.name, target.type) 1776 1777 if target.type == 'executable': 1778 if target.testonly: 1779 module_type = 'cc_test' 1780 else: 1781 # Can be used for both host and device targets. 1782 module_type = 'cc_binary' 1783 module = Module(module_type, bp_module_name, gn_target_name) 1784 elif target.type in ['static_library', 'source_set']: 1785 module = Module('cc_library_static', bp_module_name, gn_target_name) 1786 elif target.type == 'shared_library': 1787 module = Module('cc_library_shared', bp_module_name, gn_target_name) 1788 elif target.type == 'group': 1789 # "group" targets are resolved recursively by gn_utils.get_target(). 1790 # There's nothing we need to do at this level for them. 1791 return None 1792 elif target.type == 'proto_library': 1793 module = create_proto_modules(blueprint, gn, target) 1794 if module is None: 1795 return None 1796 elif target.type == 'action': 1797 module = create_action_module( 1798 blueprint, gn, target, 1799 'java_genrule' if is_descendant_of_java else 'cc_genrule', 1800 is_test_target) 1801 elif target.type == 'action_foreach': 1802 module = create_action_foreach_modules(blueprint, gn, target, 1803 is_test_target) 1804 elif target.type == 'copy': 1805 # TODO: careful now! copy targets are not supported yet, but this will stop 1806 # traversing the dependency tree. For //base:base, this is not a big 1807 # problem as libicu contains the only copy target which happens to be a 1808 # leaf node. 1809 return None 1810 elif target.type == 'java_library': 1811 if target.jar_path: 1812 module = Module('java_import', bp_module_name, gn_target_name) 1813 module.jars.add(target.jar_path) 1814 else: 1815 module = Module('java_library', bp_module_name, gn_target_name) 1816 # Don't remove GEN_JNI from those modules as they have the real GEN_JNI that we want to include 1817 if gn_target_name not in [ 1818 '//components/cronet/android:cronet_jni_registration_java', 1819 '//components/cronet/android:cronet_jni_registration_java__testing', 1820 '//components/cronet/android:cronet_tests_jni_registration_java__testing' 1821 ]: 1822 module.jarjar_rules = REMOVE_GEN_JNI_JARJAR_RULES_FILE 1823 module.min_sdk_version = 30 1824 module.apex_available = [tethering_apex] 1825 else: 1826 raise Exception('Unknown target %s (%s)' % (target.name, target.type)) 1827 1828 blueprint.add_module(module) 1829 if target.type not in ['action', 'action_foreach']: 1830 # Actions should get their srcs from their corresponding ActionSanitizer as actionSanitizer 1831 # filters srcs differently according to the type of the action. 1832 module.srcs.update( 1833 gn_utils.label_to_path(src) for src in target.sources 1834 if is_supported_source_file(src)) 1835 1836 # Add arch-specific properties 1837 for arch_name, arch in target.get_archs().items(): 1838 module.target[arch_name].srcs.update( 1839 gn_utils.label_to_path(src) for src in arch.sources 1840 if is_supported_source_file(src)) 1841 1842 module.rtti = target.rtti 1843 1844 if target.type in gn_utils.LINKER_UNIT_TYPES: 1845 set_module_flags(module, module.type, target.cflags, target.defines, 1846 target.ldflags, target.libs) 1847 set_module_include_dirs(module, target.cflags, target.include_dirs) 1848 # TODO: set_module_xxx is confusing, apply similar function to module and target in better way. 1849 for arch_name, arch in target.get_archs().items(): 1850 # TODO(aymanm): Make libs arch-specific. 1851 set_module_flags(module.target[arch_name], module.type, 1852 arch.cflags, arch.defines, arch.ldflags, []) 1853 # -Xclang -target-feature -Xclang +mte are used to enable MTE (Memory Tagging Extensions). 1854 # Flags which does not start with '-' could not be in the cflags so enabling MTE by 1855 # -march and -mcpu Feature Modifiers. MTE is only available on arm64. This is needed for 1856 # building //base/allocator/partition_allocator:partition_alloc for arm64. 1857 if '+mte' in arch.cflags and arch_name == 'android_arm64': 1858 module.target[arch_name].cflags.add('-march=armv8-a+memtag') 1859 set_module_include_dirs(module.target[arch_name], arch.cflags, 1860 arch.include_dirs) 1861 1862 module.host_supported = target.host_supported() 1863 module.device_supported = target.device_supported() 1864 module.gn_type = target.type 1865 1866 if module.is_genrule(): 1867 module.apex_available.add(tethering_apex) 1868 1869 if module.type == "java_library": 1870 if gn_utils.contains_aidl(target.sources): 1871 # frameworks/base/core/java includes the source files that are used to compile framework.aidl. 1872 # framework.aidl is added implicitly as a dependency to every AIDL GN action, this can be 1873 # identified by third_party/android_sdk/public/platforms/android-34/framework.aidl. 1874 module.aidl["include_dirs"] = {"frameworks/base/core/java/"} 1875 module.aidl["local_include_dirs"] = target.local_aidl_includes 1876 # This is used to compile against the public SDK APIs. 1877 # See build/soong/android/sdk_version.go for more information on different SDK versions. 1878 module.sdk_version = "current" 1879 1880 if module.is_compiled(): 1881 # Don't try to inject library/source dependencies into genrules or 1882 # filegroups because they are not compiled in the traditional sense. 1883 if module.type != "java_library": 1884 module.defaults = [defaults_module] 1885 for lib in target.libs: 1886 # Generally library names should be mangled as 'libXXX', unless they 1887 # are HAL libraries (e.g., [email protected]) or AIDL c++ / NDK 1888 # libraries (e.g. "android.hardware.power.stats-V1-cpp") 1889 android_lib = lib if '@' in lib or "-cpp" in lib or "-ndk" in lib \ 1890 else 'lib' + lib 1891 if lib in shared_library_allowlist: 1892 module.add_android_shared_lib(android_lib) 1893 1894 # If the module is a static library, export all the generated headers. 1895 if module.type == 'cc_library_static': 1896 module.export_generated_headers = module.generated_headers 1897 1898 if module.name in [ 1899 'cronet_aml_components_cronet_android_cronet', 1900 'cronet_aml_components_cronet_android_cronet' + 1901 gn_utils.TESTING_SUFFIX 1902 ]: 1903 if target.output_name is None: 1904 raise Exception('Failed to get output_name for libcronet name') 1905 # .so file name needs to match with CronetLibraryLoader.java (e.g. libcronet.109.0.5386.0.so) 1906 # So setting the output name based on the output_name from the desc.json 1907 module.stem = 'libmainline' + target.output_name 1908 elif module.is_test() and module.type == 'cc_library_shared': 1909 if target.output_name: 1910 # If we have an output name already declared, use it. 1911 module.stem = 'lib' + target.output_name 1912 else: 1913 # Tests output should be a shared library in the format of 'lib[module_name]' 1914 module.stem = 'lib' + target.get_target_name( 1915 )[:target.get_target_name().find(gn_utils.TESTING_SUFFIX)] 1916 1917 # dep_name is an unmangled GN target name (e.g. //foo:bar(toolchain)). 1918 all_deps = [(dep_name, 'common') for dep_name in target.proto_deps] 1919 for arch_name, arch in target.arch.items(): 1920 all_deps += [(dep_name, arch_name) for dep_name in arch.deps] 1921 1922 # Sort deps before iteration to make result deterministic. 1923 for (dep_name, arch_name) in sorted(all_deps): 1924 module_target = module.target[ 1925 arch_name] if arch_name != 'common' else module 1926 # |builtin_deps| override GN deps with Android-specific ones. See the 1927 # config in the top of this file. 1928 if dep_name in builtin_deps: 1929 builtin_deps[dep_name](module, arch_name) 1930 continue 1931 1932 dep_module = create_modules_from_target(blueprint, gn, dep_name, 1933 is_descendant_of_java, 1934 is_test_target) 1935 1936 if dep_module is None: 1937 continue 1938 # TODO: Proper dependency check for genrule. 1939 # Currently, only propagating genrule dependencies. 1940 # Also, currently, all the dependencies are propagated upwards. 1941 # in gn, public_deps should be propagated but deps should not. 1942 # Not sure this information is available in the desc.json. 1943 # Following rule works for adding android_runtime_jni_headers to base:base. 1944 # If this doesn't work for other target, hardcoding for specific target 1945 # might be better. 1946 if module.is_genrule() and dep_module.is_genrule(): 1947 if module_target.gn_type != "proto_library": 1948 # proto_library are treated differently because each proto action 1949 # is split into two different targets, a cpp target and a header target. 1950 # the cpp target is used as the entry point to the proto action, hence 1951 # it should not be propagated as a genrule header because it generates 1952 # cpp files only. 1953 module_target.genrule_headers.add(dep_module.name) 1954 module_target.genrule_headers.update(dep_module.genrule_headers) 1955 1956 # For filegroups, and genrule, recurse but don't apply the 1957 # deps. 1958 if not module.is_compiled() or module.is_genrule(): 1959 continue 1960 1961 # Drop compiled modules that doesn't provide any benefit. This is mostly 1962 # applicable to source_sets when converted to cc_static_library, sometimes 1963 # the source set only has header files which are dropped so the module becomes empty. 1964 # is_compiled is there to prevent dropping of genrules. 1965 if dep_module.is_compiled() and not dep_module.has_input_files(): 1966 continue 1967 1968 if dep_module.type == 'cc_library_shared': 1969 module_target.shared_libs.add(dep_module.name) 1970 elif dep_module.type == 'cc_library_static': 1971 if module.type in ['cc_library_shared', 'cc_binary' 1972 ] and dep_module.type == 'cc_library_static': 1973 module_target.whole_static_libs.add(dep_module.name) 1974 else: 1975 module_target.generated_headers.update( 1976 dep_module.generated_headers) 1977 module_target.shared_libs.update(dep_module.shared_libs) 1978 module_target.header_libs.update(dep_module.header_libs) 1979 elif dep_module.type == 'cc_genrule': 1980 module_target.generated_headers.update(dep_module.genrule_headers) 1981 module_target.srcs.update(dep_module.genrule_srcs) 1982 module_target.shared_libs.update(dep_module.genrule_shared_libs) 1983 module_target.header_libs.update(dep_module.genrule_header_libs) 1984 elif dep_module.type in ['java_library', 'java_import']: 1985 if module.type not in ["cc_library_static"]: 1986 # This is needed to go around the case where `url` component depends 1987 # on `url_java`. 1988 # TODO(aymanm): Remove the if condition once crrev/4902547 has been imported downstream 1989 module_target.static_libs.add(dep_module.name) 1990 elif dep_module.type in ['genrule', 'java_genrule']: 1991 module_target.srcs.add(":" + dep_module.name) 1992 else: 1993 raise Exception( 1994 'Unsupported arch-specific dependency %s of target %s with type %s' 1995 % (dep_module.name, target.name, dep_module.type)) 1996 return module 1997 1998 1999def turn_off_allocator_shim_for_musl(module): 2000 allocation_shim = "base/allocator/partition_allocator/shim/allocator_shim.cc" 2001 allocator_shim_files = { 2002 allocation_shim, 2003 "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_glibc.cc", 2004 } 2005 module.srcs -= allocator_shim_files 2006 for arch in module.target.values(): 2007 arch.srcs -= allocator_shim_files 2008 module.target['android'].srcs.add(allocation_shim) 2009 if gn_utils.TESTING_SUFFIX in module.name: 2010 # allocator_shim_default_dispatch_to_glibc is only added to the __testing version of base 2011 # since base_base__testing is compiled for host. When compiling for host. Soong compiles 2012 # using glibc or musl(experimental). We currently only support compiling for glibc. 2013 module.target['glibc'].srcs.update(allocator_shim_files) 2014 else: 2015 # allocator_shim_default_dispatch_to_glibc does not exist in the prod version of base 2016 # `base_base` since this only compiles for android and bionic is used. Bionic is the equivalent 2017 # of glibc but for android. 2018 module.target['glibc'].srcs.add(allocation_shim) 2019 2020 2021def create_blueprint_for_targets(gn, targets, test_targets): 2022 """Generate a blueprint for a list of GN targets.""" 2023 blueprint = Blueprint() 2024 2025 # Default settings used by all modules. 2026 defaults = Module('cc_defaults', defaults_module, '//gn:default_deps') 2027 defaults.cflags = [ 2028 '-DGOOGLE_PROTOBUF_NO_RTTI', 2029 '-DBORINGSSL_SHARED_LIBRARY', 2030 '-Wno-error=return-type', 2031 '-Wno-non-virtual-dtor', 2032 '-Wno-macro-redefined', 2033 '-Wno-missing-field-initializers', 2034 '-Wno-sign-compare', 2035 '-Wno-sign-promo', 2036 '-Wno-unused-parameter', 2037 '-Wno-null-pointer-subtraction', # Needed to libevent 2038 '-Wno-ambiguous-reversed-operator', # needed for icui18n 2039 '-Wno-unreachable-code-loop-increment', # needed for icui18n 2040 '-fPIC', 2041 '-Wno-c++11-narrowing', 2042 ] 2043 defaults.c_std = 'gnu11' 2044 # Chromium builds do not add a dependency for headers found inside the 2045 # sysroot, so they are added globally via defaults. 2046 defaults.target['android'].header_libs = [ 2047 'jni_headers', 2048 ] 2049 defaults.target['android'].shared_libs = ['libmediandk'] 2050 defaults.target['host'].cflags = [ 2051 # -DANDROID is added by default but target.defines contain -DANDROID if 2052 # it's required. So adding -UANDROID to cancel default -DANDROID if it's 2053 # not specified. 2054 # Note: -DANDROID is not consistently applied across the chromium code 2055 # base, so it is removed unconditionally for host targets. 2056 '-UANDROID', 2057 ] 2058 defaults.stl = 'none' 2059 defaults.cpp_std = 'c++17' 2060 defaults.min_sdk_version = 29 2061 defaults.apex_available.add(tethering_apex) 2062 blueprint.add_module(defaults) 2063 2064 for target in targets: 2065 module = create_modules_from_target(blueprint, 2066 gn, 2067 target, 2068 is_descendant_of_java=False, 2069 is_test_target=False) 2070 if module: 2071 module.visibility.update(root_modules_visibility) 2072 2073 for test_target in test_targets: 2074 module = create_modules_from_target(blueprint, 2075 gn, 2076 test_target + 2077 gn_utils.TESTING_SUFFIX, 2078 is_descendant_of_java=False, 2079 is_test_target=True) 2080 if module: 2081 module.visibility.update(root_modules_visibility) 2082 2083 # Merge in additional hardcoded arguments. 2084 for module in blueprint.modules.values(): 2085 for key, add_val in additional_args.get(module.name, []): 2086 curr = getattr(module, key) 2087 if add_val and isinstance(add_val, set) and isinstance(curr, set): 2088 curr.update(add_val) 2089 elif isinstance(add_val, str) and (not curr 2090 or isinstance(curr, str)): 2091 setattr(module, key, add_val) 2092 elif isinstance(add_val, bool) and (not curr 2093 or isinstance(curr, bool)): 2094 setattr(module, key, add_val) 2095 elif isinstance(add_val, dict) and isinstance(curr, dict): 2096 curr.update(add_val) 2097 elif isinstance(add_val[1], dict) and isinstance( 2098 curr[add_val[0]], Module.Target): 2099 curr[add_val[0]].__dict__.update(add_val[1]) 2100 else: 2101 raise Exception( 2102 'Unimplemented type %r of additional_args: %r' % 2103 (type(add_val), key)) 2104 2105 return blueprint 2106 2107 2108def create_package_module(blueprint): 2109 package = Module("package", "", "PACKAGE") 2110 package.comment = "The actual license can be found in Android.extras.bp" 2111 package.default_applicable_licenses.add(CRONET_LICENSE_NAME) 2112 package.default_visibility.append(package_default_visibility) 2113 blueprint.add_module(package) 2114 2115 2116def main(): 2117 parser = argparse.ArgumentParser( 2118 description='Generate Android.bp from a GN description.') 2119 parser.add_argument( 2120 '--desc', 2121 help= 2122 'GN description (e.g., gn desc out --format=json --all-toolchains "//*".' 2123 + 'You can specify multiple --desc options for different target_cpu', 2124 required=True, 2125 action='append') 2126 parser.add_argument( 2127 '--extras', 2128 help='Extra targets to include at the end of the Blueprint file', 2129 default=os.path.join(gn_utils.repo_root(), 'Android.bp.extras'), 2130 ) 2131 parser.add_argument( 2132 '--output', 2133 help='Blueprint file to create', 2134 default=os.path.join(gn_utils.repo_root(), 'Android.bp'), 2135 ) 2136 parser.add_argument( 2137 '-v', 2138 '--verbose', 2139 help='Print debug logs.', 2140 action='store_true', 2141 ) 2142 parser.add_argument( 2143 'targets', 2144 nargs=argparse.REMAINDER, 2145 help='Targets to include in the blueprint (e.g., "//:perfetto_tests")') 2146 args = parser.parse_args() 2147 2148 if args.verbose: 2149 log.basicConfig(format='%(levelname)s:%(funcName)s:%(message)s', 2150 level=log.DEBUG) 2151 2152 targets = args.targets or DEFAULT_TARGETS 2153 gn = gn_utils.GnParser(builtin_deps) 2154 for desc_file in args.desc: 2155 with open(desc_file) as f: 2156 desc = json.load(f) 2157 for target in targets: 2158 gn.parse_gn_desc(desc, target) 2159 for test_target in DEFAULT_TESTS: 2160 gn.parse_gn_desc(desc, test_target, is_test_target=True) 2161 blueprint = create_blueprint_for_targets(gn, targets, DEFAULT_TESTS) 2162 project_root = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) 2163 tool_name = os.path.relpath(os.path.abspath(__file__), project_root) 2164 2165 create_package_module(blueprint) 2166 output = [ 2167 """// Copyright (C) 2022 The Android Open Source Project 2168// 2169// Licensed under the Apache License, Version 2.0 (the "License"); 2170// you may not use this file except in compliance with the License. 2171// You may obtain a copy of the License at 2172// 2173// http://www.apache.org/licenses/LICENSE-2.0 2174// 2175// Unless required by applicable law or agreed to in writing, software 2176// distributed under the License is distributed on an "AS IS" BASIS, 2177// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2178// See the License for the specific language governing permissions and 2179// limitations under the License. 2180// 2181// This file is automatically generated by %s. Do not edit. 2182 2183build = ["Android.extras.bp"] 2184""" % (tool_name) 2185 ] 2186 blueprint.to_string(output) 2187 if os.path.exists(args.extras): 2188 with open(args.extras, 'r') as r: 2189 for line in r: 2190 output.append(line.rstrip("\n\r")) 2191 2192 out_files = [] 2193 2194 # Generate the Android.bp file. 2195 out_files.append(args.output + '.swp') 2196 with open(out_files[-1], 'w') as f: 2197 f.write('\n'.join(output)) 2198 # Text files should have a trailing EOL. 2199 f.write('\n') 2200 2201 return 0 2202 2203 2204if __name__ == '__main__': 2205 sys.exit(main()) 2206