Path: blob/main_old/scripts/generate_android_bp.py
1693 views
# Copyright The ANGLE Project Authors. All rights reserved.1# Use of this source code is governed by a BSD-style license that can be2# found in the LICENSE file.3#4# Generates an Android.bp file from the json output of a 'gn desc' command.5# Example usage:6# gn desc out/Android --format=json "*" > desc.json7# python scripts/generate_android_bp.py desc.json > Android.bp89import json10import sys11import re12import os13import argparse1415root_targets = [16"//:libGLESv2",17"//:libGLESv1_CM",18"//:libEGL",19]2021sdk_version = '28'22stl = 'libc++_static'2324abi_arm = 'arm'25abi_arm64 = 'arm64'26abi_x86 = 'x86'27abi_x64 = 'x86_64'2829abi_targets = [abi_arm, abi_arm64, abi_x86, abi_x64]303132def tabs(indent):33return ' ' * (indent * 4)343536def has_child_values(value):37# Elements of the blueprint can be pruned if they are empty lists or dictionaries of empty38# lists39if isinstance(value, list):40return len(value) > 041if isinstance(value, dict):42for (item, item_value) in value.items():43if has_child_values(item_value):44return True45return False4647# This is a value leaf node48return True495051def write_blueprint_key_value(output, name, value, indent=1):52if not has_child_values(value):53return5455if isinstance(value, set) or isinstance(value, list):56value = list(sorted(set(value)))5758if isinstance(value, list):59output.append(tabs(indent) + '%s: [' % name)60for item in value:61output.append(tabs(indent + 1) + '"%s",' % item)62output.append(tabs(indent) + '],')63return64if isinstance(value, dict):65if not value:66return67output.append(tabs(indent) + '%s: {' % name)68for (item, item_value) in value.items():69write_blueprint_key_value(output, item, item_value, indent + 1)70output.append(tabs(indent) + '},')71return72if isinstance(value, bool):73output.append(tabs(indent) + '%s: %s,' % (name, 'true' if value else 'false'))74return75output.append(tabs(indent) + '%s: "%s",' % (name, value))767778def write_blueprint(output, target_type, values):79if target_type == 'license':80comment = """81// Added automatically by a large-scale-change that took the approach of82// 'apply every license found to every target'. While this makes sure we respect83// every license restriction, it may not be entirely correct.84//85// e.g. GPL in an MIT project might only apply to the contrib/ directory.86//87// Please consider splitting the single license below into multiple licenses,88// taking care not to lose any license_kind information, and overriding the89// default license using the 'licenses: [...]' property on targets as needed.90//91// For unused files, consider creating a 'fileGroup' with "//visibility:private"92// to attach the license to, and including a comment whether the files may be93// used in the current project.94// See: http://go/android-license-faq"""95output.append(comment)9697output.append('%s {' % target_type)98for (key, value) in values.items():99write_blueprint_key_value(output, key, value)100output.append('}')101102103def gn_target_to_blueprint_target(target, target_info):104if 'output_name' in target_info:105return target_info['output_name']106107# Split the gn target name (in the form of //gn_file_path:target_name) into gn_file_path and108# target_name109target_regex = re.compile(r"^//([a-zA-Z0-9\-\+_/]*):([a-zA-Z0-9\-\+_.]+)$")110match = re.match(target_regex, target)111assert match is not None112113gn_file_path = match.group(1)114target_name = match.group(2)115assert len(target_name) > 0116117# Clean up the gn file path to be a valid blueprint target name.118gn_file_path = gn_file_path.replace("/", "_").replace(".", "_").replace("-", "_")119120# Generate a blueprint target name by merging the gn path and target so each target is unique.121# Prepend the 'angle' prefix to all targets in the root path (empty gn_file_path).122# Skip this step if the target name already starts with 'angle' to avoid target names such as 'angle_angle_common'.123root_prefix = "angle"124if len(gn_file_path) == 0 and not target_name.startswith(root_prefix):125gn_file_path = root_prefix126127# Avoid names such as _angle_common if the gn_file_path is empty.128if len(gn_file_path) > 0:129gn_file_path += "_"130131return gn_file_path + target_name132133134def remap_gn_path(path):135# TODO: pass the gn gen folder as an arg so it is future proof. b/150457277136remap_folders = [137('out/Android/gen/angle/', ''),138('out/Android/gen/', ''),139]140141remapped_path = path142for (remap_source, remap_dest) in remap_folders:143remapped_path = remapped_path.replace(remap_source, remap_dest)144145return remapped_path146147148def gn_path_to_blueprint_path(source):149# gn uses '//' to indicate the root directory, blueprint uses the .bp file's location150return remap_gn_path(re.sub(r'^//?', '', source))151152153def gn_paths_to_blueprint_paths(paths):154rebased_paths = []155for path in paths:156rebased_paths.append(gn_path_to_blueprint_path(path))157return rebased_paths158159160def gn_sources_to_blueprint_sources(sources):161# Blueprints only list source files in the sources list. Headers are only referenced though162# include paths.163file_extension_allowlist = [164'.c',165'.cc',166'.cpp',167]168169rebased_sources = []170for source in sources:171if os.path.splitext(source)[1] in file_extension_allowlist:172rebased_sources.append(gn_path_to_blueprint_path(source))173return rebased_sources174175176target_blockist = [177'//build/config:shared_library_deps',178'//third_party/vulkan-validation-layers/src:vulkan_clean_old_validation_layer_objects',179]180181third_party_target_allowlist = [182'//third_party/abseil-cpp',183'//third_party/vulkan-deps',184'//third_party/vulkan_memory_allocator',185'//third_party/zlib',186]187188include_blocklist = [189'//buildtools/third_party/libc++/',190'//out/Android/gen/third_party/vulkan-deps/glslang/src/include/',191'//third_party/android_ndk/sources/android/cpufeatures/',192]193194195def gn_deps_to_blueprint_deps(target_info, build_info):196static_libs = []197shared_libs = []198defaults = []199generated_headers = []200header_libs = []201if 'deps' not in target_info:202return static_libs, defaults203204for dep in target_info['deps']:205if dep not in target_blockist and (not dep.startswith('//third_party') or any(206dep.startswith(substring) for substring in third_party_target_allowlist)):207dep_info = build_info[dep]208blueprint_dep_name = gn_target_to_blueprint_target(dep, dep_info)209210# Depending on the dep type, blueprints reference it differently.211gn_dep_type = dep_info['type']212if gn_dep_type == 'static_library':213static_libs.append(blueprint_dep_name)214elif gn_dep_type == 'shared_library':215shared_libs.append(blueprint_dep_name)216elif gn_dep_type == 'source_set' or gn_dep_type == 'group':217defaults.append(blueprint_dep_name)218elif gn_dep_type == 'action':219generated_headers.append(blueprint_dep_name)220221# Blueprints do not chain linking of static libraries.222(child_static_libs, _, _, child_generated_headers, _) = gn_deps_to_blueprint_deps(223dep_info, build_info)224225# Each target needs to link all child static library dependencies.226static_libs += child_static_libs227228# Each blueprint target runs genrules in a different output directory unlike GN. If a229# target depends on another's genrule, it wont find the outputs. Propogate generated230# headers up the dependency stack.231generated_headers += child_generated_headers232elif dep == '//third_party/android_ndk:cpu_features':233# chrome_zlib needs cpufeatures from the Android NDK. Rather than including the234# entire NDK is a dep in the ANGLE checkout, use the library that's already part235# of Android.236dep_info = build_info[dep]237blueprint_dep_name = gn_target_to_blueprint_target(dep, dep_info)238static_libs.append('cpufeatures')239240return static_libs, shared_libs, defaults, generated_headers, header_libs241242243def gn_libs_to_blueprint_shared_libraries(target_info):244lib_blockist = [245'android_support',246'unwind',247]248249result = []250if 'libs' in target_info:251for lib in target_info['libs']:252if lib not in lib_blockist:253android_lib = lib if '@' in lib else 'lib' + lib254result.append(android_lib)255return result256257258def gn_include_dirs_to_blueprint_include_dirs(target_info):259result = []260if 'include_dirs' in target_info:261for include_dir in target_info['include_dirs']:262if len(include_dir) > 0 and include_dir not in include_blocklist:263result.append(gn_path_to_blueprint_path(include_dir))264return result265266267def escape_quotes(string):268return string.replace("\"", "\\\"").replace("\'", "\\\'")269270271def gn_cflags_to_blueprint_cflags(target_info):272result = []273274# regexs of allowlisted cflags275cflag_allowlist = [276r'^-Wno-.*$', # forward cflags that disable warnings277r'-mpclmul' # forward "-mpclmul" (used by zlib)278]279280for cflag_type in ['cflags', 'cflags_c', 'cflags_cc']:281if cflag_type in target_info:282for cflag in target_info[cflag_type]:283for allowlisted_cflag in cflag_allowlist:284if re.search(allowlisted_cflag, cflag):285result.append(cflag)286287# Chrome and Android use different versions of Clang which support differnt warning options.288# Ignore errors about unrecognized warning flags.289result.append('-Wno-unknown-warning-option')290291# Override AOSP build flags to match ANGLE's CQ testing and reduce binary size292result.append('-Oz')293result.append('-fno-unwind-tables')294295if 'defines' in target_info:296for define in target_info['defines']:297# Don't emit ANGLE's CPU-bits define here, it will be part of the arch-specific298# information later299result.append('-D%s' % escape_quotes(define))300301return result302303304blueprint_library_target_types = {305"static_library": "cc_library_static",306"shared_library": "cc_library_shared",307"source_set": "cc_defaults",308"group": "cc_defaults",309}310311312def merge_bps(bps_for_abis):313common_bp = {}314for abi in abi_targets:315for key in bps_for_abis[abi]:316if isinstance(bps_for_abis[abi][key], list):317# Find list values that are common to all ABIs318for value in bps_for_abis[abi][key]:319value_in_all_abis = True320for abi2 in abi_targets:321if key == 'defaults':322# arch-specific defaults are not supported323break324value_in_all_abis = value_in_all_abis and (key in bps_for_abis[abi2].keys(325)) and (value in bps_for_abis[abi2][key])326if value_in_all_abis:327if key in common_bp.keys():328common_bp[key].append(value)329else:330common_bp[key] = [value]331else:332if 'arch' not in common_bp.keys():333# Make sure there is an 'arch' entry to hold ABI-specific values334common_bp['arch'] = {}335for abi3 in abi_targets:336common_bp['arch'][abi3] = {}337if key in common_bp['arch'][abi].keys():338common_bp['arch'][abi][key].append(value)339else:340common_bp['arch'][abi][key] = [value]341else:342# Assume everything that's not a list is common to all ABIs343common_bp[key] = bps_for_abis[abi][key]344345return common_bp346347348def library_target_to_blueprint(target, build_info):349bps_for_abis = {}350blueprint_type = ""351for abi in abi_targets:352if target not in build_info[abi].keys():353bps_for_abis[abi] = {}354continue355356target_info = build_info[abi][target]357358blueprint_type = blueprint_library_target_types[target_info['type']]359360bp = {'name': gn_target_to_blueprint_target(target, target_info)}361362if 'sources' in target_info:363bp['srcs'] = gn_sources_to_blueprint_sources(target_info['sources'])364365(bp['static_libs'], bp['shared_libs'], bp['defaults'], bp['generated_headers'],366bp['header_libs']) = gn_deps_to_blueprint_deps(target_info, build_info[abi])367bp['shared_libs'] += gn_libs_to_blueprint_shared_libraries(target_info)368369bp['local_include_dirs'] = gn_include_dirs_to_blueprint_include_dirs(target_info)370371bp['cflags'] = gn_cflags_to_blueprint_cflags(target_info)372373bp['sdk_version'] = sdk_version374bp['stl'] = stl375if target in root_targets:376bp['vendor'] = True377bp['target'] = {'android': {'relative_install_path': 'egl'}}378bps_for_abis[abi] = bp379380common_bp = merge_bps(bps_for_abis)381382return blueprint_type, common_bp383384385def gn_action_args_to_blueprint_args(blueprint_inputs, blueprint_outputs, args):386# TODO: pass the gn gen folder as an arg so we know how to get from the gen path to the root387# path. b/150457277388remap_folders = [389# Specific special-cases first, since the other will strip the prefixes.390('gen/third_party/vulkan-deps/glslang/src/include/glslang/build_info.h',391'glslang/build_info.h'),392('third_party/vulkan-deps/glslang/src',393'external/angle/third_party/vulkan-deps/glslang/src'),394('../../', ''),395('gen/', ''),396]397398result_args = []399for arg in args:400# Attempt to find if this arg is a path to one of the inputs. If it is, use the blueprint401# $(location <path>) argument instead so the path gets remapped properly to the location402# that the script is run from403remapped_path_arg = arg404for (remap_source, remap_dest) in remap_folders:405remapped_path_arg = remapped_path_arg.replace(remap_source, remap_dest)406407if remapped_path_arg in blueprint_inputs or remapped_path_arg in blueprint_outputs:408result_args.append('$(location %s)' % remapped_path_arg)409elif os.path.basename(remapped_path_arg) in blueprint_outputs:410result_args.append('$(location %s)' % os.path.basename(remapped_path_arg))411else:412result_args.append(remapped_path_arg)413414return result_args415416417blueprint_gen_types = {418"action": "cc_genrule",419}420421422inputs_blocklist = [423'//.git/HEAD',424]425426outputs_remap = {427'build_info.h': 'glslang/build_info.h',428}429430431def is_input_in_tool_files(tool_files, input):432return input in tool_files433434435def action_target_to_blueprint(target, build_info):436target_info = build_info[target]437blueprint_type = blueprint_gen_types[target_info['type']]438439bp = {'name': gn_target_to_blueprint_target(target, target_info)}440441# Blueprints use only one 'srcs', merge all gn inputs into one list.442gn_inputs = []443if 'inputs' in target_info:444for input in target_info['inputs']:445if input not in inputs_blocklist:446gn_inputs.append(input)447if 'sources' in target_info:448gn_inputs += target_info['sources']449# Filter out the 'script' entry since Android.bp doesn't like the duplicate entries450if 'script' in target_info:451gn_inputs = [452input for input in gn_inputs453if not is_input_in_tool_files(target_info['script'], input)454]455bp_srcs = gn_paths_to_blueprint_paths(gn_inputs)456457bp['srcs'] = bp_srcs458459# genrules generate the output right into the 'root' directory. Strip any path before the460# file name.461bp_outputs = []462for gn_output in target_info['outputs']:463output = os.path.basename(gn_output)464if output in outputs_remap.keys():465output = outputs_remap[output]466bp_outputs.append(output)467468bp['out'] = bp_outputs469470bp['tool_files'] = [gn_path_to_blueprint_path(target_info['script'])]471472# Generate the full command, $(location) refers to tool_files[0], the script473cmd = ['$(location)'] + gn_action_args_to_blueprint_args(bp_srcs, bp_outputs,474target_info['args'])475bp['cmd'] = ' '.join(cmd)476477bp['sdk_version'] = sdk_version478479return blueprint_type, bp480481482def gn_target_to_blueprint(target, build_info):483for abi in abi_targets:484gn_type = build_info[abi][target]['type']485if gn_type in blueprint_library_target_types:486return library_target_to_blueprint(target, build_info)487elif gn_type in blueprint_gen_types:488return action_target_to_blueprint(target, build_info[abi])489else:490# Target is not used by this ABI491continue492493494def get_gn_target_dependencies(output_dependencies, build_info, target):495if target not in output_dependencies:496output_dependencies.insert(0, target)497498for dep in build_info[target]['deps']:499if dep in target_blockist:500# Blocklisted dep501continue502if dep not in build_info:503# No info for this dep, skip it504continue505506# Recurse507get_gn_target_dependencies(output_dependencies, build_info, dep)508509510def main():511parser = argparse.ArgumentParser(512description='Generate Android blueprints from gn descriptions.')513514for abi in abi_targets:515fixed_abi = abi516if abi == abi_x64:517fixed_abi = 'x64' # gn uses x64, rather than x86_64518parser.add_argument(519'gn_json_' + fixed_abi,520help=fixed_abi +521'gn desc in json format. Generated with \'gn desc <out_dir> --format=json "*"\'.')522args = vars(parser.parse_args())523524build_info = {}525for abi in abi_targets:526fixed_abi = abi527if abi == abi_x64:528fixed_abi = 'x64' # gn uses x64, rather than x86_64529with open(args['gn_json_' + fixed_abi], 'r') as f:530build_info[abi] = json.load(f)531532targets_to_write = []533for abi in abi_targets:534for root_target in root_targets:535get_gn_target_dependencies(targets_to_write, build_info[abi], root_target)536537blueprint_targets = []538for target in targets_to_write:539blueprint_targets.append(gn_target_to_blueprint(target, build_info))540541# Add license build rules542blueprint_targets.append(('package', {543'default_applicable_licenses': ['external_angle_license'],544}))545blueprint_targets.append(('license', {546'name':547'external_angle_license',548'visibility': [':__subpackages__'],549'license_kinds': [550'SPDX-license-identifier-Apache-2.0',551'SPDX-license-identifier-BSD',552'SPDX-license-identifier-LGPL',553'SPDX-license-identifier-MIT',554'SPDX-license-identifier-Zlib',555'legacy_unencumbered',556],557'license_text': [558'LICENSE',559'src/common/third_party/smhasher/LICENSE',560'src/common/third_party/xxhash/LICENSE',561'src/libANGLE/renderer/vulkan/shaders/src/third_party/ffx_spd/LICENSE',562'src/tests/test_utils/third_party/LICENSE',563'src/third_party/libXNVCtrl/LICENSE',564'src/third_party/volk/LICENSE.md',565'third_party/abseil-cpp/LICENSE',566'third_party/vulkan-deps/glslang/LICENSE',567'third_party/vulkan-deps/glslang/src/LICENSE.txt',568'third_party/vulkan-deps/LICENSE',569'third_party/vulkan-deps/spirv-headers/LICENSE',570'third_party/vulkan-deps/spirv-headers/src/LICENSE',571'third_party/vulkan-deps/spirv-tools/LICENSE',572'third_party/vulkan-deps/spirv-tools/src/LICENSE',573'third_party/vulkan-deps/spirv-tools/src/utils/vscode/src/lsp/LICENSE',574'third_party/vulkan-deps/vulkan-headers/LICENSE.txt',575'third_party/vulkan-deps/vulkan-headers/src/LICENSE.txt',576'third_party/vulkan_memory_allocator/LICENSE.txt',577'third_party/zlib/LICENSE',578'tools/flex-bison/third_party/m4sugar/LICENSE',579'tools/flex-bison/third_party/skeletons/LICENSE',580'util/windows/third_party/StackWalker/LICENSE',581],582}))583584# Add APKs with all of the root libraries585blueprint_targets.append((586'filegroup',587{588'name': 'ANGLE_srcs',589# Only add EmptyMainActivity.java since we just need to be able to reply to the intent590# android.app.action.ANGLE_FOR_ANDROID to indicate ANGLE is present on the device.591'srcs': ['src/android_system_settings/src/com/android/angle/EmptyMainActivity.java'],592}))593blueprint_targets.append((594'java_defaults',595{596'name': 'ANGLE_java_defaults',597'sdk_version': 'system_current',598'min_sdk_version': sdk_version,599'compile_multilib': 'both',600'use_embedded_native_libs': True,601'jni_libs': [602# hack: assume abi_arm603gn_target_to_blueprint_target(target, build_info[abi_arm][target])604for target in root_targets605],606'aaptflags': [607# Don't compress *.json files608'-0 .json',609],610'srcs': [':ANGLE_srcs'],611'plugins': ['java_api_finder',],612'privileged': True,613'product_specific': True,614'owner': 'google',615}))616617blueprint_targets.append(('android_app', {618'name': 'ANGLE',619'defaults': ['ANGLE_java_defaults'],620'manifest': 'android/AndroidManifest.xml',621'asset_dirs': ['src/android_system_settings/assets',],622}))623624output = [625"""// GENERATED FILE - DO NOT EDIT.626// Generated by %s627//628// Copyright 2020 The ANGLE Project Authors. All rights reserved.629// Use of this source code is governed by a BSD-style license that can be630// found in the LICENSE file.631//632""" % sys.argv[0]633]634for (blueprint_type, blueprint_data) in blueprint_targets:635write_blueprint(output, blueprint_type, blueprint_data)636637print('\n'.join(output))638639640if __name__ == '__main__':641sys.exit(main())642643644