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 typing import List, Dict, Set 37from pathlib import Path 38 39import gn_utils 40PARENT_ROOT = os.path.abspath( 41 os.path.join(os.path.dirname(__file__), os.pardir)) 42 43sys.path.insert(0, os.path.join(PARENT_ROOT, "license")) 44import license_utils 45 46ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 47 48CRONET_LICENSE_NAME = "external_cronet_license" 49 50# Default targets to translate to the blueprint file. 51DEFAULT_TARGETS = [ 52 "//components/cronet/android:cronet_api_java", 53 '//components/cronet/android:cronet', 54 '//components/cronet/android:cronet_impl_native_java', 55 '//components/cronet/android:cronet_jni_registration_java', 56] 57 58DEFAULT_TESTS = [ 59 '//components/cronet/android:cronet_unittests_android__library', 60 '//net:net_unittests__library', 61 '//components/cronet/android:cronet_tests', 62 '//components/cronet/android:cronet', 63 '//components/cronet/android:cronet_javatests', 64 '//components/cronet/android:cronet_jni_registration_java', 65 '//components/cronet/android:cronet_tests_jni_registration_java', 66 '//testing/android/native_test:native_test_java', 67 '//net/android:net_test_support_provider_java', 68 '//net/android:net_tests_java', 69 '//third_party/netty-tcnative:netty-tcnative-so', 70 '//third_party/netty4:netty_all_java', 71 "//build/rust/tests/test_rust_static_library:test_rust_static_library", # Added to make sure that rust still compiles 72] 73 74EXTRAS_ANDROID_BP_FILE = "Android.extras.bp" 75 76CRONET_API_MODULE_NAME = "cronet_aml_api_java" 77 78# All module names are prefixed with this string to avoid collisions. 79module_prefix = 'cronet_aml_' 80 81REMOVE_GEN_JNI_JARJAR_RULES_FILE = ":remove_gen_jni_jarjar_rules" 82# Shared libraries which are directly translated to Android system equivalents. 83shared_library_allowlist = [ 84 'android', 85 'log', 86] 87 88# A dictionary that adds extra content to a specific Android.bp according to the 89# provided path. 90# The path defined must be relative to the root-repo. 91BLUEPRINTS_EXTRAS = { 92 "": ["build = [\"Android.extras.bp\"]"] 93} 94 95# A dictionary that specifies the relocation of modules from one blueprint to 96# another. 97# The default format is (relative_path_A -> relative_path_B), this means 98# that all targets which should live in relative_path_A/Android.bp will live 99# inside relative_path_B/Android.bp. 100BLUEPRINTS_MAPPING = { 101 # An Android.bp already exists inside boringssl, creating another one will 102 # lead to conflicts, add all of the boringssl generated targets to the 103 # top-level Android.bp as they are only used for tests. 104 "third_party/boringssl": "", 105 # Moving is undergoing, see crbug/40273848 106 "buildtools/third_party/libc++": "third_party/libc++", 107 # Moving is undergoing, see crbug/40273848 108 "buildtools/third_party/libc++abi": "third_party/libc++abi", 109} 110 111# Usually, README.chromium lives next to the BUILD.gn. However, some cases are 112# different, this dictionary allows setting a specific README.chromium path 113# for a specific BUILD.gn 114README_MAPPING = { 115 # Moving is undergoing, see crbug/40273848 116 "buildtools/third_party/libc++": "third_party/libc++", 117 # Moving is undergoing, see crbug/40273848 118 "buildtools/third_party/libc++abi": "third_party/libc++abi", 119} 120 121# Include directories that will be removed from all targets. 122include_dirs_denylist = [ 123 'external/cronet/third_party/zlib/', 124] 125 126# Name of the module which settings such as compiler flags for all other 127# modules. 128cc_defaults_module = module_prefix + 'cc_defaults' 129 130# Name of the java default module for non-test java modules defined in Android.extras.bp 131java_framework_defaults_module = 'cronet_aml_java_framework_defaults' 132 133# Location of the project in the Android source tree. 134tree_path = 'external/cronet' 135 136# Path for the protobuf sources in the standalone build. 137buildtools_protobuf_src = '//buildtools/protobuf/src' 138 139# Location of the protobuf src dir in the Android source tree. 140android_protobuf_src = 'external/protobuf/src' 141 142# put all args on a new line for better diffs. 143NEWLINE = ' " +\n "' 144 145# Compiler flags which are passed through to the blueprint. 146cflag_allowlist = [ 147 # needed for zlib:zlib 148 "-mpclmul", 149 # needed for zlib:zlib 150 "-mssse3", 151 # needed for zlib:zlib 152 "-msse3", 153 # needed for zlib:zlib 154 "-msse4.2", 155 # flags to reduce binary size 156 "-O1", 157 "-O2", 158 "-O3", 159 "-Oz", 160 "-g1", 161 "-g2", 162 "-fdata-sections", 163 "-ffunction-sections", 164 "-fvisibility=hidden", 165 "-fvisibility-inlines-hidden", 166 "-fstack-protector", 167 "-mno-outline", 168 "-mno-outline-atomics", 169 "-fno-asynchronous-unwind-tables", 170 "-fno-unwind-tables", 171] 172 173# Linker flags which are passed through to the blueprint. 174ldflag_allowlist = [ 175 # flags to reduce binary size 176 "-Wl,--as-needed", 177 "-Wl,--gc-sections", 178 "-Wl,--icf=all", 179] 180 181def get_linker_script_ldflag(script_path): 182 return f'-Wl,--script,{tree_path}/{script_path}' 183 184# Additional arguments to apply to Android.bp rules. 185additional_args = { 186 'cronet_aml_net_third_party_quiche_net_quic_test_tools_proto_gen_headers': [ 187 ('export_include_dirs', { 188 "net/third_party/quiche/src", 189 }) 190 ], 191 'cronet_aml_net_third_party_quiche_net_quic_test_tools_proto_gen__testing_headers': [ 192 ('export_include_dirs', { 193 "net/third_party/quiche/src", 194 }) 195 ], 196 'cronet_aml_third_party_quic_trace_quic_trace_proto_gen__testing_headers': [ 197 ('export_include_dirs', { 198 "third_party/quic_trace/src", 199 }) 200 ], 201 # TODO: fix upstream. Both //base:base and 202 # //base/allocator/partition_allocator:partition_alloc do not create a 203 # dependency on gtest despite using gtest_prod.h. 204 'cronet_aml_base_base': [ 205 ('header_libs', { 206 'libgtest_prod_headers', 207 }), 208 ('export_header_lib_headers', { 209 'libgtest_prod_headers', 210 }), 211 ], 212 'cronet_aml_base_allocator_partition_allocator_partition_alloc': [ 213 ('header_libs', { 214 'libgtest_prod_headers', 215 }), 216 ], 217 # TODO(b/309920629): Remove once upstreamed. 218 'cronet_aml_components_cronet_android_cronet_api_java': [ 219 ('srcs', { 220 'components/cronet/android/api/src/org/chromium/net/UploadDataProviders.java', 221 'components/cronet/android/api/src/org/chromium/net/apihelpers/UploadDataProviders.java', 222 }), 223 ], 224 'cronet_aml_components_cronet_android_cronet_api_java__testing': [ 225 ('srcs', { 226 'components/cronet/android/api/src/org/chromium/net/UploadDataProviders.java', 227 'components/cronet/android/api/src/org/chromium/net/apihelpers/UploadDataProviders.java', 228 }), 229 ], 230 'cronet_aml_components_cronet_android_cronet_javatests__testing': [ 231 # Needed to @SkipPresubmit annotations 232 ('static_libs', { 233 'net-tests-utils', 234 }), 235 # This is necessary because net-tests-utils compiles against private SDK. 236 ('sdk_version', ""), 237 ], 238 'cronet_aml_components_cronet_android_cronet__testing': [ 239 ('target', ('android_riscv64', {'stem': "libmainlinecronet_riscv64"})), 240 ('comment', """TODO: remove stem for riscv64 241// This is essential as there can't be two different modules 242// with the same output. We usually got away with that because 243// the non-testing Cronet is part of the Tethering APEX and the 244// testing Cronet is not part of the Tethering APEX which made them 245// look like two different outputs from the build system perspective. 246// However, Cronet does not ship to Tethering APEX for RISCV64 which 247// raises the conflict. Once we start shipping Cronet for RISCV64, 248// this can be removed."""), 249 ], 250 'cronet_aml_third_party_netty_tcnative_netty_tcnative_so__testing': [ 251 ('cflags', { 252 "-Wno-error=pointer-bool-conversion" 253 }) 254 ], 255 'cronet_aml_third_party_apache_portable_runtime_apr__testing': [ 256 ('cflags', { 257 "-Wno-incompatible-pointer-types-discards-qualifiers", 258 }) 259 ], 260 # TODO(b/324872305): Remove when gn desc expands public_configs and update code to propagate the 261 # include_dir from the public_configs 262 # We had to add the export_include_dirs for each target because soong generates each header 263 # file in a specific directory named after the target. 264 'cronet_aml_base_allocator_partition_allocator_src_partition_alloc_chromecast_buildflags': [ 265 ('export_include_dirs', { 266 "base/allocator/partition_allocator/src/", 267 }) 268 ], 269 'cronet_aml_base_allocator_partition_allocator_src_partition_alloc_chromecast_buildflags__testing': [ 270 ('export_include_dirs', { 271 "base/allocator/partition_allocator/src/", 272 }) 273 ], 274 'cronet_aml_base_allocator_partition_allocator_src_partition_alloc_chromeos_buildflags': [ 275 ('export_include_dirs', { 276 "base/allocator/partition_allocator/src/", 277 }) 278 ], 279 'cronet_aml_base_allocator_partition_allocator_src_partition_alloc_chromeos_buildflags__testing': [ 280 ('export_include_dirs', { 281 "base/allocator/partition_allocator/src/", 282 }) 283 ], 284 'cronet_aml_base_allocator_partition_allocator_src_partition_alloc_debugging_buildflags': [ 285 ('export_include_dirs', { 286 "base/allocator/partition_allocator/src/", 287 }) 288 ], 289 'cronet_aml_base_allocator_partition_allocator_src_partition_alloc_debugging_buildflags__testing': [ 290 ('export_include_dirs', { 291 "base/allocator/partition_allocator/src/", 292 }) 293 ], 294 'cronet_aml_base_allocator_partition_allocator_src_partition_alloc_partition_alloc_buildflags': [ 295 ('export_include_dirs', { 296 ".", 297 "base/allocator/partition_allocator/src/", 298 }) 299 ], 300 'cronet_aml_base_allocator_partition_allocator_src_partition_alloc_partition_alloc_buildflags__testing': [ 301 ('export_include_dirs', { 302 ".", 303 "base/allocator/partition_allocator/src/", 304 }) 305 ], 306 'cronet_aml_base_allocator_partition_allocator_src_partition_alloc_raw_ptr_buildflags': [ 307 ('export_include_dirs', { 308 "base/allocator/partition_allocator/src/", 309 }) 310 ], 311 'cronet_aml_base_allocator_partition_allocator_src_partition_alloc_raw_ptr_buildflags__testing': [ 312 ('export_include_dirs', { 313 "base/allocator/partition_allocator/src/", 314 }) 315 ], 316 'cronet_aml_base_base_java_test_support__testing': [ 317 ('errorprone', ( 318 'javacflags', { 319 "-Xep:ReturnValueIgnored:WARN", 320 } 321 ) 322 ) 323 ] 324 # end export_include_dir. 325} 326 327_FEATURE_REGEX = "feature=\\\"(.+)\\\"" 328_RUST_FLAGS_TO_REMOVE = [ 329 "--target", # Added by Soong 330 "--color", # Added by Soong. 331 "--edition", # Added to the appropriate field, must be removed from flags. 332 "--sysroot", # Use AOSP's stdlib so we don't need any hacks for sysroot. 333 "-Cembed-bitcode=no", # Not compatible with Thin-LTO which is added by Soong. 334 "--cfg", # Added to the appropriate field. 335 "--extern", # Soong automatically adds that for us when we use proc_macro 336 "@", # Used by build_script outputs to have rustc load flags from a file. 337 "-Z", # Those are unstable features, completely remove those. 338] 339 340def always_disable(module, arch): 341 return None 342 343def enable_zlib(module, arch): 344 # Requires crrev/c/4109079 345 if arch == 'common': 346 module.shared_libs.add('libz') 347 else: 348 module.target[arch].shared_libs.add('libz') 349 350def enable_boringssl(module, arch): 351 # Do not add boringssl targets to cc_genrules. This happens, because protobuf targets are 352 # originally static_libraries, but later get converted to a cc_genrule. 353 if module.is_genrule(): return 354 # Lets keep statically linking BoringSSL for testing target for now. This should be fixed. 355 if module.name.endswith(gn_utils.TESTING_SUFFIX): return 356 if arch == 'common': 357 shared_libs = module.shared_libs 358 else: 359 shared_libs = module.target[arch].shared_libs 360 shared_libs.add('//external/cronet/third_party/boringssl:libcrypto') 361 shared_libs.add('//external/cronet/third_party/boringssl:libssl') 362 shared_libs.add('//external/cronet/third_party/boringssl:libpki') 363 364def add_androidx_experimental_java_deps(module, arch): 365 module.libs.add("androidx.annotation_annotation-experimental") 366 367def add_androidx_annotation_java_deps(module, arch): 368 module.libs.add("androidx.annotation_annotation") 369 370def add_protobuf_lite_runtime_java_deps(module, arch): 371 module.static_libs.add("libprotobuf-java-lite") 372 373def add_androidx_core_java_deps(module, arch): 374 module.libs.add("androidx.core_core") 375 376def add_jsr305_java_deps(module, arch): 377 module.libs.add("jsr305") 378 379def add_errorprone_annotation_java_deps(module, arch): 380 module.libs.add("error_prone_annotations") 381 382def add_androidx_collection_java_deps(module, arch): 383 module.libs.add("androidx.collection_collection") 384 385def add_junit_java_deps(module, arch): 386 module.static_libs.add("junit") 387 388def add_truth_java_deps(module, arch): 389 module.static_libs.add("truth") 390 391def add_hamcrest_java_deps(module, arch): 392 module.static_libs.add("hamcrest-library") 393 module.static_libs.add("hamcrest") 394 395def add_mockito_java_deps(module, arch): 396 module.static_libs.add("mockito") 397 398def add_guava_java_deps(module, arch): 399 module.static_libs.add("guava") 400 401def add_androidx_junit_java_deps(module, arch): 402 module.static_libs.add("androidx.test.ext.junit") 403 404def add_androidx_test_runner_java_deps(module, arch): 405 module.static_libs.add("androidx.test.runner") 406 407def add_androidx_test_rules_java_deps(module, arch): 408 module.static_libs.add("androidx.test.rules") 409 410def add_android_test_base_java_deps(module, arch): 411 module.libs.add("android.test.base") 412 413def add_accessibility_test_framework_java_deps(module, arch): 414 # BaseActivityTestRule.java depends on this but BaseActivityTestRule.java is not used in aosp. 415 pass 416 417def add_espresso_java_deps(module, arch): 418 module.static_libs.add("androidx.test.espresso.contrib") 419 420def add_android_test_mock_java_deps(module, arch): 421 module.libs.add("android.test.mock.stubs") 422 423def add_androidx_multidex_java_deps(module, arch): 424 # Androidx-multidex is disabled on unbundled branches. 425 pass 426 427def add_androidx_test_monitor_java_deps(module, arch): 428 module.libs.add("androidx.test.monitor") 429 430def add_androidx_ui_automator_java_deps(module, arch): 431 module.static_libs.add("androidx.test.uiautomator_uiautomator") 432 433def add_androidx_test_annotation_java_deps(module, arch): 434 module.static_libs.add("androidx.test.rules") 435 436def add_androidx_test_core_java_deps(module, arch): 437 module.static_libs.add("androidx.test.core") 438 439def add_androidx_activity_activity(module, arch): 440 module.static_libs.add("androidx.activity_activity") 441 442def add_androidx_fragment_fragment(module, arch): 443 module.static_libs.add("androidx.fragment_fragment") 444 445# Android equivalents for third-party libraries that the upstream project 446# depends on. This will be applied to normal and testing targets. 447_builtin_deps = { 448 '//buildtools/third_party/libunwind:libunwind': 449 always_disable, 450 # This is a binary module that generates C++ binding files, Skip this 451 # dependency completely as we construct the modules differently. 452 '//third_party/rust/cxxbridge_cmd/v1:cxxbridge': 453 always_disable, 454 '//net/data/ssl/chrome_root_store:gen_root_store_inc': 455 always_disable, 456 '//third_party/zstd:headers': 457 always_disable, 458 '//testing/buildbot/filters:base_unittests_filters': 459 always_disable, 460 '//testing/buildbot/filters:net_unittests_filters': 461 always_disable, 462 '//third_party/boringssl/src/third_party/fiat:fiat_license': 463 always_disable, 464 '//net/tools/root_store_tool:root_store_tool': 465 always_disable, 466 '//third_party/zlib:zlib': 467 enable_zlib, 468 '//third_party/androidx:androidx_annotation_annotation_java': 469 add_androidx_annotation_java_deps, 470 '//third_party/android_deps:protobuf_lite_runtime_java': 471 add_protobuf_lite_runtime_java_deps, 472 '//third_party/androidx:androidx_annotation_annotation_experimental_java': 473 add_androidx_experimental_java_deps, 474 '//third_party/androidx:androidx_core_core_java': 475 add_androidx_core_java_deps, 476 '//third_party/android_deps:com_google_code_findbugs_jsr305_java': 477 add_jsr305_java_deps, 478 '//third_party/android_deps:com_google_errorprone_error_prone_annotations_java': 479 add_errorprone_annotation_java_deps, 480 '//third_party/androidx:androidx_collection_collection_java': 481 add_androidx_collection_java_deps, 482 '//third_party/junit:junit': 483 add_junit_java_deps, 484 '//third_party/google-truth:google_truth_java': 485 add_truth_java_deps, 486 '//third_party/hamcrest:hamcrest_core_java': 487 add_hamcrest_java_deps, 488 '//third_party/mockito:mockito_java': 489 add_mockito_java_deps, 490 '//third_party/android_deps:guava_android_java': 491 add_guava_java_deps, 492 '//third_party/androidx:androidx_test_ext_junit_java': 493 add_androidx_junit_java_deps, 494 '//third_party/androidx:androidx_test_runner_java': 495 add_androidx_test_runner_java_deps, 496 '//third_party/android_sdk:android_test_base_java': 497 add_android_test_base_java_deps, 498 '//third_party/accessibility_test_framework:accessibility_test_framework_java': 499 add_accessibility_test_framework_java_deps, 500 '//third_party/accessibility_test_framework:accessibility_core_java': 501 add_accessibility_test_framework_java_deps, 502 '//third_party/android_deps:espresso_java': 503 add_espresso_java_deps, 504 '//third_party/android_sdk:android_test_mock_java': 505 add_android_test_mock_java_deps, 506 '//third_party/androidx:androidx_multidex_multidex_java': 507 add_androidx_multidex_java_deps, 508 '//third_party/androidx:androidx_test_monitor_java': 509 add_androidx_test_monitor_java_deps, 510 '//third_party/androidx:androidx_test_annotation_java': 511 add_androidx_test_annotation_java_deps, 512 '//third_party/androidx:androidx_test_core_java': 513 add_androidx_test_core_java_deps, 514 '//third_party/androidx:androidx_test_uiautomator_uiautomator_java': 515 add_androidx_ui_automator_java_deps, 516 '//third_party/hamcrest:hamcrest_java': 517 add_hamcrest_java_deps, 518 '//third_party/androidx:androidx_activity_activity_java': 519 add_androidx_activity_activity, 520 '//third_party/androidx:androidx_fragment_fragment_java': 521 add_androidx_fragment_fragment, 522 '//third_party/androidx:androidx_test_rules_java': 523 add_androidx_test_rules_java_deps, 524} 525builtin_deps = {"{}{}".format(key, suffix): value for key, value in _builtin_deps.items() for suffix in ["", gn_utils.TESTING_SUFFIX] } 526 527# Same as _builtin_deps but will only apply what is explicitly specified. 528builtin_deps.update({ 529 '//third_party/boringssl:boringssl': 530 enable_boringssl, 531 '//third_party/boringssl:boringssl_asm': 532 # Due to FIPS requirements, downstream BoringSSL has a different "shape" than upstream's. 533 # We're guaranteed that if X depends on :boringssl it will also depend on :boringssl_asm. 534 # Hence, always drop :boringssl_asm and handle the translation entirely in :boringssl. 535 always_disable, 536}) 537 538 539# Name of tethering apex module 540tethering_apex = "com.android.tethering" 541 542# Name of cronet api target 543java_api_target_name = "//components/cronet/android:cronet_api_java" 544 545# Visibility set for package default 546package_default_visibility = ":__subpackages__" 547 548# Visibility set for modules used from Connectivity and within external/cronet 549root_modules_visibility = {"//packages/modules/Connectivity:__subpackages__", 550 "//external/cronet:__subpackages__"} 551 552# ---------------------------------------------------------------------------- 553# End of configuration. 554# ---------------------------------------------------------------------------- 555 556def write_blueprint_key_value(output, name, value, sort=True): 557 """Writes a Blueprint key-value pair to the output""" 558 559 if isinstance(value, bool): 560 if value: 561 output.append(' %s: true,' % name) 562 else: 563 output.append(' %s: false,' % name) 564 return 565 if not value: 566 return 567 if isinstance(value, set): 568 value = sorted(value) 569 if isinstance(value, list): 570 output.append(' %s: [' % name) 571 for item in sorted(value) if sort else value: 572 output.append(' "%s",' % item) 573 output.append(' ],') 574 return 575 if isinstance(value, Module.Target): 576 value.to_string(output) 577 return 578 if isinstance(value, dict): 579 kv_output = [] 580 for k, v in value.items(): 581 write_blueprint_key_value(kv_output, k, v) 582 583 output.append(' %s: {' % name) 584 for line in kv_output: 585 output.append(' %s' % line) 586 output.append(' },') 587 return 588 output.append(' %s: "%s",' % (name, value)) 589 590 591 592class Module(object): 593 """A single module (e.g., cc_binary, cc_test) in a blueprint.""" 594 595 class Target(object): 596 """A target-scoped part of a module""" 597 598 def __init__(self, name): 599 self.name = name 600 self.srcs = set() 601 self.shared_libs = set() 602 self.static_libs = set() 603 self.whole_static_libs = set() 604 self.header_libs = set() 605 self.cflags = set() 606 self.stl = None 607 self.cppflags = set() 608 self.include_dirs = set() 609 self.generated_headers = set() 610 self.export_generated_headers = set() 611 self.ldflags = set() 612 self.compile_multilib = None 613 self.stem = "" 614 self.edition = "" 615 self.features = set() 616 self.cfgs = set() 617 self.flags = list() 618 self.rustlibs = set() 619 self.proc_macros = set() 620 if name == 'host': 621 self.compile_multilib = '64' 622 623 def to_string(self, output): 624 nested_out = [] 625 self._output_field(nested_out, 'srcs') 626 self._output_field(nested_out, 'shared_libs') 627 self._output_field(nested_out, 'static_libs') 628 self._output_field(nested_out, 'whole_static_libs') 629 self._output_field(nested_out, 'header_libs') 630 self._output_field(nested_out, 'cflags') 631 self._output_field(nested_out, 'stl') 632 self._output_field(nested_out, 'cppflags') 633 self._output_field(nested_out, 'include_dirs') 634 self._output_field(nested_out, 'generated_headers') 635 self._output_field(nested_out, 'export_generated_headers') 636 self._output_field(nested_out, 'ldflags') 637 self._output_field(nested_out, 'stem') 638 self._output_field(nested_out, "edition") 639 self._output_field(nested_out, 'cfgs') 640 self._output_field(nested_out, 'features') 641 self._output_field(nested_out, 'flags', False) 642 self._output_field(nested_out, 'rustlibs') 643 self._output_field(nested_out, 'proc_macros') 644 645 if nested_out: 646 # This is added here to make sure it doesn't add a `host` arch-specific module just for 647 # `compile_multilib` flag. 648 self._output_field(nested_out, 'compile_multilib') 649 output.append(' %s: {' % self.name) 650 for line in nested_out: 651 output.append(' %s' % line) 652 output.append(' },') 653 654 def _output_field(self, output, name, sort=True): 655 value = getattr(self, name) 656 return write_blueprint_key_value(output, name, value, sort) 657 658 659 def __init__(self, mod_type, name, gn_target): 660 self.type = mod_type 661 self.gn_target = gn_target 662 self.name = name 663 self.srcs = set() 664 self.comment = 'GN: ' + gn_target 665 self.shared_libs = set() 666 self.static_libs = set() 667 self.whole_static_libs = set() 668 self.tools = set() 669 self.cmd = None 670 self.host_supported = False 671 self.device_supported = True 672 self.init_rc = set() 673 self.out = set() 674 self.export_include_dirs = set() 675 self.generated_headers = set() 676 self.export_generated_headers = set() 677 self.export_static_lib_headers = set() 678 self.export_header_lib_headers = set() 679 self.defaults = set() 680 self.cflags = set() 681 self.include_dirs = set() 682 self.local_include_dirs = set() 683 self.header_libs = set() 684 self.tool_files = set() 685 # target contains a dict of Targets indexed by os_arch. 686 # example: { 'android_x86': Target('android_x86') 687 self.target = dict() 688 self.target['android'] = self.Target('android') 689 self.target['android_x86'] = self.Target('android_x86') 690 self.target['android_x86_64'] = self.Target('android_x86_64') 691 self.target['android_arm'] = self.Target('android_arm') 692 self.target['android_arm64'] = self.Target('android_arm64') 693 self.target['android_riscv64'] = self.Target('android_riscv64') 694 self.target['host'] = self.Target('host') 695 self.target['glibc'] = self.Target('glibc') 696 self.stl = None 697 self.cpp_std = None 698 self.strip = dict() 699 self.data = set() 700 self.apex_available = set() 701 self.min_sdk_version = None 702 self.proto = dict() 703 self.linker_scripts = set() 704 self.ldflags = set() 705 # The genrule_XXX below are properties that must to be propagated back 706 # on the module(s) that depend on the genrule. 707 self.genrule_headers = set() 708 self.genrule_srcs = set() 709 self.genrule_shared_libs = set() 710 self.genrule_header_libs = set() 711 self.version_script = None 712 self.test_suites = set() 713 self.test_config = None 714 self.cppflags = set() 715 self.rtti = False 716 # Name of the output. Used for setting .so file name for libcronet 717 self.libs = set() 718 self.stem = None 719 self.compile_multilib = None 720 self.aidl = dict() 721 self.plugins = set() 722 self.processor_class = None 723 self.sdk_version = None 724 self.javacflags = set() 725 self.c_std = None 726 self.default_applicable_licenses = set() 727 self.default_visibility = [] 728 self.visibility = set() 729 self.gn_type = None 730 self.jarjar_rules = "" 731 self.jars = set() 732 self.build_file_path = None 733 self.include_build_directory = None 734 self.allow_rebasing = False 735 self.license_kinds = set() 736 self.license_text = set() 737 self.errorprone = dict() 738 self.crate_name = None 739 # Should be arch-dependant 740 self.crate_root = None 741 self.edition = None 742 self.rustlibs = set() 743 self.proc_macros = set() 744 745 def to_string(self, output): 746 if self.comment: 747 output.append('// %s' % self.comment) 748 output.append('%s {' % self.type) 749 self._output_field(output, 'name') 750 self._output_field(output, 'srcs') 751 self._output_field(output, 'shared_libs') 752 self._output_field(output, 'static_libs') 753 self._output_field(output, 'whole_static_libs') 754 self._output_field(output, 'tools') 755 self._output_field(output, 'cmd', sort=False) 756 if self.host_supported: 757 self._output_field(output, 'host_supported') 758 if not self.device_supported: 759 self._output_field(output, 'device_supported') 760 self._output_field(output, 'init_rc') 761 self._output_field(output, 'out') 762 self._output_field(output, 'export_include_dirs') 763 self._output_field(output, 'generated_headers') 764 self._output_field(output, 'export_generated_headers') 765 self._output_field(output, 'export_static_lib_headers') 766 self._output_field(output, 'export_header_lib_headers') 767 self._output_field(output, 'defaults') 768 self._output_field(output, 'cflags') 769 self._output_field(output, 'include_dirs') 770 self._output_field(output, 'local_include_dirs') 771 self._output_field(output, 'header_libs') 772 self._output_field(output, 'strip') 773 self._output_field(output, 'tool_files') 774 self._output_field(output, 'data') 775 self._output_field(output, 'stl') 776 self._output_field(output, 'cpp_std') 777 self._output_field(output, 'apex_available') 778 self._output_field(output, 'min_sdk_version') 779 self._output_field(output, 'version_script') 780 self._output_field(output, 'test_suites') 781 self._output_field(output, 'test_config') 782 self._output_field(output, 'proto') 783 self._output_field(output, 'linker_scripts') 784 self._output_field(output, 'ldflags') 785 self._output_field(output, 'cppflags') 786 self._output_field(output, 'libs') 787 self._output_field(output, 'stem') 788 self._output_field(output, 'compile_multilib') 789 self._output_field(output, 'aidl') 790 self._output_field(output, 'plugins') 791 self._output_field(output, 'processor_class') 792 self._output_field(output, 'sdk_version') 793 self._output_field(output, 'javacflags') 794 self._output_field(output, 'c_std') 795 self._output_field(output, 'default_applicable_licenses') 796 self._output_field(output, 'default_visibility') 797 self._output_field(output, 'visibility') 798 self._output_field(output, 'jarjar_rules') 799 self._output_field(output, 'jars') 800 self._output_field(output, 'include_build_directory') 801 self._output_field(output, 'license_text') 802 self._output_field(output, "license_kinds") 803 self._output_field(output, "errorprone") 804 self._output_field(output, 'crate_name') 805 self._output_field(output, 'crate_root') 806 self._output_field(output, 'rustlibs') 807 self._output_field(output, 'proc_macros') 808 if self.rtti: 809 self._output_field(output, 'rtti') 810 811 target_out = [] 812 for arch, target in sorted(self.target.items()): 813 # _output_field calls getattr(self, arch). 814 setattr(self, arch, target) 815 self._output_field(target_out, arch) 816 817 if target_out: 818 output.append(' target: {') 819 for line in target_out: 820 output.append(' %s' % line) 821 output.append(' },') 822 823 output.append('}') 824 output.append('') 825 826 def add_android_shared_lib(self, lib): 827 if self.type.startswith('java'): 828 raise Exception('Adding Android shared lib for java_* targets is unsupported') 829 elif self.type == 'cc_binary_host': 830 raise Exception('Adding Android shared lib for host tool is unsupported') 831 elif self.host_supported: 832 self.target['android'].shared_libs.add(lib) 833 else: 834 self.shared_libs.add(lib) 835 836 def is_test(self): 837 if gn_utils.TESTING_SUFFIX in self.name: 838 name_without_prefix = self.name[:self.name.find(gn_utils.TESTING_SUFFIX)] 839 return any([name_without_prefix == label_to_module_name(target) for target in DEFAULT_TESTS]) 840 return False 841 842 def _output_field(self, output, name, sort=True): 843 value = getattr(self, name) 844 return write_blueprint_key_value(output, name, value, sort) 845 846 def is_compiled(self): 847 return self.type not in ('cc_genrule', 'filegroup', 'java_genrule') 848 849 def is_genrule(self): 850 return self.type == "cc_genrule" 851 852 def has_input_files(self): 853 if self.type in ["java_library", "java_import"]: 854 return True 855 if len(self.srcs) > 0: 856 return True 857 if any([len(target.srcs) > 0 for target in self.target.values()]): 858 return True 859 # Allow cc_static_library with export_generated_headers as those are crucial for 860 # the depending modules 861 return len(self.export_generated_headers) > 0 862 863 864class Blueprint(object): 865 """In-memory representation of an Android.bp file.""" 866 867 def __init__(self, buildgn_directory_path: str = ""): 868 self.modules = {} 869 # Holds the BUILD.gn path which resulted in the creation of this Android.bp. 870 self._buildgn_directory_path = buildgn_directory_path 871 self._readme_location = buildgn_directory_path 872 self._package_module = None 873 self._license_module = None 874 875 def add_module(self, module): 876 """Adds a new module to the blueprint, replacing any existing module 877 with the same name. 878 879 Args: 880 module: Module instance. 881 """ 882 self.modules[module.name] = module 883 884 def set_package_module(self, module): 885 self._package_module = module 886 887 def set_license_module(self, module): 888 self._license_module = module 889 890 def get_license_module(self): 891 return self._license_module 892 893 def set_readme_location(self, readme_path: str): 894 self._readme_location = readme_path 895 896 def get_readme_location(self): 897 return self._readme_location 898 899 def get_buildgn_location(self): 900 return self._buildgn_directory_path 901 902 def to_string(self): 903 ret = [] 904 if self._package_module: 905 self._package_module.to_string(ret) 906 if self._license_module: 907 self._license_module.to_string(ret) 908 for m in sorted(self.modules.values(), key=lambda m: m.name): 909 if m.type != "cc_library_static" or m.has_input_files(): 910 # Don't print cc_library_static with empty srcs. These attributes are already 911 # propagated up the tree. Printing them messes the presubmits because 912 # every module is compiled while those targets are not reachable in 913 # a normal compilation path. 914 m.to_string(ret) 915 return ret 916 917 918def label_to_module_name(label): 919 """Turn a GN label (e.g., //:perfetto_tests) into a module name.""" 920 module = re.sub(r'^//:?', '', label) 921 module = re.sub(r'[^a-zA-Z0-9_]', '_', module) 922 923 if not module.startswith(module_prefix): 924 return module_prefix + module 925 return module 926 927 928def is_supported_source_file(name): 929 """Returns True if |name| can appear in a 'srcs' list.""" 930 return os.path.splitext(name)[1] in ['.c', '.cc', '.cpp', '.java', '.proto', '.S', '.aidl', '.rs'] 931 932def normalize_rust_flags(rust_flags: List[str]) -> Dict[str, Set[str] | None]: 933 """ 934 Normalizes the rust params where it tries to put (key, value) param 935 as a dictionary key. A key without value will have None as value. 936 937 An example of this would be: 938 939 Input: ["--cfg=feature=\"float_roundtrip\"", "--cfg=feature=\"std\"", 940 "--edition=2021", "-Cforce-unwind-tables=no", "-Dwarnings"] 941 942 Output: { 943 "--cfg": [feature=\"float_roundtrip\", feature=\"std\"], 944 "--edition": [2021], 945 "-Cforce-unwind-tables": [no], 946 "-Dwarnings": None 947 } 948 :param rust_flags: List of rust flags. 949 :return: Dictionary of rust flags where each key will point to a list of 950 values. 951 """ 952 args_mapping = {} 953 previous_key = None 954 for rust_flag in rust_flags: 955 if not rust_flag.startswith("-"): 956 # This might be a key on its own, rustc supports params with no keys 957 # such as (@path). 958 if rust_flag.startswith("@"): 959 args_mapping[rust_flag] = None 960 if previous_key: 961 args_mapping[previous_key] = None 962 else: 963 # This is the value to the previous key (eg: ["--cfg", "val"]) 964 if not previous_key: 965 raise ValueError( 966 f"Field {rust_flag} does not relate to any key. Rust flags found: {rust_flags}") 967 if previous_key not in args_mapping: 968 args_mapping[previous_key] = set() 969 args_mapping[previous_key].add(rust_flag) 970 previous_key = None 971 else: 972 if previous_key: 973 # We have a previous key, that means that the previous key is 974 # a no-value key. 975 args_mapping[previous_key] = None 976 previous_key = None 977 # This can be a key-only string or key=value or 978 # key=foo=value (eg:--cfg=feature=X) or key and value in different strings. 979 if "=" in rust_flag: 980 # We found an equal, this is probably a key=value string. 981 rust_flag_split = rust_flag.split("=") 982 if len(rust_flag_split) > 3: 983 raise ValueError(f"Could not normalize flag {rust_flag} as it has multiple equal signs.") 984 if rust_flag_split[0] not in args_mapping: 985 args_mapping[rust_flag_split[0]] = set() 986 args_mapping[rust_flag_split[0]].add("=".join(rust_flag_split[1:])) 987 else: 988 # Assume this is a key-only string. This will be resolved in the next 989 # iteration. 990 previous_key = rust_flag 991 if previous_key: 992 # We have a previous key without a value, this must be a key-only string. 993 args_mapping[previous_key] = None 994 return args_mapping 995 996 997def _set_rust_flags(module: Module.Target, rust_flags: List[str], arch_name: str) -> None: 998 rust_flags_dict = normalize_rust_flags(rust_flags) 999 if "--edition" in rust_flags_dict: 1000 module.edition = list(rust_flags_dict["--edition"])[0] 1001 1002 for cfg in rust_flags_dict.get("--cfg", set()): 1003 feature_regex = re.match(_FEATURE_REGEX, cfg) 1004 if feature_regex: 1005 module.features.add(feature_regex.group(1)) 1006 else: 1007 module.cfgs.add(cfg.replace("\"", "\\\"")) 1008 1009 pre_filter_flags = [] 1010 for (key, values) in rust_flags_dict.items(): 1011 if values is None: 1012 pre_filter_flags.append(key) 1013 else: 1014 pre_filter_flags.extend(f"{key}={param_val}" for param_val in values) 1015 1016 flags_to_remove = _RUST_FLAGS_TO_REMOVE 1017 # AOSP compiles everything for host under panic=unwind instead of abort. 1018 # In order to be consistent with the ecosystem, remove the -Cpanic flag. 1019 if arch_name == "host": 1020 flags_to_remove.append("-Cpanic") 1021 1022 # Remove restricted flags 1023 for pre_filter_flag in pre_filter_flags: 1024 if not any([pre_filter_flag.startswith(restricted_flag) for restricted_flag in flags_to_remove]): 1025 module.flags.append(pre_filter_flag) 1026 1027def get_protoc_module_name(gn): 1028 protoc_gn_target_name = gn.get_target('//third_party/protobuf:protoc').name 1029 return label_to_module_name(protoc_gn_target_name) 1030 1031def create_rust_cxx_module(blueprint, target): 1032 """Generate genrules for a CXX GN target 1033 1034 GN actions are used to dynamically generate files during the build. The 1035 Soong equivalent is a genrule. Currently, Chromium GN targets generates 1036 both .cc and .h files in the same target, we have to split this up to be 1037 compatible with Soong. 1038 1039 CXX bridge binary is used from AOSP instead of compiling Chromium's CXX bridge. 1040 1041 Args: 1042 blueprint: Blueprint instance which is being generated. 1043 target: gn_utils.Target object. 1044 1045 Returns: 1046 The source_genrule module. 1047 """ 1048 header_genrule = Module("cc_genrule", label_to_module_name(target.name) + "_header", target.name) 1049 header_genrule.tools = {"cxxbridge"} 1050 header_genrule.cmd = "$(location cxxbridge) $(in) --header > $(out)" 1051 header_genrule.srcs = set([gn_utils.label_to_path(src) for src in target.sources]) 1052 # The output of the cc_genrule is the input + ".h" suffix, this is because 1053 # the input to a CXX genrule is just one source file. 1054 header_genrule.out = set([f"{gn_utils.label_to_path(out)}.h" for out in target.sources]) 1055 1056 cc_genrule = Module("cc_genrule", label_to_module_name(target.name), target.name) 1057 cc_genrule.tools = {"cxxbridge"} 1058 cc_genrule.cmd = "$(location cxxbridge) $(in) > $(out)" 1059 cc_genrule.srcs = set([gn_utils.label_to_path(src) for src in target.sources]) 1060 cc_genrule.genrule_srcs = {f":{cc_genrule.name}"} 1061 # The output of the cc_genrule is the input + ".cc" suffix, this is because 1062 # the input to a CXX genrule is just one source file. 1063 cc_genrule.out = set([f"{gn_utils.label_to_path(out)}.cc" for out in target.sources]) 1064 1065 cc_genrule.genrule_headers.add(header_genrule.name) 1066 blueprint.add_module(cc_genrule) 1067 blueprint.add_module(header_genrule) 1068 return cc_genrule 1069 1070def create_proto_modules(blueprint, gn, target): 1071 """Generate genrules for a proto GN target. 1072 1073 GN actions are used to dynamically generate files during the build. The 1074 Soong equivalent is a genrule. This function turns a specific kind of 1075 genrule which turns .proto files into source and header files into a pair 1076 equivalent genrules. 1077 1078 Args: 1079 blueprint: Blueprint instance which is being generated. 1080 target: gn_utils.Target object. 1081 1082 Returns: 1083 The source_genrule module. 1084 """ 1085 assert (target.type == 'proto_library') 1086 1087 protoc_module_name = get_protoc_module_name(gn) 1088 tools = {protoc_module_name} 1089 cpp_out_dir = '$(genDir)/%s/' % (target.proto_in_dir) 1090 target_module_name = label_to_module_name(target.name) 1091 1092 # In GN builds the proto path is always relative to the output directory 1093 # (out/tmp.xxx). 1094 cmd = ['$(location %s)' % protoc_module_name] 1095 cmd += ['--proto_path=%s/%s' % (tree_path, target.proto_in_dir)] 1096 1097 for proto_path in target.proto_paths: 1098 cmd += [f'--proto_path={tree_path}/{proto_path}'] 1099 if buildtools_protobuf_src in target.proto_paths: 1100 cmd += ['--proto_path=%s' % android_protobuf_src] 1101 1102 # We don't generate any targets for source_set proto modules because 1103 # they will be inlined into other modules if required. 1104 if target.proto_plugin == 'source_set': 1105 return None 1106 1107 # Descriptor targets only generate a single target. 1108 if target.proto_plugin == 'descriptor': 1109 out = '{}.bin'.format(target_module_name) 1110 1111 cmd += ['--descriptor_set_out=$(out)'] 1112 cmd += ['$(in)'] 1113 1114 descriptor_module = Module('cc_genrule', target_module_name, target.name) 1115 descriptor_module.cmd = ' '.join(cmd) 1116 descriptor_module.out = [out] 1117 descriptor_module.tools = tools 1118 blueprint.add_module(descriptor_module) 1119 1120 # Recursively extract the .proto files of all the dependencies and 1121 # add them to srcs. 1122 descriptor_module.srcs.update( 1123 gn_utils.label_to_path(src) for src in target.sources) 1124 for dep in target.proto_deps: 1125 current_target = gn.get_target(dep) 1126 descriptor_module.srcs.update( 1127 gn_utils.label_to_path(src) for src in current_target.sources) 1128 1129 return descriptor_module 1130 1131 # We create two genrules for each proto target: one for the headers and 1132 # another for the sources. This is because the module that depends on the 1133 # generated files needs to declare two different types of dependencies -- 1134 # source files in 'srcs' and headers in 'generated_headers' -- and it's not 1135 # valid to generate .h files from a source dependency and vice versa. 1136 source_module_name = target_module_name 1137 source_module = Module('cc_genrule', source_module_name, target.name) 1138 blueprint.add_module(source_module) 1139 source_module.srcs.update( 1140 gn_utils.label_to_path(src) for src in target.sources) 1141 1142 header_module = Module('cc_genrule', source_module_name + '_headers', 1143 target.name) 1144 blueprint.add_module(header_module) 1145 header_module.srcs = set(source_module.srcs) 1146 1147 header_module.export_include_dirs = {'.', 'protos'} 1148 # Since the .cc file and .h get created by a different gerule target, they 1149 # are not put in the same intermediate path, so local includes do not work 1150 # without explictily exporting the include dir. 1151 header_module.export_include_dirs.add(target.proto_in_dir) 1152 1153 # This function does not return header_module so setting apex_available attribute here. 1154 header_module.apex_available.add(tethering_apex) 1155 1156 source_module.genrule_srcs.add(':' + source_module.name) 1157 source_module.genrule_headers.add(header_module.name) 1158 1159 if target.proto_plugin == 'proto': 1160 suffixes = ['pb'] 1161 source_module.genrule_shared_libs.add('libprotobuf-cpp-lite') 1162 cmd += ['--cpp_out=lite=true:' + cpp_out_dir] 1163 else: 1164 raise Exception('Unsupported proto plugin: %s' % target.proto_plugin) 1165 1166 cmd += ['$(in)'] 1167 source_module.cmd = ' '.join(cmd) 1168 header_module.cmd = source_module.cmd 1169 source_module.tools = tools 1170 header_module.tools = tools 1171 1172 for sfx in suffixes: 1173 source_module.out.update('%s' % 1174 src.replace('.proto', '.%s.cc' % sfx) 1175 for src in source_module.srcs) 1176 header_module.out.update('%s' % 1177 src.replace('.proto', '.%s.h' % sfx) 1178 for src in header_module.srcs) 1179 # This has proto files that will be used for reference resolution 1180 # but not compiled into cpp files. These additional sources has no output. 1181 proto_data_sources = sorted([gn_utils.label_to_path(proto_src) 1182 for proto_src in target.inputs if proto_src.endswith(".proto")]) 1183 source_module.srcs.update(proto_data_sources) 1184 header_module.srcs.update(proto_data_sources) 1185 1186 # Allow rebasing proto genrules according to their proper path. 1187 source_module.allow_rebasing = True 1188 header_module.allow_rebasing = True 1189 header_module.build_file_path = target.build_file_path 1190 source_module.build_file_path = target.build_file_path 1191 return source_module 1192 1193 1194def create_gcc_preprocess_modules(blueprint, target): 1195 # gcc_preprocess.py internally execute host gcc which is not allowed in genrule. 1196 # So, this function create multiple modules and realize equivalent processing 1197 assert (len(target.sources) == 1) 1198 source = list(target.sources)[0] 1199 assert (Path(source).suffix == '.template') 1200 stem = Path(source).stem 1201 1202 bp_module_name = label_to_module_name(target.name) 1203 1204 # Rename .template to .cc since cc_preprocess_no_configuration does not accept .template file as 1205 # srcs 1206 rename_module = Module('genrule', bp_module_name + '_rename', target.name) 1207 rename_module.srcs.add(gn_utils.label_to_path(source)) 1208 rename_module.out.add(stem + '.cc') 1209 rename_module.cmd = 'cp $(in) $(out)' 1210 blueprint.add_module(rename_module) 1211 1212 # Preprocess template file and generates java file 1213 preprocess_module = Module( 1214 'cc_preprocess_no_configuration', 1215 bp_module_name + '_preprocess', 1216 target.name 1217 ) 1218 # -E: stop after preprocessing. 1219 # -P: disable line markers, i.e. '#line 309' 1220 preprocess_module.cflags.update(['-E', '-P', '-DANDROID']) 1221 preprocess_module.srcs.add(':' + rename_module.name) 1222 defines = ['-D' + target.args[i+1] for i, arg in enumerate(target.args) if arg == '--define'] 1223 preprocess_module.cflags.update(defines) 1224 blueprint.add_module(preprocess_module) 1225 1226 # Generates srcjar using soong_zip 1227 module = Module('genrule', bp_module_name, target.name) 1228 module.srcs.add(':' + preprocess_module.name) 1229 module.out.add(stem + '.srcjar') 1230 module.cmd = NEWLINE.join([ 1231 f'cp $(in) $(genDir)/{stem}.java &&', 1232 f'$(location soong_zip) -o $(out) -srcjar -C $(genDir) -f $(genDir)/{stem}.java' 1233 ]) 1234 module.tools.add('soong_zip') 1235 blueprint.add_module(module) 1236 return module 1237 1238 1239class BaseActionSanitizer(): 1240 def __init__(self, target, arch): 1241 # Just to be on the safe side, create a deep-copy. 1242 self.target = copy.deepcopy(target) 1243 if arch: 1244 # Merge arch specific attributes 1245 self.target.sources |= arch.sources 1246 self.target.inputs |= arch.inputs 1247 self.target.outputs |= arch.outputs 1248 self.target.script = self.target.script or arch.script 1249 self.target.args = self.target.args or arch.args 1250 self.target.response_file_contents = \ 1251 self.target.response_file_contents or arch.response_file_contents 1252 self.target.args = self._normalize_args() 1253 1254 def get_name(self): 1255 return label_to_module_name(self.target.name) 1256 1257 def _normalize_args(self): 1258 # Convert ['--param=value'] to ['--param', 'value'] for consistency. 1259 # Escape quotations. 1260 normalized_args = [] 1261 for arg in self.target.args: 1262 arg = arg.replace('"', r'\"') 1263 if arg.startswith('-'): 1264 normalized_args.extend(arg.split('=')) 1265 else: 1266 normalized_args.append(arg) 1267 return normalized_args 1268 1269 # There are three types of args: 1270 # - flags (--flag) 1271 # - value args (--arg value) 1272 # - list args (--arg value1 --arg value2) 1273 # value args have exactly one arg value pair and list args have one or more arg value pairs. 1274 # Note that the set of list args contains the set of value args. 1275 # This is because list and value args are identical when the list args has only one arg value pair 1276 # Some functions provide special implementations for each type, while others 1277 # work on all of them. 1278 def _has_arg(self, arg): 1279 return arg in self.target.args 1280 1281 def _get_arg_indices(self, target_arg): 1282 return [i for i, arg in enumerate(self.target.args) if arg == target_arg] 1283 1284 # Whether an arg value pair appears once or more times 1285 def _is_list_arg(self, arg): 1286 indices = self._get_arg_indices(arg) 1287 return len(indices) > 0 and all([not self.target.args[i + 1].startswith('--') for i in indices]) 1288 1289 def _update_list_arg(self, arg, func, throw_if_absent = True): 1290 if self._should_fail_silently(arg, throw_if_absent): 1291 return 1292 assert(self._is_list_arg(arg)) 1293 indices = self._get_arg_indices(arg) 1294 for i in indices: 1295 self._set_arg_at(i + 1, func(self.target.args[i + 1])) 1296 1297 # Whether an arg value pair appears exactly once 1298 def _is_value_arg(self, arg): 1299 return operator.countOf(self.target.args, arg) == 1 and self._is_list_arg(arg) 1300 1301 def _get_value_arg(self, arg): 1302 assert(self._is_value_arg(arg)) 1303 i = self.target.args.index(arg) 1304 return self.target.args[i + 1] 1305 1306 # used to check whether a function call should cause an error when an arg is 1307 # missing. 1308 def _should_fail_silently(self, arg, throw_if_absent): 1309 return not throw_if_absent and not self._has_arg(arg) 1310 1311 def _set_value_arg(self, arg, value, throw_if_absent = True): 1312 if self._should_fail_silently(arg, throw_if_absent): 1313 return 1314 assert(self._is_value_arg(arg)) 1315 i = self.target.args.index(arg) 1316 self.target.args[i + 1] = value 1317 1318 def _update_value_arg(self, arg, func, throw_if_absent = True): 1319 if self._should_fail_silently(arg, throw_if_absent): 1320 return 1321 self._set_value_arg(arg, func(self._get_value_arg(arg))) 1322 1323 def _set_arg_at(self, position, value): 1324 self.target.args[position] = value 1325 1326 def _update_arg_at(self, position, func): 1327 self.target.args[position] = func(self.target.args[position]) 1328 1329 def _delete_value_arg(self, arg, throw_if_absent = True): 1330 if self._should_fail_silently(arg, throw_if_absent): 1331 return 1332 assert(self._is_value_arg(arg)) 1333 i = self.target.args.index(arg) 1334 self.target.args.pop(i) 1335 self.target.args.pop(i) 1336 1337 def _append_arg(self, arg, value): 1338 self.target.args.append(arg) 1339 self.target.args.append(value) 1340 1341 def _sanitize_filepath_with_location_tag(self, arg): 1342 if arg.startswith('../../'): 1343 arg = self._sanitize_filepath(arg) 1344 arg = self._add_location_tag(arg) 1345 return arg 1346 1347 # wrap filename in location tag. 1348 def _add_location_tag(self, filename): 1349 return '$(location %s)' % filename 1350 1351 # applies common directory transformation that *should* be universally applicable. 1352 # TODO: verify if it actually *is* universally applicable. 1353 def _sanitize_filepath(self, filepath): 1354 # Careful, order matters! 1355 # delete all leading ../ 1356 filepath = re.sub('^(\.\./)+', '', filepath) 1357 filepath = re.sub('^gen/jni_headers', '$(genDir)', filepath) 1358 filepath = re.sub('^gen', '$(genDir)', filepath) 1359 return filepath 1360 1361 # Iterate through all the args and apply function 1362 def _update_all_args(self, func): 1363 self.target.args = [func(arg) for arg in self.target.args] 1364 1365 def get_pre_cmd(self): 1366 pre_cmd = [] 1367 out_dirs = [out[:out.rfind("/")] for out in self.target.outputs if "/" in out] 1368 # Sort the list to make the output deterministic. 1369 for out_dir in sorted(set(out_dirs)): 1370 pre_cmd.append("mkdir -p $(genDir)/{} && ".format(out_dir)) 1371 return NEWLINE.join(pre_cmd) 1372 1373 def get_base_cmd(self): 1374 arg_string = NEWLINE.join(self.target.args) 1375 cmd = '$(location %s) %s' % ( 1376 gn_utils.label_to_path(self.target.script), arg_string) 1377 1378 if self.use_response_file: 1379 # Pipe response file contents into script 1380 cmd = 'echo \'%s\' |%s%s' % (self.target.response_file_contents, NEWLINE, cmd) 1381 return cmd 1382 1383 def get_cmd(self): 1384 return self.get_pre_cmd() + self.get_base_cmd() 1385 1386 def get_outputs(self): 1387 return self.target.outputs 1388 1389 def get_srcs(self): 1390 # gn treats inputs and sources for actions equally. 1391 # soong only supports source files inside srcs, non-source files are added as 1392 # tool_files dependency. 1393 files = self.target.sources.union(self.target.inputs) 1394 return {gn_utils.label_to_path(file) for file in files if is_supported_source_file(file)} 1395 1396 def get_tools(self): 1397 return set() 1398 1399 def get_tool_files(self): 1400 # gn treats inputs and sources for actions equally. 1401 # soong only supports source files inside srcs, non-source files are added as 1402 # tool_files dependency. 1403 files = self.target.sources.union(self.target.inputs) 1404 tool_files = {gn_utils.label_to_path(file) 1405 for file in files if not is_supported_source_file(file)} 1406 tool_files.add(gn_utils.label_to_path(self.target.script)) 1407 return tool_files 1408 1409 def _sanitize_args(self): 1410 # Handle passing parameters via response file by piping them into the script 1411 # and reading them from /dev/stdin. 1412 1413 self.use_response_file = gn_utils.RESPONSE_FILE in self.target.args 1414 if self.use_response_file: 1415 # Replace {{response_file_contents}} with /dev/stdin 1416 self.target.args = ['/dev/stdin' if it == gn_utils.RESPONSE_FILE else it 1417 for it in self.target.args] 1418 1419 def _sanitize_inputs(self): 1420 pass 1421 1422 def get_deps(self): 1423 return self.target.deps 1424 1425 def sanitize(self): 1426 self._sanitize_args() 1427 self._sanitize_inputs() 1428 1429 # Whether this target generates header files 1430 def is_header_generated(self): 1431 return any(os.path.splitext(it)[1] == '.h' for it in self.target.outputs) 1432 1433class WriteBuildDateHeaderSanitizer(BaseActionSanitizer): 1434 def _sanitize_args(self): 1435 self._set_arg_at(0, '$(out)') 1436 super()._sanitize_args() 1437 1438class WriteBuildFlagHeaderSanitizer(BaseActionSanitizer): 1439 def _sanitize_args(self): 1440 self._set_value_arg('--gen-dir', '.') 1441 self._set_value_arg('--output', '$(out)') 1442 super()._sanitize_args() 1443 1444class GnRunBinarySanitizer(BaseActionSanitizer): 1445 def __init__(self, target, arch): 1446 super().__init__(target, arch) 1447 self.binary_to_target = { 1448 "clang_x64/transport_security_state_generator": 1449 "cronet_aml_net_tools_transport_security_state_generator_transport_security_state_generator__testing", 1450 } 1451 self.binary = self.binary_to_target[self.target.args[0]] 1452 1453 def _replace_gen_with_location_tag(self, arg): 1454 if arg.startswith("gen/"): 1455 return "$(location %s)" % arg.replace("gen/", "") 1456 return arg 1457 1458 def _replace_binary(self, arg): 1459 if arg in self.binary_to_target: 1460 return '$(location %s)' % self.binary 1461 return arg 1462 1463 def _remove_python_args(self): 1464 self.target.args = [arg for arg in self.target.args if "python3" not in arg] 1465 1466 def _sanitize_args(self): 1467 self._update_all_args(self._sanitize_filepath_with_location_tag) 1468 self._update_all_args(self._replace_gen_with_location_tag) 1469 self._update_all_args(self._replace_binary) 1470 self._remove_python_args() 1471 super()._sanitize_args() 1472 1473 def get_tools(self): 1474 tools = super().get_tools() 1475 tools.add(self.binary) 1476 return tools 1477 1478 def get_cmd(self): 1479 # Remove the script and use the binary right away 1480 return self.get_pre_cmd() + NEWLINE.join(self.target.args) 1481 1482class JniGeneratorSanitizer(BaseActionSanitizer): 1483 def __init__(self, target, arch, is_test_target): 1484 self.is_test_target = is_test_target 1485 super().__init__(target, arch) 1486 1487 def get_srcs(self): 1488 all_srcs = super().get_srcs() 1489 all_srcs.update({gn_utils.label_to_path(file) 1490 for file in self.target.transitive_jni_java_sources 1491 if is_supported_source_file(file)}) 1492 return set(src for src in all_srcs if src.endswith(".java")) 1493 1494 def _add_location_tag_to_filepath(self, arg): 1495 if not arg.endswith('.class'): 1496 # --input_file supports both .class specifiers or source files as arguments. 1497 # Only source files need to be wrapped inside a $(location <label>) tag. 1498 arg = self._add_location_tag(arg) 1499 return arg 1500 1501 def _sanitize_args(self): 1502 self._set_value_arg('--jar-file', '$(location :current_android_jar)', False) 1503 if self._has_arg('--jar-file'): 1504 self._set_value_arg('--javap', '$(location :javap)') 1505 self._update_value_arg('--srcjar-path', self._sanitize_filepath, False) 1506 self._update_value_arg('--output-dir', self._sanitize_filepath) 1507 self._update_value_arg('--extra-include', self._sanitize_filepath, False) 1508 self._update_value_arg('--placeholder-srcjar-path', self._sanitize_filepath, False) 1509 self._update_list_arg('--input-file', self._sanitize_filepath) 1510 self._update_list_arg('--input-file', self._add_location_tag_to_filepath) 1511 if not self.is_test_target and not self._has_arg('--jar-file'): 1512 # Don't jarjar classes that already exists within the java SDK. The headers generated 1513 # from those genrule can simply call into the original class as it exists outside 1514 # of cronet's jar. 1515 # Only jarjar platform code 1516 self._append_arg('--package-prefix', 'android.net.connectivity') 1517 super()._sanitize_args() 1518 1519 def get_outputs(self): 1520 outputs = set() 1521 for out in super().get_outputs(): 1522 # placeholder.srcjar contains empty placeholder classes used to compile generated java files 1523 # without any other deps. This is not used in aosp. 1524 if out.endswith("_placeholder.srcjar"): 1525 continue 1526 # fix target.output directory to match #include statements. 1527 outputs.add(re.sub('^jni_headers/', '', out)) 1528 return outputs 1529 1530 def get_tool_files(self): 1531 tool_files = super().get_tool_files() 1532 1533 # Filter android.jar and add :current_android_jar 1534 tool_files = {file if not file.endswith('android.jar') else ':current_android_jar' 1535 for file in tool_files } 1536 # Filter bin/javap 1537 tool_files = {file for file in tool_files if not file.endswith('bin/javap') } 1538 1539 # TODO: Remove once https://chromium-review.googlesource.com/c/chromium/src/+/5370266 has made 1540 # its way to AOSP 1541 # Files not specified in anywhere but jni_generator.py imports this file 1542 tool_files.add('third_party/jni_zero/codegen/header_common.py') 1543 tool_files.add('third_party/jni_zero/codegen/placeholder_java_type.py') 1544 1545 return tool_files 1546 1547 def get_tools(self): 1548 tools = super().get_tools() 1549 if self._has_arg('--jar-file'): 1550 tools.add(":javap") 1551 return tools 1552 1553 1554class JavaJniGeneratorSanitizer(JniGeneratorSanitizer): 1555 def __init__(self, target, arch, is_test_target): 1556 self.is_test_target = is_test_target 1557 super().__init__(target, arch, is_test_target) 1558 1559 def get_outputs(self): 1560 # fix target.output directory to match #include statements. 1561 outputs = {re.sub('^jni_headers/', '', out) for out in super().get_outputs()} 1562 self.target.outputs = [out for out in outputs if 1563 out.endswith(".srcjar")] 1564 return outputs 1565 1566 def get_deps(self): 1567 return {} 1568 1569 def get_name(self): 1570 name = super().get_name() + "__java" 1571 return name 1572 1573class JniRegistrationGeneratorSanitizer(BaseActionSanitizer): 1574 def __init__(self, target, arch, is_test_target): 1575 self.is_test_target = is_test_target 1576 super().__init__(target, arch) 1577 1578 def get_srcs(self): 1579 all_srcs = super().get_srcs() 1580 all_srcs.update({gn_utils.label_to_path(file) 1581 for file in self.target.transitive_jni_java_sources 1582 if is_supported_source_file(file)}) 1583 return set(src for src in all_srcs if src.endswith(".java")) 1584 1585 def _sanitize_inputs(self): 1586 self.target.inputs = [file for file in self.target.inputs if not file.startswith('//out/')] 1587 1588 def get_outputs(self): 1589 outputs = set() 1590 for out in super().get_outputs(): 1591 # placeholder.srcjar contains empty placeholder classes used to compile generated java files 1592 # without any other deps. This is not used in aosp. 1593 if out.endswith("_placeholder.srcjar"): 1594 continue 1595 # fix target.output directory to match #include statements. 1596 outputs.add(re.sub('^jni_headers/', '', out)) 1597 return outputs 1598 1599 def _sanitize_args(self): 1600 self._update_value_arg('--depfile', self._sanitize_filepath) 1601 self._update_value_arg('--srcjar-path', self._sanitize_filepath) 1602 self._update_value_arg('--header-path', self._sanitize_filepath) 1603 self._update_value_arg('--placeholder-srcjar-path', self._sanitize_filepath, False) 1604 self._delete_value_arg('--depfile', False) 1605 self._set_value_arg('--java-sources-file', '$(genDir)/java.sources') 1606 if not self.is_test_target: 1607 # Only jarjar platform code 1608 self._append_arg('--package-prefix', 'android.net.connectivity') 1609 super()._sanitize_args() 1610 1611 def get_cmd(self): 1612 # jni_registration_generator.py doesn't work with python2 1613 cmd = "python3 " + super().get_base_cmd() 1614 # Path in the original sources file does not work in genrule. 1615 # So creating sources file in cmd based on the srcs of this target. 1616 # Adding ../$(current_dir)/ to the head because jni_registration_generator.py uses the files 1617 # whose path startswith(..) 1618 commands = ["current_dir=`basename \\\`pwd\\\``;", 1619 "for f in $(in);", 1620 "do", 1621 "echo \\\"../$$current_dir/$$f\\\" >> $(genDir)/java.sources;", 1622 "done;", 1623 cmd] 1624 1625 return self.get_pre_cmd() + NEWLINE.join(commands) 1626 1627 def get_tool_files(self): 1628 tool_files = super().get_tool_files() 1629 # TODO: Remove once https://chromium-review.googlesource.com/c/chromium/src/+/5370266 has made 1630 # its way to AOSP 1631 # Files not specified in anywhere but jni_generator.py imports this file 1632 tool_files.add('third_party/jni_zero/codegen/header_common.py') 1633 tool_files.add('third_party/jni_zero/codegen/placeholder_java_type.py') 1634 return tool_files 1635 1636class JavaJniRegistrationGeneratorSanitizer(JniRegistrationGeneratorSanitizer): 1637 def get_name(self): 1638 name = super().get_name() + "__java" 1639 return name 1640 1641 def get_outputs(self): 1642 return [out for out in super().get_outputs() if 1643 out.endswith(".srcjar")] 1644 1645 def get_deps(self): 1646 return {} 1647 1648class VersionSanitizer(BaseActionSanitizer): 1649 def _sanitize_args(self): 1650 self._set_value_arg('-o', '$(out)') 1651 # args for the version.py contain file path without leading --arg key. So apply sanitize 1652 # function for all the args. 1653 self._update_all_args(self._sanitize_filepath_with_location_tag) 1654 self._update_list_arg('-e', self._sanitize_eval) 1655 super()._sanitize_args() 1656 1657 def _sanitize_eval(self, eval_arg): 1658 return "'%s'" % eval_arg.replace("\'", "\\\"") 1659 1660 def get_tool_files(self): 1661 tool_files = super().get_tool_files() 1662 # android_chrome_version.py is not specified in anywhere but version.py imports this file 1663 tool_files.add('build/util/android_chrome_version.py') 1664 return tool_files 1665 1666class JavaCppEnumSanitizer(BaseActionSanitizer): 1667 def _sanitize_args(self): 1668 self._update_all_args(self._sanitize_filepath_with_location_tag) 1669 self._set_value_arg('--srcjar', '$(out)') 1670 super()._sanitize_args() 1671 1672class MakeDafsaSanitizer(BaseActionSanitizer): 1673 def is_header_generated(self): 1674 # This script generates .cc files but they are #included by other sources 1675 # (e.g. registry_controlled_domain.cc) 1676 return True 1677 1678class JavaCppFeatureSanitizer(BaseActionSanitizer): 1679 def _sanitize_args(self): 1680 self._update_all_args(self._sanitize_filepath_with_location_tag) 1681 self._set_value_arg('--srcjar', '$(out)') 1682 super()._sanitize_args() 1683 1684class JavaCppStringSanitizer(BaseActionSanitizer): 1685 def _sanitize_args(self): 1686 self._update_all_args(self._sanitize_filepath_with_location_tag) 1687 self._set_value_arg('--srcjar', '$(out)') 1688 super()._sanitize_args() 1689 1690class WriteNativeLibrariesJavaSanitizer(BaseActionSanitizer): 1691 def _sanitize_args(self): 1692 self._set_value_arg('--output', '$(out)') 1693 super()._sanitize_args() 1694 1695 1696class ProtocJavaSanitizer(BaseActionSanitizer): 1697 def __init__(self, target, arch, gn): 1698 super().__init__(target, arch) 1699 self._protoc = get_protoc_module_name(gn) 1700 1701 def _sanitize_proto_path(self, arg): 1702 arg = self._sanitize_filepath(arg) 1703 return tree_path + '/' + arg 1704 1705 def _sanitize_args(self): 1706 super()._sanitize_args() 1707 self._delete_value_arg('--depfile') 1708 self._set_value_arg('--protoc', '$(location %s)' % self._protoc) 1709 self._update_value_arg('--proto-path', self._sanitize_proto_path) 1710 self._set_value_arg('--srcjar', '$(out)') 1711 self._update_arg_at(-1, self._sanitize_filepath_with_location_tag) 1712 1713 def get_tools(self): 1714 tools = super().get_tools() 1715 tools.add(self._protoc) 1716 return tools 1717 1718 1719def get_action_sanitizer(gn, target, type, arch, is_test_target): 1720 if target.script == "//build/write_buildflag_header.py": 1721 return WriteBuildFlagHeaderSanitizer(target, arch) 1722 elif target.script == "//base/write_build_date_header.py": 1723 return WriteBuildDateHeaderSanitizer(target, arch) 1724 elif target.script == "//build/util/version.py": 1725 return VersionSanitizer(target, arch) 1726 elif target.script == "//build/android/gyp/java_cpp_enum.py": 1727 return JavaCppEnumSanitizer(target, arch) 1728 elif target.script == "//net/tools/dafsa/make_dafsa.py": 1729 return MakeDafsaSanitizer(target, arch) 1730 elif target.script == '//build/android/gyp/java_cpp_features.py': 1731 return JavaCppFeatureSanitizer(target, arch) 1732 elif target.script == '//build/android/gyp/java_cpp_strings.py': 1733 return JavaCppStringSanitizer(target, arch) 1734 elif target.script == '//build/android/gyp/write_native_libraries_java.py': 1735 return WriteNativeLibrariesJavaSanitizer(target, arch) 1736 elif target.script == '//build/gn_run_binary.py': 1737 return GnRunBinarySanitizer(target, arch) 1738 elif target.script == '//build/protoc_java.py': 1739 return ProtocJavaSanitizer(target, arch, gn) 1740 elif target.script == '//third_party/jni_zero/jni_zero.py': 1741 if target.args[0] == 'generate-final': 1742 if type == 'java_genrule': 1743 # Fill up the sources of the target for JniRegistrationGenerator 1744 # actions with all the java sources found under targets of type 1745 # `generate_jni`. Note 1: Only do this for the java part in order to 1746 # generate a complete GEN_JNI. The C++ part MUST only include java 1747 # source files that are listed explicitly in `generate_jni` targets 1748 # in the transitive dependency, this is handled inside the action 1749 # sanitizer itself (See `get_srcs`). Adding java sources that are not 1750 # listed to the C++ version of JniRegistrationGenerator will result 1751 # in undefined symbols as the C++ part generates declarations that 1752 # would have no definitions. Note 2: This is only done for the 1753 # testing targets because their JniRegistration is not complete, 1754 # Chromium generates Jni files for testing targets implicitly (See 1755 # https://source.chromium.org/chromium/chromium/src/+/main:testing 1756 # /test.gni;l=422;bpv=1;bpt=0;drc 1757 # =02820c1b362c3a00f426d7c4eab18703d89cda03) to avoid having to 1758 # replicate the same setup, just fill up the java JniRegistration 1759 # with all java sources found under `generate_jni` targets and fill 1760 # the C++ version with the exact files. 1761 if is_test_target: 1762 target.sources.update(gn.jni_java_sources) 1763 return JavaJniRegistrationGeneratorSanitizer(target, arch, is_test_target) 1764 else: 1765 return JniRegistrationGeneratorSanitizer(target, arch, is_test_target) 1766 else: 1767 if type == 'cc_genrule': 1768 return JniGeneratorSanitizer(target, arch, is_test_target) 1769 else: 1770 return JavaJniGeneratorSanitizer(target, arch, is_test_target) 1771 else: 1772 raise Exception('Unsupported action %s from %s' % (target.script, target.name)) 1773 1774def create_action_foreach_modules(blueprint, gn, target, is_test_target): 1775 """ The following assumes that rebase_path exists in the args. 1776 The args of an action_foreach contains hints about which output files are generated 1777 by which source files. 1778 This is copied directly from the args 1779 "gen/net/base/registry_controlled_domains/{{source_name_part}}-reversed-inc.cc" 1780 So each source file will generate an output whose name is the {source_name-reversed-inc.cc} 1781 """ 1782 new_args = [] 1783 for i, src in enumerate(sorted(target.sources)): 1784 # don't add script arg for the first source -- create_action_module 1785 # already does this. 1786 if i != 0: 1787 new_args.append('&&') 1788 new_args.append('python3 $(location %s)' % 1789 gn_utils.label_to_path(target.script)) 1790 for arg in target.args: 1791 if '{{source}}' in arg: 1792 new_args.append('$(location %s)' % (gn_utils.label_to_path(src))) 1793 elif '{{source_name_part}}' in arg: 1794 source_name_part = src.split("/")[-1] # Get the file name only 1795 source_name_part = source_name_part.split(".")[0] # Remove the extension (Ex: .cc) 1796 file_name = arg.replace('{{source_name_part}}', source_name_part).split("/")[-1] 1797 # file_name represent the output file name. But we need the whole path 1798 # This can be found from target.outputs. 1799 for out in target.outputs: 1800 if out.endswith(file_name): 1801 new_args.append('$(location %s)' % out) 1802 1803 for file in (target.sources | target.inputs): 1804 if file.endswith(file_name): 1805 new_args.append('$(location %s)' % gn_utils.label_to_path(file)) 1806 else: 1807 new_args.append(arg) 1808 1809 target.args = new_args 1810 return create_action_module(blueprint, gn, target, 'cc_genrule', is_test_target) 1811 1812def create_action_module_internal(gn, target, type, is_test_target, blueprint, arch=None): 1813 if target.script == '//build/android/gyp/gcc_preprocess.py': 1814 return create_gcc_preprocess_modules(blueprint, target) 1815 sanitizer = get_action_sanitizer(gn, target, type, arch, is_test_target) 1816 sanitizer.sanitize() 1817 1818 module = Module(type, sanitizer.get_name(), target.name) 1819 module.cmd = sanitizer.get_cmd() 1820 module.out = sanitizer.get_outputs() 1821 if sanitizer.is_header_generated(): 1822 module.genrule_headers.add(module.name) 1823 module.srcs = sanitizer.get_srcs() 1824 module.tool_files = sanitizer.get_tool_files() 1825 module.tools = sanitizer.get_tools() 1826 target.deps = sanitizer.get_deps() 1827 1828 return module 1829 1830def get_cmd_condition(arch): 1831 ''' 1832 :param arch: archtecture name e.g. android_x86_64, android_arm64 1833 :return: condition that can be used in cc_genrule cmd to switch the behavior based on arch 1834 ''' 1835 if arch == "android_x86_64": 1836 return "( $$CC_ARCH == 'x86_64' && $$CC_OS == 'android' )" 1837 elif arch == "android_x86": 1838 return "( $$CC_ARCH == 'x86' && $$CC_OS == 'android' )" 1839 elif arch == "android_arm": 1840 return "( $$CC_ARCH == 'arm' && $$CC_OS == 'android' )" 1841 elif arch == "android_arm64": 1842 return "( $$CC_ARCH == 'arm64' && $$CC_OS == 'android' )" 1843 elif arch == "android_riscv64": 1844 return "( $$CC_ARCH == 'riscv64' && $$CC_OS == 'android' )" 1845 elif arch == "host": 1846 return "$$CC_OS != 'android'" 1847 else: 1848 raise Exception(f'Unknown architecture type {arch}') 1849 1850def merge_cmd(modules, genrule_type): 1851 ''' 1852 :param modules: dictionary whose key is arch name and value is module 1853 :param genrule_type: cc_genrule or java_genrule 1854 :return: merged command or common command if all the archs have the same command. 1855 ''' 1856 commands = list({module.cmd for module in modules.values()}) 1857 if len(commands) == 1: 1858 # If all the archs have the same command, return the command 1859 return commands[0] 1860 1861 if genrule_type != 'cc_genrule': 1862 raise Exception(f'{genrule_type} can not have different cmd between archs') 1863 1864 merged_cmd = [] 1865 for arch, module in sorted(modules.items()): 1866 merged_cmd.append(f'if [[ {get_cmd_condition(arch)} ]];') 1867 merged_cmd.append('then') 1868 merged_cmd.append(module.cmd + ';') 1869 merged_cmd.append('fi;') 1870 return NEWLINE.join(merged_cmd) 1871 1872def merge_modules(modules, genrule_type): 1873 ''' 1874 :param modules: dictionary whose key is arch name and value is module 1875 :param genrule_type: cc_genrule or java_genrule 1876 :return: merged module of input modules 1877 ''' 1878 merged_module = list(modules.values())[0] 1879 1880 # Following attributes must be the same between archs 1881 for key in ('genrule_headers', 'srcs', 'tool_files'): 1882 if any([getattr(merged_module, key) != getattr(module, key) for module in modules.values()]): 1883 raise Exception(f'{merged_module.name} has different values for {key} between archs') 1884 1885 merged_module.cmd = merge_cmd(modules, genrule_type) 1886 return merged_module 1887 1888def create_action_module(blueprint, gn, target, genrule_type, is_test_target): 1889 ''' 1890 Create module for action target and add to the blueprint. If target has arch specific attributes 1891 this function merge them and create a single module. 1892 :param blueprint: 1893 :param target: target which is converted to the module. 1894 :param genrule_type: cc_genrule or java_genrule 1895 :return: created module 1896 ''' 1897 # TODO: Handle this target correctly, this target generates java_genrule but this target has 1898 # different value for cpu-family arg between archs 1899 if re.match('//build/android:native_libraries_gen(__testing)?$', target.name): 1900 module = create_action_module_internal(gn, target, genrule_type, 1901 is_test_target, blueprint, 1902 target.arch['android_arm']) 1903 blueprint.add_module(module) 1904 return module 1905 1906 modules = {arch_name: create_action_module_internal(gn, target, genrule_type, 1907 is_test_target, blueprint, arch) 1908 for arch_name, arch in target.get_archs().items()} 1909 module = merge_modules(modules, genrule_type) 1910 blueprint.add_module(module) 1911 return module 1912 1913 1914def _get_cflags(cflags, defines): 1915 cflags = {flag for flag in cflags if flag in cflag_allowlist} 1916 # Consider proper allowlist or denylist if needed 1917 cflags |= set("-D%s" % define.replace("\"", "\\\"") for define in defines) 1918 return cflags 1919 1920def _set_linker_script(module, libs): 1921 for lib in libs: 1922 if lib.endswith(".lds"): 1923 module.ldflags.add(get_linker_script_ldflag(gn_utils.label_to_path(lib))) 1924 1925def set_module_flags(module, module_type, cflags, defines, ldflags, libs): 1926 module.cflags.update(_get_cflags(cflags, defines)) 1927 module.ldflags.update({flag for flag in ldflags 1928 if flag in ldflag_allowlist or flag.startswith("-Wl,-wrap,")}) 1929 _set_linker_script(module, libs) 1930 # TODO: implement proper cflag parsing. 1931 for flag in cflags: 1932 if '-std=' in flag: 1933 module.cpp_std = flag[len('-std='):] 1934 if '-fexceptions' in flag: 1935 module.cppflags.add('-fexceptions') 1936 1937def set_module_include_dirs(module, cflags, include_dirs): 1938 for flag in cflags: 1939 if '-isystem' in flag: 1940 module.include_dirs.add(f"external/cronet/{flag[len('-isystem../../'):]}") 1941 1942 # Adding include_dirs is necessary due to source_sets / filegroups 1943 # which do not properly propagate include directories. 1944 # Filter any directory inside //out as a) this directory does not exist for 1945 # aosp / soong builds and b) the include directory should already be 1946 # configured via library dependency. 1947 # Note: include_dirs is used instead of local_include_dirs as an Android.bp 1948 # can't access other directories outside of its current directory. This 1949 # is worked around by using include_dirs. 1950 module.include_dirs.update([f"external/cronet/{gn_utils.label_to_path(d)}" 1951 for d in include_dirs if not d.startswith('//out')]) 1952 # Remove prohibited include directories 1953 module.include_dirs = [d for d in module.include_dirs 1954 if d not in include_dirs_denylist] 1955 1956 1957def create_modules_from_target(blueprint, gn, gn_target_name, parent_gn_type, is_test_target): 1958 """Generate module(s) for a given GN target. 1959 1960 Given a GN target name, generate one or more corresponding modules into a 1961 blueprint. The only case when this generates >1 module is proto libraries. 1962 1963 Args: 1964 blueprint: Blueprint instance which is being generated. 1965 gn: gn_utils.GnParser object. 1966 gn_target_name: GN target for module generation. 1967 parent_gn_type: GN type of the parent node. 1968 """ 1969 bp_module_name = label_to_module_name(gn_target_name) 1970 target = gn.get_target(gn_target_name) 1971 1972 # Append __java suffix to actions reachable from java_library. This is necessary 1973 # to differentiate them from cc actions. 1974 # This means that a GN action of name X will be translated to two different modules of names 1975 # X and X__java(only if X is reachable from a java target). 1976 if target.type == "action" and parent_gn_type == "java_library": 1977 bp_module_name += "__java" 1978 1979 if target.type in ["rust_library", "rust_proc_macro"]: 1980 # "lib{crate_name}" must be a prefix of the module name, this is a Soong 1981 # restriction. 1982 # https://cs.android.com/android/_/android/platform/build/soong/+/31934a55a8a1f9e4d56d68810f4a646f12ab6eb5:rust/library.go;l=724;drc=fdec8723d574daf54b956cc0f6dc879087da70a6;bpv=0;bpt=0 1983 if len(target.crate_name) > 50: 1984 # It is unreasonable to have such a crate name, this usually happens when 1985 # the name of the crate is equal to the target path, this is the default 1986 # for Chromium when a crate_name is not declared. Since we are prepending 1987 # the crate name to the name, let's cut it short. 1988 # There is a length limit on the module name because module names 1989 # are used to create files and there is a limit on file names. 1990 target.crate_name = target.crate_name[:50] + "__TRIMMED" 1991 bp_module_name = f"lib{target.crate_name}_{bp_module_name}" 1992 1993 if parent_gn_type == "static_library": 1994 # CC modules must depend on a different type of modules that are 1995 # rust_ffi_static instead of rust_library_rlib 1996 bp_module_name += "__FFI" 1997 1998 # There is a limit on the length of the module name as the output depends 1999 # on that. 2000 if len(bp_module_name) > 128: 2001 bp_module_name = bp_module_name[:128] + "__TRIMMED" 2002 if bp_module_name in blueprint.modules: 2003 return blueprint.modules[bp_module_name] 2004 2005 log.info('create modules for %s (%s)', target.name, target.type) 2006 2007 if target.type == 'executable': 2008 if target.testonly: 2009 module_type = 'cc_test' 2010 else: 2011 # Can be used for both host and device targets. 2012 module_type = 'cc_binary' 2013 module = Module(module_type, bp_module_name, gn_target_name) 2014 elif target.type == 'rust_executable': 2015 module = Module("rust_binary", bp_module_name, gn_target_name) 2016 elif target.type == "rust_library": 2017 _type = "rust_library_rlib" 2018 if parent_gn_type == "static_library": 2019 # CPP modules must depend on rust_ffi_static as this generates the 2020 # necessary static library that can be linked. 2021 _type = "rust_ffi_static" 2022 # Chromium only uses rlibs. 2023 module = Module(_type, bp_module_name, gn_target_name) 2024 elif target.type == "rust_proc_macro": 2025 module = Module("rust_proc_macro", bp_module_name, gn_target_name) 2026 elif target.type in ['static_library', 'source_set']: 2027 module = Module('cc_library_static', bp_module_name, gn_target_name) 2028 elif target.type == 'shared_library': 2029 module = Module('cc_library_shared', bp_module_name, gn_target_name) 2030 elif target.type == 'group': 2031 # "group" targets are resolved recursively by gn_utils.get_target(). 2032 # There's nothing we need to do at this level for them. 2033 return None 2034 elif target.type == 'proto_library': 2035 module = create_proto_modules(blueprint, gn, target) 2036 if module is None: 2037 return None 2038 elif target.type == 'action': 2039 module = create_action_module(blueprint, gn, target, 'java_genrule' if parent_gn_type == "java_library" else 'cc_genrule', is_test_target) 2040 elif target.type == 'action_foreach': 2041 if target.script == "//third_party/rust/cxx/chromium_integration/run_cxxbridge.py": 2042 module = create_rust_cxx_module(blueprint, target) 2043 else: 2044 module = create_action_foreach_modules(blueprint, gn, target, is_test_target) 2045 elif target.type == 'copy': 2046 # TODO: careful now! copy targets are not supported yet, but this will stop 2047 # traversing the dependency tree. For //base:base, this is not a big 2048 # problem as libicu contains the only copy target which happens to be a 2049 # leaf node. 2050 return None 2051 elif target.type == 'java_library': 2052 if target.jar_path: 2053 module = Module('java_import', bp_module_name, gn_target_name) 2054 module.jars.add(target.jar_path) 2055 else: 2056 module = Module('java_library', bp_module_name, gn_target_name) 2057 # Don't remove GEN_JNI from those modules as they have the real GEN_JNI that we want to include 2058 if gn_target_name not in ['//components/cronet/android:cronet_jni_registration_java', 2059 '//components/cronet/android:cronet_jni_registration_java__testing', 2060 '//components/cronet/android:cronet_tests_jni_registration_java__testing']: 2061 module.jarjar_rules = REMOVE_GEN_JNI_JARJAR_RULES_FILE 2062 module.min_sdk_version = 30 2063 module.apex_available = [tethering_apex] 2064 if is_test_target: 2065 module.sdk_version = target.sdk_version 2066 else: 2067 module.defaults.add(java_framework_defaults_module) 2068 else: 2069 raise Exception('Unknown target %s (%s)' % (target.name, target.type)) 2070 2071 blueprint.add_module(module) 2072 if target.type not in ['action', 'action_foreach']: 2073 # Actions should get their srcs from their corresponding ActionSanitizer as actionSanitizer 2074 # filters srcs differently according to the type of the action. 2075 module.srcs.update(gn_utils.label_to_path(src) 2076 for src in target.sources if is_supported_source_file(src)) 2077 2078 # Add arch-specific properties 2079 for arch_name, arch in target.get_archs().items(): 2080 module.target[arch_name].srcs.update(gn_utils.label_to_path(src) 2081 for src in arch.sources if is_supported_source_file(src)) 2082 2083 module.rtti = target.rtti 2084 2085 if target.type in gn_utils.LINKER_UNIT_TYPES: 2086 set_module_flags(module, module.type, target.cflags, target.defines, target.ldflags, target.libs) 2087 set_module_include_dirs(module, target.cflags, target.include_dirs) 2088 # TODO: set_module_xxx is confusing, apply similar function to module and target in better way. 2089 for arch_name, arch in target.get_archs().items(): 2090 # TODO(aymanm): Make libs arch-specific. 2091 set_module_flags(module.target[arch_name], module.type, 2092 arch.cflags, arch.defines, arch.ldflags, []) 2093 # -Xclang -target-feature -Xclang +mte are used to enable MTE (Memory Tagging Extensions). 2094 # Flags which does not start with '-' could not be in the cflags so enabling MTE by 2095 # -march and -mcpu Feature Modifiers. MTE is only available on arm64. This is needed for 2096 # building //base/allocator/partition_allocator:partition_alloc for arm64. 2097 if '+mte' in arch.cflags and arch_name == 'android_arm64': 2098 module.target[arch_name].cflags.add('-march=armv8-a+memtag') 2099 set_module_include_dirs(module.target[arch_name], arch.cflags, arch.include_dirs) 2100 2101 if not module.type == "rust_proc_macro": 2102 # rust_proc_macro modules does not support the fields of `host_supported` 2103 # or `device_supported`. In a different world, we would have classes for 2104 # each different module that specifies what it can support to avoid 2105 # those kind of conditions. 2106 # 2107 # See go/android.bp for additional information. 2108 module.host_supported = target.host_supported() 2109 module.device_supported = target.device_supported() 2110 2111 module.gn_type = target.type 2112 module.build_file_path = target.build_file_path 2113 # Chromium does not use visibility at all, in order to avoid visibility issues 2114 # in AOSP. Make every module visible to any module in external/cronet. 2115 module.visibility = {"//external/cronet:__subpackages__"} 2116 2117 if module.type.startswith("rust"): 2118 module.crate_name = target.crate_name 2119 module.crate_root = gn_utils.label_to_path(target.crate_root) 2120 module.min_sdk_version = 30 2121 module.apex_available = [tethering_apex] 2122 for arch_name, arch in target.get_archs().items(): 2123 _set_rust_flags(module.target[arch_name], arch.rust_flags, arch_name) 2124 2125 if module.is_genrule(): 2126 module.apex_available.add(tethering_apex) 2127 2128 if module.type == "java_library": 2129 if gn_utils.contains_aidl(target.sources): 2130 # frameworks/base/core/java includes the source files that are used to compile framework.aidl. 2131 # framework.aidl is added implicitly as a dependency to every AIDL GN action, this can be 2132 # identified by third_party/android_sdk/public/platforms/android-34/framework.aidl. 2133 module.aidl["include_dirs"] = {"frameworks/base/core/java/"} 2134 module.aidl["local_include_dirs"] = target.local_aidl_includes 2135 2136 if (module.is_compiled() and not module.type.startswith("java") 2137 and not module.type.startswith("rust")): 2138 # Don't try to inject library/source dependencies into genrules or 2139 # filegroups because they are not compiled in the traditional sense. 2140 module.defaults = [cc_defaults_module] 2141 for lib in target.libs: 2142 # Generally library names should be mangled as 'libXXX', unless they 2143 # are HAL libraries (e.g., [email protected]) or AIDL c++ / NDK 2144 # libraries (e.g. "android.hardware.power.stats-V1-cpp") 2145 android_lib = lib if '@' in lib or "-cpp" in lib or "-ndk" in lib \ 2146 else 'lib' + lib 2147 if lib in shared_library_allowlist: 2148 module.add_android_shared_lib(android_lib) 2149 2150 # If the module is a static library, export all the generated headers. 2151 if module.type == 'cc_library_static': 2152 module.export_generated_headers = module.generated_headers 2153 2154 if module.name in ['cronet_aml_components_cronet_android_cronet', 2155 'cronet_aml_components_cronet_android_cronet' + gn_utils.TESTING_SUFFIX]: 2156 if target.output_name is None: 2157 raise Exception('Failed to get output_name for libcronet name') 2158 # .so file name needs to match with CronetLibraryLoader.java (e.g. libcronet.109.0.5386.0.so) 2159 # So setting the output name based on the output_name from the desc.json 2160 module.stem = 'libmainline' + target.output_name 2161 elif module.is_test() and module.type == 'cc_library_shared': 2162 if target.output_name: 2163 # If we have an output name already declared, use it. 2164 module.stem = 'lib' + target.output_name 2165 else: 2166 # Tests output should be a shared library in the format of 'lib[module_name]' 2167 module.stem = 'lib' + target.get_target_name()[ 2168 :target.get_target_name().find(gn_utils.TESTING_SUFFIX)] 2169 2170 # dep_name is an unmangled GN target name (e.g. //foo:bar(toolchain)). 2171 all_deps = [(dep_name, 'common') for dep_name in target.proto_deps] 2172 for arch_name, arch in target.arch.items(): 2173 all_deps += [(dep_name, arch_name) for dep_name in arch.deps] 2174 2175 # Sort deps before iteration to make result deterministic. 2176 for (dep_name, arch_name) in sorted(all_deps): 2177 module_target = module.target[arch_name] if arch_name != 'common' else module 2178 # |builtin_deps| override GN deps with Android-specific ones. See the 2179 # config in the top of this file. 2180 if dep_name in builtin_deps: 2181 builtin_deps[dep_name](module, arch_name) 2182 continue 2183 2184 # This is like the builtin_deps with always_disable except that it matches 2185 # a string. 2186 if "_build_script" in dep_name: 2187 continue 2188 2189 dep_module = create_modules_from_target(blueprint, gn, dep_name, target.type, is_test_target) 2190 2191 if dep_module is None: 2192 continue 2193 2194 # TODO: Proper dependency check for genrule. 2195 # Currently, only propagating genrule dependencies. 2196 # Also, currently, all the dependencies are propagated upwards. 2197 # in gn, public_deps should be propagated but deps should not. 2198 # Not sure this information is available in the desc.json. 2199 # Following rule works for adding android_runtime_jni_headers to base:base. 2200 # If this doesn't work for other target, hardcoding for specific target 2201 # might be better. 2202 if module.is_genrule() and dep_module.is_genrule(): 2203 if module_target.gn_type != "proto_library": 2204 # proto_library are treated differently because each proto action 2205 # is split into two different targets, a cpp target and a header target. 2206 # the cpp target is used as the entry point to the proto action, hence 2207 # it should not be propagated as a genrule header because it generates 2208 # cpp files only. 2209 module_target.genrule_headers.add(dep_module.name) 2210 module_target.genrule_headers.update(dep_module.genrule_headers) 2211 2212 # For filegroups, and genrule, recurse but don't apply the 2213 # deps. 2214 if not module.is_compiled() or module.is_genrule(): 2215 continue 2216 2217 # Drop compiled modules that doesn't provide any benefit. This is mostly 2218 # applicable to source_sets when converted to cc_static_library, sometimes 2219 # the source set only has header files which are dropped so the module becomes empty. 2220 # is_compiled is there to prevent dropping of genrules. 2221 if dep_module.is_compiled() and not dep_module.has_input_files(): 2222 continue 2223 2224 if dep_module.type == 'cc_library_shared': 2225 module_target.shared_libs.add(dep_module.name) 2226 elif dep_module.type == 'cc_library_static': 2227 if module.type in ['cc_library_shared', 'cc_binary']: 2228 module_target.whole_static_libs.add(dep_module.name) 2229 elif module.type == 'cc_library_static': 2230 module_target.generated_headers.update(dep_module.generated_headers) 2231 module_target.shared_libs.update(dep_module.shared_libs) 2232 module_target.header_libs.update(dep_module.header_libs) 2233 elif module.type in ["rust_library_rlib", "rust_binary"]: 2234 module_target.static_libs.add(dep_module.name) 2235 else: 2236 raise Exception(f"Trying to add an unknown type {dep_module.type} to a type of {module.type}") 2237 elif dep_module.type == "rust_library_rlib": 2238 module_target.rustlibs.add(dep_module.name) 2239 elif dep_module.type == "rust_ffi_static": 2240 assert module.type == "cc_library_static", "Only static CC libraries can depend on rust_ffi_static" 2241 # CPP libraries must not depend on rust_library_rlib, they must depend 2242 # on rust_ffi_rlib as per aosp/3094614 and go/android-made-to-order-rust-staticlibs. 2243 module_target.static_libs.add(dep_module.name) 2244 elif dep_module.type == "rust_proc_macro": 2245 module_target.proc_macros.add(dep_module.name) 2246 elif dep_module.type == 'cc_genrule': 2247 module_target.generated_headers.update(dep_module.genrule_headers) 2248 module_target.srcs.update(dep_module.genrule_srcs) 2249 module_target.shared_libs.update(dep_module.genrule_shared_libs) 2250 module_target.header_libs.update(dep_module.genrule_header_libs) 2251 elif dep_module.type in ['java_library', 'java_import']: 2252 # A module depending on a module with system_current sdk version should also compile against 2253 # the system sdk. This is because a module's SDK API surface should be >= its deps SDK API surface. 2254 # And system_current has a larger API surface than current or module_current. 2255 if dep_module.sdk_version == 'system_current': 2256 module.sdk_version = 'system_current' 2257 if module.type not in ["cc_library_static"]: 2258 # This is needed to go around the case where `url` component depends 2259 # on `url_java`. 2260 # TODO(aymanm): Remove the if condition once crrev/4902547 has been imported downstream 2261 module_target.static_libs.add(dep_module.name) 2262 elif dep_module.type in ['genrule', 'java_genrule']: 2263 module_target.srcs.add(":" + dep_module.name) 2264 else: 2265 raise Exception('Unsupported arch-specific dependency %s of target %s with type %s' % 2266 (dep_module.name, target.name, dep_module.type)) 2267 return module 2268 2269def turn_off_allocator_shim_for_musl(module): 2270 allocation_shim = "base/allocator/partition_allocator/shim/allocator_shim.cc" 2271 allocator_shim_files = { 2272 allocation_shim, 2273 "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_glibc.cc", 2274 } 2275 module.srcs -= allocator_shim_files 2276 for arch in module.target.values(): 2277 arch.srcs -= allocator_shim_files 2278 module.target['android'].srcs.add(allocation_shim) 2279 if gn_utils.TESTING_SUFFIX in module.name: 2280 # allocator_shim_default_dispatch_to_glibc is only added to the __testing version of base 2281 # since base_base__testing is compiled for host. When compiling for host. Soong compiles 2282 # using glibc or musl(experimental). We currently only support compiling for glibc. 2283 module.target['glibc'].srcs.update(allocator_shim_files) 2284 else: 2285 # allocator_shim_default_dispatch_to_glibc does not exist in the prod version of base 2286 # `base_base` since this only compiles for android and bionic is used. Bionic is the equivalent 2287 # of glibc but for android. 2288 module.target['glibc'].srcs.add(allocation_shim) 2289 2290def create_cc_defaults_module(): 2291 defaults = Module('cc_defaults', cc_defaults_module, '//gn:default_deps') 2292 defaults.cflags = [ 2293 '-DGOOGLE_PROTOBUF_NO_RTTI', 2294 '-DBORINGSSL_SHARED_LIBRARY', 2295 '-Wno-error=return-type', 2296 '-Wno-non-virtual-dtor', 2297 '-Wno-macro-redefined', 2298 '-Wno-missing-field-initializers', 2299 '-Wno-sign-compare', 2300 '-Wno-sign-promo', 2301 '-Wno-unused-parameter', 2302 '-Wno-null-pointer-subtraction', # Needed to libevent 2303 '-Wno-ambiguous-reversed-operator', # needed for icui18n 2304 '-Wno-unreachable-code-loop-increment', # needed for icui18n 2305 '-fPIC', 2306 '-Wno-c++11-narrowing', 2307 # Needed for address_space_randomization.h on riscv 2308 # Can be removed after 125.0.6375.0 is imported 2309 '-Wno-invalid-constexpr', 2310 # b/330508686 disable coverage profiling for files or function in this list. 2311 '-fprofile-list=external/cronet/exclude_coverage.list', 2312 ] 2313 defaults.build_file_path = "" 2314 defaults.include_build_directory = False 2315 defaults.c_std = 'gnu11' 2316 # Chromium builds do not add a dependency for headers found inside the 2317 # sysroot, so they are added globally via defaults. 2318 defaults.target['android'].header_libs = [ 2319 'jni_headers', 2320 ] 2321 defaults.target['android'].shared_libs = [ 2322 'libmediandk' 2323 ] 2324 defaults.target['host'].cflags = [ 2325 # -DANDROID is added by default but target.defines contain -DANDROID if 2326 # it's required. So adding -UANDROID to cancel default -DANDROID if it's 2327 # not specified. 2328 # Note: -DANDROID is not consistently applied across the chromium code 2329 # base, so it is removed unconditionally for host targets. 2330 '-UANDROID', 2331 ] 2332 defaults.stl = 'none' 2333 defaults.cpp_std = 'c++17' 2334 defaults.min_sdk_version = 29 2335 defaults.apex_available.add(tethering_apex) 2336 return defaults 2337 2338def create_blueprint_for_targets(gn, targets, test_targets): 2339 """Generate a blueprint for a list of GN targets.""" 2340 blueprint = Blueprint() 2341 2342 # Default settings used by all modules. 2343 blueprint.add_module(create_cc_defaults_module()) 2344 2345 for target in targets: 2346 module = create_modules_from_target(blueprint, gn, target, parent_gn_type=None, is_test_target=False) 2347 if module: 2348 module.visibility.update(root_modules_visibility) 2349 2350 for test_target in test_targets: 2351 module = create_modules_from_target(blueprint, gn, test_target + gn_utils.TESTING_SUFFIX, parent_gn_type=None, is_test_target=True) 2352 if module: 2353 module.visibility.update(root_modules_visibility) 2354 2355 # Merge in additional hardcoded arguments. 2356 for module in blueprint.modules.values(): 2357 for key, add_val in additional_args.get(module.name, []): 2358 curr = getattr(module, key) 2359 if add_val and isinstance(add_val, set) and isinstance(curr, set): 2360 curr.update(add_val) 2361 elif isinstance(add_val, str) and (not curr or isinstance(curr, str)): 2362 setattr(module, key, add_val) 2363 elif isinstance(add_val, bool) and (not curr or isinstance(curr, bool)): 2364 setattr(module, key, add_val) 2365 elif isinstance(add_val, dict) and isinstance(curr, dict): 2366 curr.update(add_val) 2367 elif isinstance(add_val[1], dict) and isinstance(curr[add_val[0]], Module.Target): 2368 curr[add_val[0]].__dict__.update(add_val[1]) 2369 elif isinstance(curr, dict): 2370 curr[add_val[0]] = add_val[1] 2371 else: 2372 raise Exception('Unimplemented type %r of additional_args: %r' % (type(add_val), key)) 2373 2374 return blueprint 2375 2376def _rebase_file(filepath: str, blueprint_path: str) -> str | None: 2377 """ 2378 Rebases a single file, this method delegates to _rebase_files 2379 2380 :param filepath: a single string representing filepath. 2381 :param blueprint_path: Path for which the srcs will be rebased relative to. 2382 :returns The rebased filepaths or None. 2383 """ 2384 rebased_file = _rebase_files([filepath], blueprint_path) 2385 if rebased_file: 2386 return list(rebased_file)[0] 2387 return None 2388 2389def _rebase_files(filepaths, parent_prefix): 2390 """ 2391 Rebase a list of filepaths according to the provided path. This assumes 2392 that the |filepaths| are subdirectories of the |parent|. 2393 If the assumption is violated then None is returned. 2394 2395 Note: filepath can be references to other modules (eg: ":module"), those 2396 are added as-is without any translation. 2397 2398 :param filepaths: Collection of strings representing filepaths. 2399 :param parent_prefix: Path for which the srcs will be rebased relative to. 2400 :returns The rebased filepaths or None. 2401 """ 2402 if not parent_prefix: 2403 return filepaths 2404 2405 rebased_srcs = set() 2406 for src in filepaths: 2407 if src.startswith(":"): 2408 # This is a reference to another Android.bp module, add as-is. 2409 rebased_srcs.add(src) 2410 continue 2411 2412 if not src.startswith(parent_prefix): 2413 # This module depends on a source file that is not in its subpackage. 2414 return None 2415 # Remove the BUILD file path to make it relative. 2416 rebased_srcs.add(src[len(parent_prefix) + 1:]) 2417 return rebased_srcs 2418 2419 2420# TODO: Move to Module's class. 2421def _rebase_module(module: Module, blueprint_path: str) -> Module | None: 2422 """ 2423 Rebases the module specified on top of the blueprint_path if possible. 2424 If the rebase operation has failed, None is returned to indicate that the 2425 module should stay as a top-level module. 2426 2427 Currently, there is no support for rebasing genrules and libraries that 2428 breaks the package boundaries. 2429 2430 :returns A new module based on the provided one but rebased or None. 2431 """ 2432 2433 module_copy = copy.deepcopy(module) 2434 # TODO: Find a better way to rebase attribute and verify if all rebase operations 2435 # have succeeded or not. 2436 if module_copy.crate_root: 2437 module_copy.crate_root = _rebase_file(module_copy.crate_root, 2438 blueprint_path) 2439 if module_copy.crate_root is None: 2440 return None 2441 2442 if module_copy.srcs: 2443 module_copy.srcs = _rebase_files(module_copy.srcs, blueprint_path) 2444 if module_copy.srcs is None: 2445 return None 2446 2447 if module_copy.jars: 2448 module_copy.jars = _rebase_files(module_copy.jars, blueprint_path) 2449 if module_copy.jars is None: 2450 return None 2451 2452 for (arch_name, arch) in module_copy.target.items(): 2453 module_copy.target[arch_name].srcs = ( 2454 _rebase_files(module_copy.target[arch_name].srcs, blueprint_path)) 2455 if module_copy.target[arch_name].srcs is None: 2456 return None 2457 2458 return module_copy 2459 2460def _path_to_name(path: str) -> str: 2461 return "external_cronet_%s_license" % (path.replace("/", "_").lower()) 2462 2463def _maybe_create_license_module(path: str) -> Module | None: 2464 """ 2465 Creates a module license if a README.chromium exists in the path provided 2466 otherwise just returns None. 2467 2468 :param path: Path to check for README.chromium 2469 :return: Module or None. 2470 """ 2471 readme_chromium_file = Path(os.path.join(path, "README.chromium")) 2472 if (not readme_chromium_file.exists() or 2473 license_utils.is_ignored_readme_chromium(str(readme_chromium_file))): 2474 return None 2475 2476 license_module = Module("license", _path_to_name(path), "License-Artificial") 2477 license_module.visibility = {":__subpackages__"} 2478 # Assume that a LICENSE file always exist as we run the 2479 # create_android_metadata_license.py script each time we run GN2BP. 2480 license_module.license_text = {"LICENSE"} 2481 metadata = license_utils.parse_chromium_readme_file( 2482 str(readme_chromium_file)) 2483 for license in metadata.get_licenses(): 2484 license_module.license_kinds.add(license_utils.get_license_bp_name(license)) 2485 return license_module 2486 2487 2488def _get_longest_matching_blueprint(current_blueprint_path: str, 2489 all_blueprints: Dict[str, Blueprint]) -> Blueprint | None: 2490 longest_path_matching = None 2491 for (blueprint_path, search_blueprint) in all_blueprints.items(): 2492 if (search_blueprint.get_license_module() 2493 and current_blueprint_path.startswith(blueprint_path) 2494 and (longest_path_matching is None or len(blueprint_path) > len( 2495 longest_path_matching))): 2496 longest_path_matching = blueprint_path 2497 2498 if longest_path_matching: 2499 return all_blueprints[longest_path_matching] 2500 return None 2501 2502def finalize_package_modules(blueprints: Dict[str, Blueprint]): 2503 """ 2504 Adds a package module to every blueprint passed in |blueprints|. A package 2505 module is just a reference to a license module, the approach here is that 2506 the package module will point to the nearest ancestor's license module, the 2507 nearest ancestor could be in the same Android.bp. 2508 2509 :param blueprints: Dictionary of (path, blueprint) to be populated with 2510 """ 2511 2512 for (current_path, blueprint) in blueprints.items(): 2513 if current_path == "": 2514 # Don't add a package module for the top-level Android.bp, this is handled 2515 # manually in Android.extras.bp. 2516 continue 2517 2518 package_module = Module("package", None, "Package-Artificial") 2519 if blueprint.get_license_module(): 2520 package_module.default_applicable_licenses.add( 2521 blueprint.get_license_module().name) 2522 else: # Search for closest ancestor with a license module 2523 ancestor_blueprint = _get_longest_matching_blueprint(current_path, 2524 blueprints) 2525 if ancestor_blueprint: 2526 # We found an ancestor, make a reference to its license module 2527 package_module.default_applicable_licenses.add( 2528 ancestor_blueprint.get_license_module().name) 2529 else: 2530 # No ancestor with a license found, this is most likely a non-third 2531 # license, just point at Chromium's license in Android.extras.bp. 2532 package_module.default_applicable_licenses.add( 2533 "external_cronet_license") 2534 2535 blueprint.set_package_module(package_module) 2536 2537 2538def create_license_modules(blueprints: Dict[str, Blueprint]) -> Dict[ 2539 str, Module]: 2540 """ 2541 Creates license module (if possible) for each blueprint passed, a license 2542 module will be created if a README.chromium exists in the same directory as 2543 the BUILD.gn which created that blueprint. 2544 2545 Note: A blueprint can be in a different directory than where the BUILD.gn is 2546 declared, this is the case in rust crates. 2547 2548 :param blueprints: List of paths for all possible blueprints. 2549 :return: Dictionary of (path, license_module). 2550 """ 2551 license_modules = {} 2552 for blueprint_path, blueprint in blueprints.items(): 2553 if not blueprint.get_readme_location(): 2554 # Don't generate a license for the top-level Android.bp as this is handled 2555 # manually in Android.extras.bp 2556 continue 2557 2558 license_module = _maybe_create_license_module(blueprint.get_readme_location()) 2559 if license_module: 2560 license_modules[blueprint_path] = license_module 2561 return license_modules 2562 2563 2564def _get_rust_crate_root_directory_from_crate_root(crate_root: str) -> str: 2565 if crate_root and crate_root.startswith( 2566 "third_party/rust/chromium_crates_io/vendor"): 2567 # Return the first 5 directories (a/b/c/d/e) 2568 crate_root_dir = crate_root.split("/")[:5] 2569 return "/".join(crate_root_dir) 2570 return None 2571 2572 2573def _locate_android_bp_destination(module: Module) -> str: 2574 """Returns the appropriate location of the generated Android.bp for the 2575 specified module. Sometimes it is favourable to relocate the Android.bp to 2576 a different location other than next to BUILD.gn (eg: rust's BUILD.gn are 2577 defined in a different directory than the source code). 2578 2579 :returns the appropriate location for the blueprint 2580 """ 2581 crate_root_dir = _get_rust_crate_root_directory_from_crate_root( 2582 module.crate_root) 2583 if module.build_file_path in BLUEPRINTS_MAPPING: 2584 return BLUEPRINTS_MAPPING[module.build_file_path] 2585 elif crate_root_dir: 2586 return crate_root_dir 2587 else: 2588 return module.build_file_path 2589 2590def _break_down_blueprint(top_level_blueprint: Blueprint): 2591 """ 2592 This breaks down the top-level blueprint into smaller blueprints in 2593 different directory. The goal here is to break down the huge Android.bp 2594 into smaller ones for compliance with SBOM. At the moment, not all targets 2595 can be easily rebased to a different directory as GN does not respect 2596 package boundaries. 2597 2598 :returns A dictionary of path -> Blueprint, the path is relative to repository 2599 root. 2600 """ 2601 blueprints = {"": Blueprint()} 2602 for (module_name, module) in top_level_blueprint.modules.items(): 2603 if module.type in ["package", "genrule", "cc_genrule", "java_genrule", 2604 "cc_preprocess_no_configuration"] and not module.allow_rebasing: 2605 # Exclude the genrules from the rebasing as there is no support for them. 2606 # cc_preprocess_no_configuration is created only for the sake of genrules as an intermediate 2607 # target. 2608 blueprints[""].add_module(module) 2609 continue 2610 2611 android_bp_path = _locate_android_bp_destination(module) 2612 if android_bp_path is None: 2613 # Raise an exception if the module does not specify a BUILD file path. 2614 raise Exception(f"Found module {module_name} without a build file path.") 2615 2616 rebased_module = _rebase_module(module, android_bp_path) 2617 if rebased_module: 2618 if not android_bp_path in blueprints.keys(): 2619 blueprints[android_bp_path] = Blueprint(module.build_file_path) 2620 blueprints[android_bp_path].add_module(rebased_module) 2621 else: 2622 # Append to the top-level blueprint. 2623 blueprints[""].add_module(module) 2624 2625 for blueprint in blueprints.values(): 2626 if blueprint.get_buildgn_location() in README_MAPPING: 2627 blueprint.set_readme_location(README_MAPPING[ 2628 blueprint.get_buildgn_location()]) 2629 return blueprints 2630 2631def main(): 2632 parser = argparse.ArgumentParser( 2633 description='Generate Android.bp from a GN description.') 2634 parser.add_argument( 2635 '--desc', 2636 help='GN description (e.g., gn desc out --format=json --all-toolchains "//*".' + 2637 'You can specify multiple --desc options for different target_cpu', 2638 required=True, 2639 action='append' 2640 ) 2641 parser.add_argument( 2642 '--repo_root', 2643 required=True, 2644 help='Path to the root of the repistory' 2645 ) 2646 parser.add_argument( 2647 '--build_script_output', 2648 help='JSON-formatted file containing output of build scripts broken down' + 2649 'by architecture.', 2650 required=True 2651 ) 2652 parser.add_argument( 2653 '--extras', 2654 help='Extra targets to include at the end of the Blueprint file', 2655 default=os.path.join(gn_utils.repo_root(), 'Android.bp.extras'), 2656 ) 2657 parser.add_argument( 2658 '--output', 2659 help='Blueprint file to create', 2660 default=os.path.join(gn_utils.repo_root(), 'Android.bp'), 2661 ) 2662 parser.add_argument( 2663 '-v', 2664 '--verbose', 2665 help='Print debug logs.', 2666 action='store_true', 2667 ) 2668 parser.add_argument( 2669 'targets', 2670 nargs=argparse.REMAINDER, 2671 help='Targets to include in the blueprint (e.g., "//:perfetto_tests")' 2672 ) 2673 args = parser.parse_args() 2674 2675 if args.verbose: 2676 log.basicConfig(format='%(levelname)s:%(funcName)s:%(message)s', level=log.DEBUG) 2677 2678 targets = args.targets or DEFAULT_TARGETS 2679 build_scripts_output = None 2680 with open(args.build_script_output) as f: 2681 build_scripts_output = json.load(f) 2682 gn = gn_utils.GnParser(builtin_deps, build_scripts_output) 2683 for desc_file in args.desc: 2684 with open(desc_file) as f: 2685 desc = json.load(f) 2686 for target in targets: 2687 gn.parse_gn_desc(desc, target) 2688 for test_target in DEFAULT_TESTS: 2689 gn.parse_gn_desc(desc, test_target, is_test_target=True) 2690 top_level_blueprint = create_blueprint_for_targets(gn, targets, DEFAULT_TESTS) 2691 project_root = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) 2692 tool_name = os.path.relpath(os.path.abspath(__file__), project_root) 2693 2694 final_blueprints = _break_down_blueprint(top_level_blueprint) 2695 license_modules = create_license_modules(final_blueprints) 2696 for (path, module) in license_modules.items(): 2697 final_blueprints[path].set_license_module(module) 2698 2699 finalize_package_modules(final_blueprints) 2700 2701 2702 header = """// Copyright (C) 2022 The Android Open Source Project 2703// 2704// Licensed under the Apache License, Version 2.0 (the "License"); 2705// you may not use this file except in compliance with the License. 2706// You may obtain a copy of the License at 2707// 2708// http://www.apache.org/licenses/LICENSE-2.0 2709// 2710// Unless required by applicable law or agreed to in writing, software 2711// distributed under the License is distributed on an "AS IS" BASIS, 2712// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2713// See the License for the specific language governing permissions and 2714// limitations under the License. 2715// 2716// This file is automatically generated by %s. Do not edit. 2717""" % (tool_name) 2718 2719 for (path, blueprint) in final_blueprints.items(): 2720 android_bp_file = Path(os.path.join(args.repo_root, path, "Android.bp")) 2721 android_bp_file.write_text("\n".join( 2722 [header] + BLUEPRINTS_EXTRAS.get(path, []) + blueprint.to_string())) 2723 2724 return 0 2725 2726 2727if __name__ == '__main__': 2728 sys.exit(main()) 2729