1# Copyright The ANGLE Project Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4# 5# Generates an Android.bp file from the json output of 4 'gn desc' commands. 6# Invoked during Skia rolls by roll_aosp.sh. For local testing, see: 7# scripts/roll_aosp.sh --genAndroidBp 8 9import json 10import sys 11import re 12import os 13import argparse 14import functools 15import collections 16import textwrap 17from typing import List, Tuple 18 19ROOT_TARGETS = [ 20 "//:libGLESv2", 21 "//:libGLESv1_CM", 22 "//:libEGL", 23] 24 25END2END_TEST_TARGET = "//src/tests:angle_end2end_tests__library" 26 27# Used only in generated Android.bp file for DMA-BUF-enabled builds on Android. 28# See b/353262025 for details. 29DMA_BUF_TARGET = "//src/libANGLE/renderer/vulkan:angle_android_vulkan_dma_buf" 30 31BLUEPRINT_COMMENT_PROPERTY = '__android_bp_comment' 32 33CURRENT_SDK_VERSION = 'current' 34MIN_SDK_VERSION = '28' 35TARGET_SDK_VERSION = '35' 36STL = 'libc++_static' 37 38ABI_ARM = 'arm' 39ABI_ARM64 = 'arm64' 40ABI_X86 = 'x86' 41ABI_X64 = 'x86_64' 42 43ABI_TARGETS = [ABI_ARM, ABI_ARM64, ABI_X86, ABI_X64] 44 45 46def gn_abi(abi): 47 # gn uses x64, rather than x86_64 48 return 'x64' if abi == ABI_X64 else abi 49 50 51# Makes dict cache-able "by reference" (assumed not to be mutated) 52class BuildInfo(dict): 53 54 def __hash__(self): 55 return 0 56 57 def __eq__(self, other): 58 return self is other 59 60 61def tabs(indent): 62 return ' ' * (indent * 4) 63 64 65def has_child_values(value): 66 # Elements of the blueprint can be pruned if they are empty lists or dictionaries of empty 67 # lists 68 if isinstance(value, list): 69 return len(value) > 0 70 if isinstance(value, dict): 71 for (item, item_value) in value.items(): 72 if has_child_values(item_value): 73 return True 74 return False 75 76 # This is a value leaf node 77 return True 78 79 80def write_blueprint_key_value(output, name, value, indent=1): 81 if not has_child_values(value): 82 return 83 84 if isinstance(value, set) or isinstance(value, list): 85 value = list(sorted(set(value))) 86 if name == 'cflags': 87 fix_fortify_source_cflags(value) 88 89 if isinstance(value, list): 90 output.append(tabs(indent) + '%s: [' % name) 91 for item in value: 92 output.append(tabs(indent + 1) + '"%s",' % item) 93 output.append(tabs(indent) + '],') 94 return 95 if isinstance(value, dict): 96 if not value: 97 return 98 output.append(tabs(indent) + '%s: {' % name) 99 for (item, item_value) in value.items(): 100 write_blueprint_key_value(output, item, item_value, indent + 1) 101 output.append(tabs(indent) + '},') 102 return 103 if isinstance(value, bool): 104 output.append(tabs(indent) + '%s: %s,' % (name, 'true' if value else 'false')) 105 return 106 output.append(tabs(indent) + '%s: "%s",' % (name, value)) 107 108 109def write_blueprint(output, target_type, values): 110 if target_type == 'license': 111 comment = """ 112// Added automatically by a large-scale-change that took the approach of 113// 'apply every license found to every target'. While this makes sure we respect 114// every license restriction, it may not be entirely correct. 115// 116// e.g. GPL in an MIT project might only apply to the contrib/ directory. 117// 118// Please consider splitting the single license below into multiple licenses, 119// taking care not to lose any license_kind information, and overriding the 120// default license using the 'licenses: [...]' property on targets as needed. 121// 122// For unused files, consider creating a 'fileGroup' with "//visibility:private" 123// to attach the license to, and including a comment whether the files may be 124// used in the current project. 125// See: http://go/android-license-faq""" 126 output.append(comment) 127 128 output.append('') 129 blueprint_comment = values.get(BLUEPRINT_COMMENT_PROPERTY) 130 if blueprint_comment: 131 for comment_line in textwrap.wrap(blueprint_comment, width=70): 132 output.append('// %s' % comment_line) 133 output.append('%s {' % target_type) 134 for (key, value) in values.items(): 135 if key != BLUEPRINT_COMMENT_PROPERTY: 136 write_blueprint_key_value(output, key, value) 137 output.append('}') 138 139 140def gn_target_to_blueprint_target(target, target_info): 141 if 'output_name' in target_info: 142 return target_info['output_name'] 143 144 if target_info.get('type') == 'shared_library': 145 # Deduce name from the shared library path 146 # end2end tests lib and libangle_util.so don't have output_name set for the early return above 147 return os.path.splitext(os.path.basename(target_info['outputs'][0]))[0] 148 149 # Split the gn target name (in the form of //gn_file_path:target_name) into gn_file_path and 150 # target_name 151 match = re.match(r"^//([a-zA-Z0-9\-\+_/]*):([a-zA-Z0-9\-\+_.]+)$", target) 152 assert match is not None, target 153 154 gn_file_path = match.group(1) 155 target_name = match.group(2) 156 assert len(target_name) > 0 157 158 # Clean up the gn file path to be a valid blueprint target name. 159 gn_file_path = gn_file_path.replace("/", "_").replace(".", "_").replace("-", "_") 160 161 # Generate a blueprint target name by merging the gn path and target so each target is unique. 162 # Prepend the 'angle' prefix to all targets in the root path (empty gn_file_path). 163 # Skip this step if the target name already starts with 'angle' to avoid target names such as 'angle_angle_common'. 164 root_prefix = "angle" 165 if len(gn_file_path) == 0 and not target_name.startswith(root_prefix): 166 gn_file_path = root_prefix 167 168 # Avoid names such as _angle_common if the gn_file_path is empty. 169 if len(gn_file_path) > 0: 170 gn_file_path += "_" 171 172 return gn_file_path + target_name 173 174 175def remap_gn_path(path): 176 # TODO: pass the gn gen folder as an arg so it is future proof. b/150457277 177 remap_folders = [ 178 ('out/Android/gen/angle/', ''), 179 ('out/Android/gen/', ''), 180 ] 181 182 remapped_path = path 183 for (remap_source, remap_dest) in remap_folders: 184 remapped_path = remapped_path.replace(remap_source, remap_dest) 185 186 return remapped_path 187 188 189def gn_path_to_blueprint_path(source): 190 # gn uses '//' to indicate the root directory, blueprint uses the .bp file's location 191 return remap_gn_path(re.sub(r'^//?', '', source)) 192 193 194def gn_paths_to_blueprint_paths(paths): 195 rebased_paths = [] 196 for path in paths: 197 rebased_paths.append(gn_path_to_blueprint_path(path)) 198 return rebased_paths 199 200 201def gn_sources_to_blueprint_sources(sources): 202 # Blueprints only list source files in the sources list. Headers are only referenced though 203 # include paths. 204 file_extension_allowlist = [ 205 '.c', 206 '.cc', 207 '.cpp', 208 ] 209 210 rebased_sources = [] 211 for source in sources: 212 if os.path.splitext(source)[1] in file_extension_allowlist: 213 rebased_sources.append(gn_path_to_blueprint_path(source)) 214 return rebased_sources 215 216 217target_blockist = [ 218 '//build/config:shared_library_deps', 219 '//third_party/zlib:zlib', 220 '//third_party/zlib/google:compression_utils_portable', 221 222 # end2end tests: -> platform libgtest, libgmock 223 '//testing/gtest:gtest', 224 '//testing/gmock:gmock', 225 '//third_party/googletest:gtest', 226 '//third_party/googletest:gmock', 227] 228 229third_party_target_allowlist = [ 230 '//third_party/abseil-cpp', 231 '//third_party/glslang/src', 232 '//third_party/spirv-cross/src', 233 '//third_party/spirv-headers/src', 234 '//third_party/spirv-tools/src', 235 '//third_party/vulkan-headers/src', 236 '//third_party/vulkan-loader/src', 237 '//third_party/vulkan-tools/src', 238 '//third_party/vulkan-utility-libraries/src', 239 '//third_party/vulkan-validation-layers/src', 240 '//third_party/vulkan_memory_allocator', 241] 242 243include_blocklist = [ 244 '//buildtools/third_party/libc++/', 245 '//third_party/libc++/src/', 246 '//out/Android/gen/third_party/glslang/src/include/', 247 '//third_party/zlib/', 248 '//third_party/zlib/google/', 249 # end2end tests: -> platform libgtest, libgmock 250 '//third_party/googletest/custom/', 251 '//third_party/googletest/src/googletest/include/', 252 '//third_party/googletest/src/googlemock/include/', 253] 254 255targets_using_jni = [ 256 '//src/tests:native_test_support_android', 257 '//util:angle_util', 258] 259 260 261@functools.lru_cache(maxsize=None) # .cache() is py3.9 http://b/246559064#comment8 262def gn_deps_to_blueprint_deps(abi, target, build_info): 263 target_info = build_info[abi][target] 264 static_libs = [] 265 shared_libs = [] 266 defaults = [] 267 generated_headers = [] 268 header_libs = [] 269 if target in targets_using_jni: 270 header_libs.append('jni_headers') 271 if 'deps' not in target_info: 272 return static_libs, defaults 273 274 for dep in target_info['deps']: 275 if dep not in target_blockist and (not dep.startswith('//third_party') or any( 276 dep.startswith(substring) for substring in third_party_target_allowlist)): 277 dep_info = build_info[abi][dep] 278 blueprint_dep_name = gn_target_to_blueprint_target(dep, dep_info) 279 280 # Depending on the dep type, blueprints reference it differently. 281 gn_dep_type = dep_info['type'] 282 if gn_dep_type == 'static_library': 283 static_libs.append(blueprint_dep_name) 284 elif gn_dep_type == 'shared_library': 285 shared_libs.append(blueprint_dep_name) 286 elif gn_dep_type == 'source_set' or gn_dep_type == 'group': 287 defaults.append(blueprint_dep_name) 288 elif gn_dep_type == 'action': 289 # ANGLE in Android repo does not depend on 290 # angle_commit_id target to get ANGLE git has. See b/348044346. 291 # Files include angle_commit.h will be able to find this 292 # header file generated by roll_aosp.sh during ANGLE to 293 # Android roll, instead of the version that is generated 294 # by angle_commit_id build target at compile time. 295 if blueprint_dep_name == 'angle_commit_id': 296 continue 297 generated_headers.append(blueprint_dep_name) 298 299 # Blueprints do not chain linking of static libraries. 300 (child_static_libs, _, _, child_generated_headers, 301 _) = gn_deps_to_blueprint_deps(abi, dep, build_info) 302 303 # Each target needs to link all child static library dependencies. 304 static_libs += child_static_libs 305 306 # Each blueprint target runs genrules in a different output directory unlike GN. If a 307 # target depends on another's genrule, it wont find the outputs. Propogate generated 308 # headers up the dependency stack. 309 generated_headers += child_generated_headers 310 elif dep == '//third_party/zlib/google:compression_utils_portable': 311 # Replace zlib by Android's zlib, compression_utils_portable is the root dependency 312 shared_libs.append('libz') 313 static_libs.extend(['zlib_google_compression_utils_portable', 'cpufeatures']) 314 elif dep == '//testing/gtest:gtest': 315 static_libs.append('libgtest_ndk_c++') 316 elif dep == '//testing/gmock:gmock': 317 static_libs.append('libgmock_ndk') 318 319 return static_libs, shared_libs, defaults, generated_headers, header_libs 320 321 322def gn_libs_to_blueprint_shared_libraries(target_info): 323 lib_blockist = [ 324 'android_support', 325 'unwind', 326 ] 327 328 result = [] 329 if 'libs' in target_info: 330 for lib in target_info['libs']: 331 if lib not in lib_blockist and not lib.startswith('//'): 332 android_lib = lib if '@' in lib else 'lib' + lib 333 result.append(android_lib) 334 return result 335 336 337def gn_include_dirs_to_blueprint_include_dirs(target_info): 338 result = [] 339 if 'include_dirs' in target_info: 340 for include_dir in target_info['include_dirs']: 341 if len(include_dir) > 0 and include_dir not in include_blocklist: 342 result.append(gn_path_to_blueprint_path(include_dir)) 343 return result 344 345 346def escape_quotes(string): 347 return string.replace("\"", "\\\"").replace("\'", "\\\'") 348 349 350def gn_cflags_to_blueprint_cflags(target_info): 351 result = [] 352 353 # regexs of allowlisted cflags 354 cflag_allowlist = [ 355 r'^-Wno-.*$', # forward cflags that disable warnings 356 r'^-fvisibility.*$', # forward visibility (https://gcc.gnu.org/wiki/Visibility) flags for better perf on x86 357 r'-mpclmul' # forward "-mpclmul" (used by zlib) 358 ] 359 360 for cflag_type in ['cflags', 'cflags_c', 'cflags_cc']: 361 if cflag_type in target_info: 362 for cflag in target_info[cflag_type]: 363 for allowlisted_cflag in cflag_allowlist: 364 if re.search(allowlisted_cflag, cflag): 365 result.append(cflag) 366 367 if 'defines' in target_info: 368 for define in target_info['defines']: 369 # Don't emit ANGLE's CPU-bits define here, it will be part of the arch-specific 370 # information later 371 result.append('-D%s' % escape_quotes(define)) 372 373 return result 374 375 376blueprint_library_target_types = { 377 "static_library": "cc_library_static", 378 "shared_library": "cc_library_shared", 379 "source_set": "cc_defaults", 380 "group": "cc_defaults", 381} 382 383 384def merge_bps(bps_for_abis): 385 common_bp = {} 386 for abi in ABI_TARGETS: 387 for key, values in bps_for_abis[abi].items(): 388 if not isinstance(values, list): 389 # Assume everything that's not a list is common to all ABIs 390 common_bp[key] = values 391 continue 392 393 # Find list values that are common to all ABIs 394 values_in_all_abis = set.intersection( 395 *[set(bps_for_abis[abi2].get(key, [])) for abi2 in ABI_TARGETS]) 396 397 for value in values: 398 if value in values_in_all_abis or key == 'defaults': # arch-specific defaults are not supported 399 common_bp.setdefault(key, []) 400 common_bp[key].append(value) 401 else: 402 common_bp.setdefault('arch', {abi3: {} for abi3 in ABI_TARGETS}) 403 abi_specific = common_bp['arch'][abi] 404 abi_specific.setdefault(key, []) 405 abi_specific[key].append(value) 406 407 return common_bp 408 409 410def library_target_to_blueprint(target, build_info): 411 bps_for_abis = {} 412 blueprint_type = "" 413 for abi in ABI_TARGETS: 414 if target not in build_info[abi].keys(): 415 bps_for_abis[abi] = {} 416 continue 417 418 target_info = build_info[abi][target] 419 420 blueprint_type = blueprint_library_target_types[target_info['type']] 421 422 bp = {'name': gn_target_to_blueprint_target(target, target_info)} 423 424 if 'sources' in target_info: 425 bp['srcs'] = gn_sources_to_blueprint_sources(target_info['sources']) 426 427 (bp['static_libs'], bp['shared_libs'], bp['defaults'], bp['generated_headers'], 428 bp['header_libs']) = gn_deps_to_blueprint_deps(abi, target, build_info) 429 bp['shared_libs'] += gn_libs_to_blueprint_shared_libraries(target_info) 430 431 bp['local_include_dirs'] = gn_include_dirs_to_blueprint_include_dirs(target_info) 432 433 bp['cflags'] = gn_cflags_to_blueprint_cflags(target_info) 434 435 bp['defaults'].append('angle_common_library_cflags') 436 437 bp['sdk_version'] = CURRENT_SDK_VERSION 438 439 bp['stl'] = STL 440 if target in ROOT_TARGETS: 441 bp['defaults'].append('angle_vendor_cc_defaults') 442 bp['defaults'].append('angle_dma_buf_cc_defaults') 443 bps_for_abis[abi] = bp 444 445 common_bp = merge_bps(bps_for_abis) 446 447 return blueprint_type, common_bp 448 449 450def gn_action_args_to_blueprint_args(blueprint_inputs, blueprint_outputs, args): 451 # TODO: pass the gn gen folder as an arg so we know how to get from the gen path to the root 452 # path. b/150457277 453 remap_folders = [ 454 # Specific special-cases first, since the other will strip the prefixes. 455 ('gen/third_party/glslang/src/include/glslang/build_info.h', 'glslang/build_info.h'), 456 ('third_party/glslang/src', 'external/angle/third_party/glslang/src'), 457 ('../../', ''), 458 ('gen/', ''), 459 ] 460 461 result_args = [] 462 for arg in args: 463 # Attempt to find if this arg is a path to one of the inputs. If it is, use the blueprint 464 # $(location <path>) argument instead so the path gets remapped properly to the location 465 # that the script is run from 466 remapped_path_arg = arg 467 for (remap_source, remap_dest) in remap_folders: 468 remapped_path_arg = remapped_path_arg.replace(remap_source, remap_dest) 469 470 if remapped_path_arg in blueprint_inputs or remapped_path_arg in blueprint_outputs: 471 result_args.append('$(location %s)' % remapped_path_arg) 472 elif os.path.basename(remapped_path_arg) in blueprint_outputs: 473 result_args.append('$(location %s)' % os.path.basename(remapped_path_arg)) 474 else: 475 result_args.append(remapped_path_arg) 476 477 return result_args 478 479 480blueprint_gen_types = { 481 "action": "cc_genrule", 482} 483 484 485inputs_blocklist = [ 486 '//.git/HEAD', 487] 488 489outputs_remap = { 490 'build_info.h': 'glslang/build_info.h', 491} 492 493 494def is_input_in_tool_files(tool_files, input): 495 return input in tool_files 496 497 498# special handling the {{response_file_name}} args in GN: 499# see https://gn.googlesource.com/gn/+/main/docs/reference.md#var_response_file_contents 500# in GN, if we use response_file_contents, the GN build system will automatically 501# write contents specified in response_file_contents arg into a temporary file 502# identified by {{response_file_name}}. However, Android blueprint does not have 503# the matching machanism. Android blueprint does automatically generate the 504# temporary file and does not recognize '{{response_file_name}}'. 505# To solve the problem: 506# 1) replace the '{{response_file_name}}' in command argument with the new 507# temporary file name. 508# 2) write the content specified in 'response_file_contents' to the new temporary 509# file 510# This function completes step 1) above. It checks if there are 511# '{{response_file_name}}' used in the command arguments. If there are, 512# the function replaces the '{{response_file_name}}' with the new temp file 513# named 'gn_response_file', and returns the new temp file to indicate 514# we need to complete step 2) 515def handle_gn_build_arg_response_file_name(command_arg_list): 516 new_temp_file_name = None 517 updated_args = command_arg_list[:] 518 for index, arg in enumerate(updated_args): 519 if arg == '{{response_file_name}}': 520 new_temp_file_name = '$(genDir)/gn_response_file' 521 updated_args[index] = new_temp_file_name 522 return new_temp_file_name, updated_args 523 524 525def action_target_to_blueprint(abi, target, build_info): 526 target_info = build_info[abi][target] 527 blueprint_type = blueprint_gen_types[target_info['type']] 528 529 bp = {'name': gn_target_to_blueprint_target(target, target_info)} 530 531 # Blueprints use only one 'srcs', merge all gn inputs into one list. 532 gn_inputs = [] 533 if 'inputs' in target_info: 534 for input in target_info['inputs']: 535 if input not in inputs_blocklist: 536 gn_inputs.append(input) 537 if 'sources' in target_info: 538 gn_inputs += target_info['sources'] 539 # Filter out the 'script' entry since Android.bp doesn't like the duplicate entries 540 if 'script' in target_info: 541 gn_inputs = [ 542 input for input in gn_inputs 543 if not is_input_in_tool_files(target_info['script'], input) 544 ] 545 546 bp_srcs = gn_paths_to_blueprint_paths(gn_inputs) 547 548 bp['srcs'] = bp_srcs 549 550 # genrules generate the output right into the 'root' directory. Strip any path before the 551 # file name. 552 bp_outputs = [] 553 for gn_output in target_info['outputs']: 554 output = os.path.basename(gn_output) 555 if output in outputs_remap.keys(): 556 output = outputs_remap[output] 557 bp_outputs.append(output) 558 559 bp['out'] = bp_outputs 560 561 bp['tool_files'] = [gn_path_to_blueprint_path(target_info['script'])] 562 563 new_temporary_gn_response_file, updated_args = handle_gn_build_arg_response_file_name( 564 target_info['args']) 565 566 if new_temporary_gn_response_file: 567 # add the command 'echo $(in) > $(genDir)/gn_response_file' to 568 # write $response_file_contents into the new_temporary_gn_response_file. 569 cmd = ['echo $(in) >', new_temporary_gn_response_file, '&&', '$(location)' 570 ] + gn_action_args_to_blueprint_args(bp_srcs, bp_outputs, updated_args) 571 else: 572 cmd = ['$(location)'] + gn_action_args_to_blueprint_args(bp_srcs, bp_outputs, 573 target_info['args']) 574 575 bp['cmd'] = ' '.join(cmd) 576 577 bp['sdk_version'] = CURRENT_SDK_VERSION 578 579 return blueprint_type, bp 580 581 582def gn_target_to_blueprint(target, build_info): 583 for abi in ABI_TARGETS: 584 gn_type = build_info[abi][target]['type'] 585 if gn_type in blueprint_library_target_types: 586 return library_target_to_blueprint(target, build_info) 587 elif gn_type in blueprint_gen_types: 588 return action_target_to_blueprint(abi, target, build_info) 589 else: 590 # Target is not used by this ABI 591 continue 592 593 594@functools.lru_cache(maxsize=None) 595def get_gn_target_dependencies(abi, target, build_info): 596 result = collections.OrderedDict() 597 result[target] = 1 598 599 for dep in build_info[abi][target]['deps']: 600 if dep in target_blockist: 601 # Blocklisted dep 602 continue 603 if dep not in build_info[abi]: 604 # No info for this dep, skip it 605 continue 606 607 # Recurse 608 result.update(get_gn_target_dependencies(abi, dep, build_info)) 609 610 return result 611 612 613def get_angle_in_vendor_flag_config(): 614 blueprint_results = [] 615 616 blueprint_results.append(('soong_config_module_type', { 617 'name': 'angle_config_cc_defaults', 618 'module_type': 'cc_defaults', 619 'config_namespace': 'angle', 620 'bool_variables': ['angle_in_vendor',], 621 'properties': [ 622 'target.android.relative_install_path', 623 'vendor', 624 ], 625 })) 626 627 blueprint_results.append(('soong_config_bool_variable', { 628 'name': 'angle_in_vendor', 629 })) 630 631 blueprint_results.append(( 632 'angle_config_cc_defaults', 633 { 634 'name': 'angle_vendor_cc_defaults', 635 'vendor': False, 636 'target': { 637 'android': { 638 # Android EGL loader can not load from /system/egl/${LIB} 639 # path and hence don't set the relative path so that ANGLE 640 # libraries get built into /system/${LIB} 641 'relative_install_path': '', 642 }, 643 }, 644 'soong_config_variables': { 645 'angle_in_vendor': { 646 'vendor': True, 647 'target': { 648 'android': { 649 'relative_install_path': 'egl', 650 }, 651 }, 652 }, 653 }, 654 })) 655 656 return blueprint_results 657 658 659def get_angle_android_dma_buf_flag_config(build_info): 660 """ 661 Generates a list of Android.bp definitions for angle_android_dma_buf flag. 662 """ 663 664 blueprint_results = [] 665 666 blueprint_results.append(('soong_config_module_type', { 667 'name': 'angle_dma_buf_config_cc_defaults', 668 'module_type': 'cc_defaults', 669 'config_namespace': 'angle', 670 'bool_variables': ['angle_android_dma_buf'], 671 'properties': ['defaults'] 672 })) 673 674 blueprint_results.append(('soong_config_bool_variable', { 675 'name': 'angle_android_dma_buf', 676 })) 677 678 blueprint_results.append(('angle_dma_buf_config_cc_defaults', { 679 BLUEPRINT_COMMENT_PROPERTY: 680 ('Note: this is a no-op for most builds, only applies to products that explicitly ' 681 'enable the angle_android_dma_buf config flag. See b/353262025 for details of the ' 682 'products that use it.'), 683 'name': 'angle_dma_buf_cc_defaults', 684 'soong_config_variables': { 685 'angle_android_dma_buf': { 686 'defaults': [gn_target_to_blueprint_target(DMA_BUF_TARGET, {})], 687 } 688 }, 689 })) 690 691 return blueprint_results 692 693 694# returns list of (blueprint module type, dict with contents) 695def get_blueprint_targets_from_build_info(build_info: BuildInfo) -> List[Tuple[str, dict]]: 696 targets_to_write = collections.OrderedDict() 697 for abi in ABI_TARGETS: 698 for root_target in ROOT_TARGETS + [END2END_TEST_TARGET, DMA_BUF_TARGET]: 699 700 targets_to_write.update(get_gn_target_dependencies(abi, root_target, build_info)) 701 702 generated_targets = [] 703 for target in reversed(targets_to_write.keys()): 704 # Do not export angle_commit_id target in Android.bp, because the script 705 # src/commit_id.py invoked by this target can't guarantee to generate a 706 # meaningful ANGLE git hash during compile time, see b/348044346. 707 # The script src/commit_id.py will be invoked by roll_aosp.h during 708 # ANGLE to Android roll time, and the ANGLE git hash will be output in 709 # {AndroidANGLERoot}/angle_commmit.h. 710 if target == '//:angle_commit_id': 711 continue 712 generated_targets.append(gn_target_to_blueprint(target, build_info)) 713 714 return generated_targets 715 716 717def handle_angle_non_conformant_extensions_and_versions( 718 generated_targets: List[Tuple[str, dict]], 719 blueprint_targets: List[dict], 720): 721 """Replace the non conformant cflags with a separate cc_defaults. 722 723 The downstream can custom the cflags easier. 724 """ 725 non_conform_cflag = '-DANGLE_EXPOSE_NON_CONFORMANT_EXTENSIONS_AND_VERSIONS' 726 non_conform_defaults = 'angle_non_conformant_extensions_and_versions_cflags' 727 728 blueprint_targets.append(('cc_defaults', { 729 'name': non_conform_defaults, 730 'cflags': [non_conform_cflag], 731 })) 732 733 for _, bp in generated_targets: 734 if 'cflags' in bp and non_conform_cflag in bp['cflags']: 735 bp['cflags'] = list(set(bp['cflags']) - {non_conform_cflag}) 736 bp['defaults'].append(non_conform_defaults) 737 738 739def fix_fortify_source_cflags(cflags): 740 # search if there is any cflag starts with '-D_FORTIFY_SOURCE' 741 d_fortify_source_flag = [cflag for cflag in cflags if '-D_FORTIFY_SOURCE' in cflag] 742 # Insert -U_FORTIFY_SOURCE before the first -D_FORTIFY_SOURCE flag. 743 # In case a default mode for FORTIFY_SOURCE is predefined for a compiler, 744 # and the -D_FORTIFY_SOURCE mode we set is different from the default mode, 745 # the compiler will warn about "redefining FORTIFY_SOURCE macro". 746 # To fix this compiler warning, we unset the default mode with 747 # -U_FORTIFY_SOURCE before setting the desired FORTIFY_SOURCE mode in our 748 # cflags. 749 # reference: 750 # https://best.openssf.org/Compiler-Hardening-Guides/Compiler-Options-Hardening-Guide-for-C-and-C++#tldr-what-compiler-options-should-i-use 751 if d_fortify_source_flag: 752 cflags.insert(cflags.index(d_fortify_source_flag[0]), '-U_FORTIFY_SOURCE') 753 754 755def main(): 756 parser = argparse.ArgumentParser( 757 description='Generate Android blueprints from gn descriptions.') 758 759 for abi in ABI_TARGETS: 760 parser.add_argument( 761 '--gn_json_' + gn_abi(abi), 762 help=gn_abi(abi) + 763 ' gn desc file in json format. Generated with \'gn desc <out_dir> --format=json "*"\'.', 764 required=True) 765 parser.add_argument('--output', help='output file (e.g. Android.bp)') 766 args = vars(parser.parse_args()) 767 768 infos = {} 769 for abi in ABI_TARGETS: 770 with open(args['gn_json_' + gn_abi(abi)], 'r') as f: 771 infos[abi] = json.load(f) 772 773 build_info = BuildInfo(infos) 774 775 blueprint_targets = [] 776 777 blueprint_targets.extend(get_angle_in_vendor_flag_config()) 778 blueprint_targets.extend(get_angle_android_dma_buf_flag_config(build_info)) 779 780 blueprint_targets.append(( 781 'cc_defaults', 782 { 783 'name': 784 'angle_common_library_cflags', 785 'cpp_std': 786 'gnu++17', # TODO(b/330910097): std::popcount missing from external/libcxx 787 'cflags': [ 788 # Chrome and Android use different versions of Clang which support differnt warning options. 789 # Ignore errors about unrecognized warning flags. 790 '-Wno-unknown-warning-option', 791 '-O2', 792 # Override AOSP build flags to match ANGLE's CQ testing and reduce binary size 793 '-fno-unwind-tables', 794 # Disable stack protector to reduce cpu overhead. 795 '-fno-stack-protector', 796 ], 797 })) 798 799 generated_targets = get_blueprint_targets_from_build_info(build_info) 800 801 # Will modify generated_targets and blueprint_targets in-place to handle the 802 # angle_expose_non_conformant_extensions_and_versions gn argument. 803 handle_angle_non_conformant_extensions_and_versions(generated_targets, blueprint_targets) 804 805 # Move cflags that are repeated in each target to cc_defaults 806 all_cflags = [set(bp['cflags']) for _, bp in generated_targets if 'cflags' in bp] 807 all_target_cflags = set.intersection(*all_cflags) 808 809 for _, bp in generated_targets: 810 if 'cflags' in bp: 811 bp['cflags'] = list(set(bp['cflags']) - all_target_cflags) 812 bp['defaults'].append('angle_common_auto_cflags') 813 814 blueprint_targets.append(('cc_defaults', { 815 'name': 'angle_common_auto_cflags', 816 'cflags': list(all_target_cflags), 817 })) 818 blueprint_targets.extend(generated_targets) 819 820 # Add license build rules 821 blueprint_targets.append(('package', { 822 'default_applicable_licenses': ['external_angle_license'], 823 })) 824 blueprint_targets.append(('license', { 825 'name': 826 'external_angle_license', 827 'visibility': [':__subpackages__'], 828 'license_kinds': [ 829 'SPDX-license-identifier-Apache-2.0', 830 'SPDX-license-identifier-BSD', 831 'SPDX-license-identifier-GPL', 832 'SPDX-license-identifier-GPL-2.0', 833 'SPDX-license-identifier-GPL-3.0', 834 'SPDX-license-identifier-LGPL', 835 'SPDX-license-identifier-MIT', 836 'SPDX-license-identifier-Zlib', 837 'legacy_unencumbered', 838 ], 839 'license_text': [ 840 'LICENSE', 841 'src/common/third_party/xxhash/LICENSE', 842 'src/libANGLE/renderer/vulkan/shaders/src/third_party/ffx_spd/LICENSE', 843 'src/tests/test_utils/third_party/LICENSE', 844 'src/third_party/libXNVCtrl/LICENSE', 845 'src/third_party/volk/LICENSE.md', 846 'third_party/abseil-cpp/LICENSE', 847 'third_party/android_system_sdk/LICENSE', 848 'third_party/bazel/LICENSE', 849 'third_party/colorama/LICENSE', 850 'third_party/proguard/LICENSE', 851 'third_party/r8/LICENSE', 852 'third_party/turbine/LICENSE', 853 'third_party/glslang/LICENSE', 854 'third_party/glslang/src/LICENSE.txt', 855 'third_party/spirv-headers/LICENSE', 856 'third_party/spirv-headers/src/LICENSE', 857 'third_party/spirv-tools/LICENSE', 858 'third_party/spirv-tools/src/LICENSE', 859 'third_party/spirv-tools/src/utils/vscode/src/lsp/LICENSE', 860 'third_party/vulkan-headers/LICENSE.txt', 861 'third_party/vulkan-headers/src/LICENSE.md', 862 'third_party/vulkan_memory_allocator/LICENSE.txt', 863 'tools/flex-bison/third_party/m4sugar/LICENSE', 864 'tools/flex-bison/third_party/skeletons/LICENSE', 865 'util/windows/third_party/StackWalker/LICENSE', 866 ], 867 })) 868 869 # Add APKs with all of the root libraries and permissions xml 870 blueprint_targets.append(( 871 'filegroup', 872 { 873 'name': 874 'ANGLE_srcs', 875 # We only need EmptyMainActivity.java since we just need to be able to reply to the intent 876 # android.app.action.ANGLE_FOR_ANDROID to indicate ANGLE is present on the device. 877 # However, the internal branch currently uses these files with patches in that branch. 878 'srcs': [ 879 'src/android_system_settings/src/com/android/angle/MainActivity.java', 880 'src/android_system_settings/src/com/android/angle/common/AngleRuleHelper.java', 881 'src/android_system_settings/src/com/android/angle/common/GlobalSettings.java', 882 'src/android_system_settings/src/com/android/angle/common/MainFragment.java', 883 'src/android_system_settings/src/com/android/angle/common/Receiver.java', 884 'src/android_system_settings/src/com/android/angle/common/SearchProvider.java', 885 ], 886 })) 887 888 blueprint_targets.append(('prebuilt_etc', { 889 'name': 'android.software.angle.xml', 890 'src': 'android/android.software.angle.xml', 891 'product_specific': True, 892 'sub_dir': 'permissions', 893 })) 894 895 blueprint_targets.append(( 896 'java_defaults', 897 { 898 'name': 'ANGLE_java_defaults', 899 'sdk_version': 'system_current', 900 'target_sdk_version': TARGET_SDK_VERSION, 901 'min_sdk_version': MIN_SDK_VERSION, 902 'compile_multilib': 'both', 903 'use_embedded_native_libs': True, 904 'jni_libs': [ 905 # hack: assume ABI_ARM 906 gn_target_to_blueprint_target(target, build_info[ABI_ARM][target]) 907 for target in ROOT_TARGETS 908 ], 909 'aaptflags': [ 910 '-0 .json', # Don't compress *.json files 911 "--extra-packages com.android.angle.common", 912 ], 913 'srcs': [':ANGLE_srcs'], 914 'privileged': True, 915 'product_specific': True, 916 'owner': 'google', 917 'required': ['android.software.angle.xml'], 918 })) 919 920 blueprint_targets.append(('android_library', { 921 'name': 'ANGLE_library', 922 'sdk_version': 'system_current', 923 'target_sdk_version': TARGET_SDK_VERSION, 924 'min_sdk_version': MIN_SDK_VERSION, 925 'resource_dirs': ['src/android_system_settings/res',], 926 'asset_dirs': ['src/android_system_settings/assets',], 927 'aaptflags': ['-0 .json',], 928 'manifest': 'src/android_system_settings/src/com/android/angle/AndroidManifest.xml', 929 'static_libs': ['androidx.preference_preference',], 930 })) 931 932 blueprint_targets.append(('android_app', { 933 'name': 'ANGLE', 934 'defaults': ['ANGLE_java_defaults'], 935 'manifest': 'src/android_system_settings/src/com/android/angle/AndroidManifest.xml', 936 'static_libs': ['ANGLE_library'], 937 'optimize': { 938 'enabled': True, 939 'shrink': True, 940 'proguard_compatibility': False, 941 }, 942 'asset_dirs': ['src/android_system_settings/assets',], 943 })) 944 945 blueprint_targets.append(( 946 'java_defaults', 947 { 948 'name': 'ANGLE_java_settings_defaults', 949 'sdk_version': 'system_current', 950 'target_sdk_version': TARGET_SDK_VERSION, 951 'min_sdk_version': MIN_SDK_VERSION, 952 'compile_multilib': 'both', 953 'use_embedded_native_libs': True, 954 'aaptflags': [ 955 '-0 .json', # Don't compress *.json files 956 "--extra-packages com.android.angle.common", 957 ], 958 'srcs': [':ANGLE_srcs'], 959 'privileged': True, 960 'product_specific': True, 961 'owner': 'google', 962 'required': ['android.software.angle.xml'], 963 })) 964 965 blueprint_targets.append(('android_app', { 966 'name': 'ANGLE_settings', 967 'defaults': ['ANGLE_java_settings_defaults'], 968 'manifest': 'src/android_system_settings/src/com/android/angle/AndroidManifest.xml', 969 'static_libs': ['ANGLE_library'], 970 'optimize': { 971 'enabled': True, 972 'shrink': True, 973 'proguard_compatibility': False, 974 }, 975 'asset_dirs': ['src/android_system_settings/assets',], 976 })) 977 978 output = [ 979 """// GENERATED FILE - DO NOT EDIT. 980// Generated by %s 981// 982// Copyright 2020 The ANGLE Project Authors. All rights reserved. 983// Use of this source code is governed by a BSD-style license that can be 984// found in the LICENSE file. 985//""" % sys.argv[0] 986 ] 987 for (blueprint_type, blueprint_data) in blueprint_targets: 988 write_blueprint(output, blueprint_type, blueprint_data) 989 990 with open(args['output'], 'w') as f: 991 f.write('\n'.join(output) + '\n') 992 993 994if __name__ == '__main__': 995 sys.exit(main()) 996