| # Copyright The ANGLE Project Authors. All rights reserved. | |
| # Use of this source code is governed by a BSD-style license that can be | |
| # found in the LICENSE file. | |
| # | |
| # Generates an Android.bp file from the json output of 4 'gn desc' commands. | |
| # Invoked during Skia rolls by roll_aosp.sh. For local testing, see: | |
| # scripts/roll_aosp.sh --genAndroidBp | |
| import json | |
| import sys | |
| import re | |
| import os | |
| import argparse | |
| import functools | |
| import collections | |
| import textwrap | |
| from typing import List, Tuple | |
| ROOT_TARGETS = [ | |
| "//:libGLESv2", | |
| "//:libGLESv1_CM", | |
| "//:libEGL", | |
| ] | |
| END2END_TEST_TARGET = "//src/tests:angle_end2end_tests__library" | |
| # Used only in generated Android.bp file for DMA-BUF-enabled builds on Android. | |
| # See b/353262025 for details. | |
| DMA_BUF_TARGET = "//src/libANGLE/renderer/vulkan:angle_android_vulkan_dma_buf" | |
| BLUEPRINT_COMMENT_PROPERTY = '__android_bp_comment' | |
| CURRENT_SDK_VERSION = 'current' | |
| MIN_SDK_VERSION = '28' | |
| TARGET_SDK_VERSION = '37' | |
| STL = 'libc++_static' | |
| ABI_ARM = 'arm' | |
| ABI_ARM64 = 'arm64' | |
| ABI_X86 = 'x86' | |
| ABI_X64 = 'x86_64' | |
| ABI_TARGETS = [ABI_ARM, ABI_ARM64, ABI_X86, ABI_X64] | |
| rust_ffi_gen_output_map = {} | |
| def gn_abi(abi): | |
| # gn uses x64, rather than x86_64 | |
| return 'x64' if abi == ABI_X64 else abi | |
| # Makes dict cache-able "by reference" (assumed not to be mutated) | |
| class BuildInfo(dict): | |
| def __hash__(self): | |
| return 0 | |
| def __eq__(self, other): | |
| return self is other | |
| def tabs(indent): | |
| return ' ' * (indent * 4) | |
| def has_child_values(value): | |
| # Elements of the blueprint can be pruned if they are empty lists or dictionaries of empty | |
| # lists | |
| if isinstance(value, list): | |
| return len(value) > 0 | |
| if isinstance(value, dict): | |
| for (item, item_value) in value.items(): | |
| if has_child_values(item_value): | |
| return True | |
| return False | |
| # This is a value leaf node | |
| return True | |
| def write_blueprint_key_value(output, name, value, indent=1): | |
| if not has_child_values(value): | |
| return | |
| if isinstance(value, set) or isinstance(value, list): | |
| value = list(sorted(set(value))) | |
| if name == 'cflags': | |
| fix_platform_default_defines(value) | |
| if isinstance(value, list): | |
| output.append(tabs(indent) + '%s: [' % name) | |
| for item in value: | |
| output.append(tabs(indent + 1) + '"%s",' % item) | |
| output.append(tabs(indent) + '],') | |
| return | |
| if isinstance(value, dict): | |
| if not value: | |
| return | |
| output.append(tabs(indent) + '%s: {' % name) | |
| for (item, item_value) in value.items(): | |
| write_blueprint_key_value(output, item, item_value, indent + 1) | |
| output.append(tabs(indent) + '},') | |
| return | |
| if isinstance(value, bool): | |
| output.append(tabs(indent) + '%s: %s,' % (name, 'true' if value else 'false')) | |
| return | |
| output.append(tabs(indent) + '%s: "%s",' % (name, value)) | |
| def write_blueprint(output, target_type, values): | |
| if target_type == 'license': | |
| comment = """ | |
| // Added automatically by a large-scale-change that took the approach of | |
| // 'apply every license found to every target'. While this makes sure we respect | |
| // every license restriction, it may not be entirely correct. | |
| // | |
| // e.g. GPL in an MIT project might only apply to the contrib/ directory. | |
| // | |
| // Please consider splitting the single license below into multiple licenses, | |
| // taking care not to lose any license_kind information, and overriding the | |
| // default license using the 'licenses: [...]' property on targets as needed. | |
| // | |
| // For unused files, consider creating a 'fileGroup' with "//visibility:private" | |
| // to attach the license to, and including a comment whether the files may be | |
| // used in the current project. | |
| // See: http://go/android-license-faq""" | |
| output.append(comment) | |
| output.append('') | |
| blueprint_comment = values.get(BLUEPRINT_COMMENT_PROPERTY) | |
| if blueprint_comment: | |
| for comment_line in textwrap.wrap(blueprint_comment, width=70): | |
| output.append('// %s' % comment_line) | |
| output.append('%s {' % target_type) | |
| for (key, value) in values.items(): | |
| if key != BLUEPRINT_COMMENT_PROPERTY: | |
| write_blueprint_key_value(output, key, value) | |
| output.append('}') | |
| def gn_target_to_blueprint_target(target, target_info): | |
| if 'output_name' in target_info and target_info.get('type') != 'rust_library': | |
| return target_info['output_name'] | |
| if target_info.get('type') == 'rust_library': | |
| return 'lib' + target_info['crate_name'] | |
| if target_info.get('type') == 'shared_library': | |
| # Deduce name from the shared library path | |
| # end2end tests lib and libangle_util.so don't have output_name set for the early return above | |
| return os.path.splitext(os.path.basename(target_info['outputs'][0]))[0] | |
| # Split the gn target name (in the form of //gn_file_path:target_name) into gn_file_path and | |
| # target_name | |
| match = re.match(r"^//([a-zA-Z0-9\-\+_/]*):([a-zA-Z0-9\-\+_.]+)$", target) | |
| assert match is not None, target | |
| gn_file_path = match.group(1) | |
| target_name = match.group(2) | |
| assert len(target_name) > 0 | |
| # Clean up the gn file path to be a valid blueprint target name. | |
| gn_file_path = gn_file_path.replace("/", "_").replace(".", "_").replace("-", "_") | |
| # Generate a blueprint target name by merging the gn path and target so each target is unique. | |
| # Prepend the 'angle' prefix to all targets in the root path (empty gn_file_path). | |
| # Skip this step if the target name already starts with 'angle' to avoid target names such as 'angle_angle_common'. | |
| root_prefix = "angle" | |
| if len(gn_file_path) == 0 and not target_name.startswith(root_prefix): | |
| gn_file_path = root_prefix | |
| # Avoid names such as _angle_common if the gn_file_path is empty. | |
| if len(gn_file_path) > 0: | |
| gn_file_path += "_" | |
| return gn_file_path + target_name | |
| def remap_gn_path(path): | |
| # TODO: pass the gn gen folder as an arg so it is future proof. b/150457277 | |
| remap_folders = [ | |
| ('out/Android/gen/angle/', ''), | |
| ('out/Android/gen/', ''), | |
| ] | |
| remapped_path = path | |
| for (remap_source, remap_dest) in remap_folders: | |
| remapped_path = remapped_path.replace(remap_source, remap_dest) | |
| return remapped_path | |
| def gn_path_to_blueprint_path(source): | |
| # gn uses '//' to indicate the root directory, blueprint uses the .bp file's location | |
| return remap_gn_path(re.sub(r'^//?', '', source)) | |
| def gn_paths_to_blueprint_paths(paths): | |
| rebased_paths = [] | |
| for path in paths: | |
| rebased_paths.append(gn_path_to_blueprint_path(path)) | |
| return rebased_paths | |
| def gn_generated_sources_to_blueprint_generated_sources(sources): | |
| # Blueprints only list source files in the sources list. Headers are only referenced though | |
| # include paths. | |
| generated_source_file_extension_allowlist = [ | |
| '.c', | |
| '.cc', | |
| '.cpp', | |
| ] | |
| generated_source_targets = [] | |
| for source in sources: | |
| if source in rust_ffi_gen_output_map and os.path.splitext( | |
| source)[1] in generated_source_file_extension_allowlist: | |
| generated_source_targets.append(rust_ffi_gen_output_map[source]) | |
| return generated_source_targets | |
| def gn_sources_to_blueprint_sources(sources): | |
| # Blueprints only list source files in the sources list. Headers are only referenced though | |
| # include paths. | |
| file_extension_allowlist = [ | |
| '.c', | |
| '.cc', | |
| '.cpp', | |
| ] | |
| rebased_sources = [] | |
| for source in sources: | |
| if source in rust_ffi_gen_output_map: | |
| # these need to be placed under generated_sources and generated_headers entry in Android.bp | |
| continue | |
| if os.path.splitext(source)[1] in file_extension_allowlist: | |
| rebased_sources.append(gn_path_to_blueprint_path(source)) | |
| return rebased_sources | |
| target_blockist = [ | |
| '//build/config:shared_library_deps', | |
| '//third_party/zlib:zlib', | |
| '//third_party/zlib/google:compression_utils_portable', | |
| # end2end tests: -> platform libgtest, libgmock | |
| '//testing/gtest:gtest', | |
| '//testing/gmock:gmock', | |
| '//third_party/googletest:gtest', | |
| '//third_party/googletest:gmock', | |
| # Do not add chromium rust dependencies, we will use Android rust dependencies instead | |
| '//build/rust:cxx_cppdeps', | |
| '//build/rust:cxx_rustdeps', | |
| '//build/rust/allocator:allocator', | |
| '//build/rust/std:std', | |
| '//build/rust/gni_impl:rustc_print_cfg', | |
| '//third_party/rust/cxxbridge_cmd/v1:cxxbridge(//build/toolchain/linux:clang_x64_for_rust_host_build_tools)', | |
| # Do not add chromium perfetto dependencies, use Android perfetto library instead | |
| '//third_party/perfetto/src/tracing:client_api', | |
| ] | |
| third_party_target_allowlist = [ | |
| '//third_party/abseil-cpp', | |
| '//third_party/glslang/src', | |
| '//third_party/spirv-cross/src', | |
| '//third_party/spirv-headers/src', | |
| '//third_party/spirv-tools/src', | |
| '//third_party/vulkan-headers/src', | |
| '//third_party/vulkan-loader/src', | |
| '//third_party/vulkan-tools/src', | |
| '//third_party/vulkan-utility-libraries/src', | |
| '//third_party/vulkan-validation-layers/src', | |
| '//third_party/vulkan_memory_allocator', | |
| ] | |
| include_blocklist = [ | |
| '//buildtools/third_party/libc++/', | |
| '//third_party/libc++/src/', | |
| '//out/Android/gen/third_party/glslang/src/include/', | |
| '//third_party/zlib/', | |
| '//third_party/zlib/google/', | |
| # end2end tests: -> platform libgtest, libgmock | |
| '//third_party/googletest/custom/', | |
| '//third_party/googletest/src/googletest/include/', | |
| '//third_party/googletest/src/googlemock/include/', | |
| # rust ffi gen directory | |
| '//out/Android/gen/', | |
| '//out/Android/gen/src/', | |
| '//out/Android/gen/third_party/angle/src/', | |
| # Do not add chromium perfetto dependencies. The include will be resolved by Android libperfetto_client_experimental | |
| '//third_party/perfetto/include/', | |
| '//out/Android/gen/third_party/perfetto/', | |
| '//out/Android/gen/third_party/perfetto/build_config/', | |
| '//third_party/protobuf/*', | |
| ] | |
| targets_using_jni = [ | |
| '//src/tests:native_test_support_android', | |
| '//util:angle_util', | |
| ] | |
| def get_rust_ffi_header_file_gen_target_name(rust_source_full_path): | |
| _, _, rust_source_without_full_path = rust_source_full_path.rpartition('/') | |
| generated_rust_ffi_header_file = "libcxx_" + rust_source_without_full_path.removesuffix( | |
| ".rs") + "_header" | |
| return generated_rust_ffi_header_file | |
| def get_rust_ffi_source_file_gen_target_name(rust_source_full_path): | |
| _, _, rust_source_without_full_path = rust_source_full_path.rpartition('/') | |
| generated_rust_ffi_source_file = "libcxx_" + rust_source_without_full_path.removesuffix( | |
| ".rs") + "_cpp" | |
| return generated_rust_ffi_source_file | |
| # Check if the given gn build target type and target information matches with the target that generates C++ headers | |
| # and C++ source from rust files | |
| def is_gn_target_rust_ffi(gn_dep_type, dep_info): | |
| if gn_dep_type in blueprint_gen_rust_ffi_types and 'script' in dep_info and dep_info[ | |
| 'script'] == '//third_party/rust/cxx/chromium_integration/run_cxxbridge.py': | |
| return True | |
| else: | |
| return False | |
| @functools.lru_cache(maxsize=None) # .cache() is py3.9 http://b/246559064#comment8 | |
| def gn_deps_to_blueprint_deps(abi, target, build_info): | |
| target_info = build_info[abi][target] | |
| static_libs = [] | |
| shared_libs = [] | |
| defaults = [] | |
| generated_headers = [] | |
| whole_static_libs = [] | |
| header_libs = [] | |
| if target in targets_using_jni: | |
| header_libs.append('jni_headers') | |
| if 'deps' not in target_info: | |
| return static_libs, defaults | |
| for dep in target_info['deps']: | |
| if dep not in target_blockist and (not dep.startswith('//third_party') or any( | |
| dep.startswith(substring) for substring in third_party_target_allowlist)): | |
| dep_info = build_info[abi][dep] | |
| blueprint_dep_name = gn_target_to_blueprint_target(dep, dep_info) | |
| # Depending on the dep type, blueprints reference it differently. | |
| gn_dep_type = dep_info['type'] | |
| if gn_dep_type == 'static_library': | |
| static_libs.append(blueprint_dep_name) | |
| elif gn_dep_type == 'shared_library': | |
| shared_libs.append(blueprint_dep_name) | |
| elif gn_dep_type == 'source_set' or gn_dep_type == 'group': | |
| defaults.append(blueprint_dep_name) | |
| elif gn_dep_type == 'action': | |
| # ANGLE in Android repo does not depend on | |
| # angle_commit_id target to get ANGLE git has. See b/348044346. | |
| # Files include angle_commit.h will be able to find this | |
| # header file generated by roll_aosp.sh during ANGLE to | |
| # Android roll, instead of the version that is generated | |
| # by angle_commit_id build target at compile time. | |
| if blueprint_dep_name == 'angle_commit_id': | |
| continue | |
| generated_headers.append(blueprint_dep_name) | |
| elif is_gn_target_rust_ffi(gn_dep_type, dep_info): | |
| for generated_file in dep_info['outputs']: | |
| if generated_file in rust_ffi_gen_output_map and os.path.splitext( | |
| generated_file)[1] == '.h': | |
| generated_headers.append(rust_ffi_gen_output_map[generated_file]) | |
| elif gn_dep_type == 'rust_library': | |
| whole_static_libs.append(blueprint_dep_name) | |
| # Blueprints do not chain linking of static libraries. | |
| (child_static_libs, _, _, _, child_generated_headers, | |
| _) = gn_deps_to_blueprint_deps(abi, dep, build_info) | |
| # Each target needs to link all child static library dependencies. | |
| static_libs += child_static_libs | |
| # Each blueprint target runs genrules in a different output directory unlike GN. If a | |
| # target depends on another's genrule, it wont find the outputs. Propogate generated | |
| # headers up the dependency stack. | |
| generated_headers += child_generated_headers | |
| elif dep == '//third_party/zlib/google:compression_utils_portable': | |
| # Replace zlib by Android's zlib, compression_utils_portable is the root dependency | |
| shared_libs.append('libz') | |
| static_libs.extend(['zlib_google_compression_utils_portable']) | |
| elif dep == '//testing/gtest:gtest': | |
| static_libs.append('libgtest_ndk_c++') | |
| elif dep == '//testing/gmock:gmock': | |
| static_libs.append('libgmock_ndk') | |
| elif dep == '//third_party/perfetto/src/tracing:client_api': | |
| # Replace perfetto dependency with Android's prebuilt perfetto library | |
| static_libs.append('libperfetto_client_experimental') | |
| return static_libs, shared_libs, defaults, whole_static_libs, generated_headers, header_libs | |
| def gn_libs_to_blueprint_shared_libraries(target_info): | |
| lib_blockist = [ | |
| 'android_support', | |
| 'unwind', | |
| ] | |
| result = [] | |
| if 'libs' in target_info: | |
| for lib in target_info['libs']: | |
| if lib not in lib_blockist and not lib.startswith('//'): | |
| android_lib = lib if '@' in lib else 'lib' + lib | |
| result.append(android_lib) | |
| return result | |
| def is_include_dir_blocklisted(include_dir): | |
| norm_dir = include_dir.rstrip('/') + '/' | |
| for entry in include_blocklist: | |
| if entry.endswith('*'): | |
| prefix = entry[:-1].rstrip('/') + '/' | |
| if norm_dir.startswith(prefix): | |
| return True | |
| elif norm_dir == entry.rstrip('/') + '/': | |
| return True | |
| return False | |
| def gn_include_dirs_to_blueprint_include_dirs(target_info): | |
| result = [] | |
| if 'include_dirs' in target_info: | |
| for include_dir in target_info['include_dirs']: | |
| if len(include_dir) > 0 and not is_include_dir_blocklisted(include_dir): | |
| result.append(gn_path_to_blueprint_path(include_dir)) | |
| return result | |
| def escape_quotes(string): | |
| return string.replace("\"", "\\\"").replace("\'", "\\\'") | |
| def gn_cflags_to_blueprint_cflags(target_info): | |
| result = [] | |
| # regexs of allowlisted cflags | |
| cflag_allowlist = [ | |
| r'^-Wno-.*$', # forward cflags that disable warnings | |
| r'^-fvisibility.*$', # forward visibility (https://gcc.gnu.org/wiki/Visibility) flags for better perf on x86 | |
| r'-mpclmul' # forward "-mpclmul" (used by zlib) | |
| ] | |
| for cflag_type in ['cflags', 'cflags_c', 'cflags_cc']: | |
| if cflag_type in target_info: | |
| for cflag in target_info[cflag_type]: | |
| for allowlisted_cflag in cflag_allowlist: | |
| if re.search(allowlisted_cflag, cflag): | |
| result.append(cflag) | |
| if 'defines' in target_info: | |
| for define in target_info['defines']: | |
| # Don't emit ANGLE's CPU-bits define here, it will be part of the arch-specific | |
| # information later | |
| result.append('-D%s' % escape_quotes(define)) | |
| return result | |
| blueprint_library_target_types = { | |
| "static_library": "cc_library_static", | |
| "shared_library": "cc_library_shared", | |
| "source_set": "cc_defaults", | |
| "group": "cc_defaults", | |
| } | |
| blueprint_rust_library_target_types = { | |
| "rust_library": "rust_ffi_static", | |
| } | |
| def merge_bps(bps_for_abis): | |
| common_bp = {} | |
| for abi in ABI_TARGETS: | |
| for key, values in bps_for_abis[abi].items(): | |
| if not isinstance(values, list): | |
| # Assume everything that's not a list is common to all ABIs | |
| common_bp[key] = values | |
| continue | |
| # Find list values that are common to all ABIs | |
| values_in_all_abis = set.intersection( | |
| *[set(bps_for_abis[abi2].get(key, [])) for abi2 in ABI_TARGETS]) | |
| for value in values: | |
| if value in values_in_all_abis or key == 'defaults': # arch-specific defaults are not supported | |
| common_bp.setdefault(key, []) | |
| common_bp[key].append(value) | |
| else: | |
| common_bp.setdefault('arch', {abi3: {} for abi3 in ABI_TARGETS}) | |
| abi_specific = common_bp['arch'][abi] | |
| abi_specific.setdefault(key, []) | |
| abi_specific[key].append(value) | |
| return common_bp | |
| def rustlibrary_target_to_blueprint(target, build_info) -> list[tuple[str, dict]]: | |
| bps_for_abis = {} | |
| blueprint_type = "" | |
| for abi in ABI_TARGETS: | |
| if target not in build_info[abi].keys(): | |
| bps_for_abis[abi] = {} | |
| continue | |
| target_info = build_info[abi][target] | |
| blueprint_type = blueprint_rust_library_target_types[target_info['type']] | |
| bp = {'name': gn_target_to_blueprint_target(target, target_info)} | |
| bp['crate_name'] = target_info['crate_name'] | |
| bp['srcs'] = [gn_path_to_blueprint_path(target_info['crate_root'])] | |
| bp['rustlibs'] = {"libcxx"} | |
| bp['sdk_version'] = CURRENT_SDK_VERSION | |
| rustEditionPrefix = "--edition=" | |
| if 'rustflags' in target_info: | |
| for rustflag in target_info['rustflags']: | |
| if rustflag.startswith(rustEditionPrefix): | |
| bp['edition'] = rustflag[len(rustEditionPrefix):] | |
| break | |
| bps_for_abis[abi] = bp | |
| common_bp = merge_bps(bps_for_abis) | |
| return [(blueprint_type, common_bp)] | |
| def library_target_to_blueprint(target, build_info) -> list[tuple[str, dict]]: | |
| bps_for_abis = {} | |
| blueprint_type = "" | |
| for abi in ABI_TARGETS: | |
| if target not in build_info[abi].keys(): | |
| bps_for_abis[abi] = {} | |
| continue | |
| target_info = build_info[abi][target] | |
| blueprint_type = blueprint_library_target_types[target_info['type']] | |
| bp = {'name': gn_target_to_blueprint_target(target, target_info)} | |
| if 'sources' in target_info: | |
| bp['srcs'] = gn_sources_to_blueprint_sources(target_info['sources']) | |
| bp['generated_sources'] = gn_generated_sources_to_blueprint_generated_sources( | |
| target_info['sources']) | |
| (bp['static_libs'], bp['shared_libs'], bp['defaults'], bp['whole_static_libs'], | |
| bp['generated_headers'], | |
| bp['header_libs']) = gn_deps_to_blueprint_deps(abi, target, build_info) | |
| bp['shared_libs'] += gn_libs_to_blueprint_shared_libraries(target_info) | |
| bp['local_include_dirs'] = gn_include_dirs_to_blueprint_include_dirs(target_info) | |
| bp['cflags'] = gn_cflags_to_blueprint_cflags(target_info) | |
| bp['defaults'].append('angle_common_library_cflags') | |
| bp['sdk_version'] = CURRENT_SDK_VERSION | |
| bp['stl'] = STL | |
| # Explicitly disable general Control Flow Integrity (CFI) | |
| bp['sanitize'] = { | |
| 'cfi': False, | |
| } | |
| # Explicitly enable LTO and add aggressive LTO optimizations | |
| bp['whole_program_vtables'] = True | |
| bp['ldflags'] = ['-Wl,-plugin-opt,O2'] | |
| if target in ROOT_TARGETS: | |
| bp['defaults'].append('angle_vendor_cc_defaults') | |
| bp['defaults'].append('angle_dma_buf_cc_defaults') | |
| bp['afdo'] = True | |
| bps_for_abis[abi] = bp | |
| common_bp = merge_bps(bps_for_abis) | |
| return [(blueprint_type, common_bp)] | |
| def gn_action_args_to_blueprint_args(blueprint_inputs, blueprint_outputs, args): | |
| # TODO: pass the gn gen folder as an arg so we know how to get from the gen path to the root | |
| # path. b/150457277 | |
| remap_folders = [ | |
| # Specific special-cases first, since the other will strip the prefixes. | |
| ('gen/third_party/glslang/src/include/glslang/build_info.h', 'glslang/build_info.h'), | |
| ('third_party/glslang/src', 'external/angle/third_party/glslang/src'), | |
| ('../../', ''), | |
| ('gen/', ''), | |
| ] | |
| # some args have special prefix added in front of the file name, we need to | |
| # take them out before transforming the file path, and then add them back: | |
| # e.g. | |
| # transform | |
| # --extinst=,../../third_party/spirv-headers/src/include/spirv/unified1/extinst.debuginfo.grammar.json | |
| # to | |
| # --extinst=,$(location third_party/spirv-headers/src/include/spirv/unified1/extinst.debuginfo.grammar.json) | |
| special_prefixs = [ | |
| '--extinst=,', | |
| '--extinst=SHDEBUG100_,', | |
| '--extinst=CLDEBUG100_,', | |
| ] | |
| result_args = [] | |
| for arg in args: | |
| # Attempt to find if this arg is a path to one of the inputs. If it is, use the blueprint | |
| # $(location <path>) argument instead so the path gets remapped properly to the location | |
| # that the script is run from | |
| remapped_path_arg = arg | |
| for (remap_source, remap_dest) in remap_folders: | |
| remapped_path_arg = remapped_path_arg.replace(remap_source, remap_dest) | |
| special_prefix = '' | |
| for special_item in special_prefixs: | |
| if remapped_path_arg.startswith(special_item): | |
| special_prefix = special_item | |
| break | |
| if special_prefix != '': | |
| remapped_path_arg = remapped_path_arg[len(special_prefix):] | |
| if remapped_path_arg in blueprint_inputs or remapped_path_arg in blueprint_outputs: | |
| remapped_path_arg = remapped_path_arg | |
| result_args.append('%s$(location %s)' % (special_prefix, remapped_path_arg)) | |
| elif os.path.basename(remapped_path_arg) in blueprint_outputs: | |
| remapped_path_arg = remapped_path_arg | |
| result_args.append('%s$(location %s)' % | |
| (special_prefix, os.path.basename(remapped_path_arg))) | |
| else: | |
| remapped_path_arg = special_prefix + remapped_path_arg | |
| result_args.append(remapped_path_arg) | |
| return result_args | |
| blueprint_gen_types = { | |
| "action": "cc_genrule", | |
| } | |
| blueprint_gen_rust_ffi_types = { | |
| "action_foreach": "genrule", | |
| } | |
| inputs_blocklist = [ | |
| '//.git/HEAD', | |
| ] | |
| outputs_remap = { | |
| 'build_info.h': 'glslang/build_info.h', | |
| } | |
| def is_input_in_tool_files(tool_files, input): | |
| return input in tool_files | |
| def separate_py_files_from_bp_srcs_entry(input_list): | |
| bp_tools_list = [] # Initialize the new list for ".py" items | |
| remaining_gn_input_list = [] # Initialize the list for the items that are not ".py" | |
| for item in input_list: | |
| if item.endswith(".py"): | |
| bp_tools_list.append(item) | |
| else: | |
| remaining_gn_input_list.append(item) | |
| return remaining_gn_input_list, bp_tools_list | |
| # special handling the {{response_file_name}} args in GN: | |
| # see https://gn.googlesource.com/gn/+/main/docs/reference.md#var_response_file_contents | |
| # in GN, if we use response_file_contents, the GN build system will automatically | |
| # write contents specified in response_file_contents arg into a temporary file | |
| # identified by {{response_file_name}}. However, Android blueprint does not have | |
| # the matching machanism. Android blueprint does automatically generate the | |
| # temporary file and does not recognize '{{response_file_name}}'. | |
| # To solve the problem: | |
| # 1) replace the '{{response_file_name}}' in command argument with the new | |
| # temporary file name. | |
| # 2) write the content specified in 'response_file_contents' to the new temporary | |
| # file | |
| # This function completes step 1) above. It checks if there are | |
| # '{{response_file_name}}' used in the command arguments. If there are, | |
| # the function replaces the '{{response_file_name}}' with the new temp file | |
| # named 'gn_response_file', and returns the new temp file to indicate | |
| # we need to complete step 2) | |
| def handle_gn_build_arg_response_file_name(command_arg_list): | |
| new_temp_file_name = None | |
| updated_args = command_arg_list[:] | |
| for index, arg in enumerate(updated_args): | |
| if arg == '{{response_file_name}}': | |
| new_temp_file_name = '$(genDir)/gn_response_file' | |
| updated_args[index] = new_temp_file_name | |
| return new_temp_file_name, updated_args | |
| def add_rust_ffi_to_map(gn_rust_ffi_output_list, android_bp_rust_ffi_output, | |
| android_bp_rust_ffi_gen_target): | |
| for gn_rust_ffi_output in gn_rust_ffi_output_list: | |
| if gn_rust_ffi_output.endswith(android_bp_rust_ffi_output): | |
| rust_ffi_gen_output_map[gn_rust_ffi_output] = android_bp_rust_ffi_gen_target | |
| return | |
| def action_foreach_target_to_blueprint(abi, target, build_info) -> list[tuple[str, dict]]: | |
| target_info = build_info[abi][target] | |
| blueprint_type = blueprint_gen_rust_ffi_types[target_info['type']] | |
| output_rust_ffi_list = target_info['outputs'] | |
| bp_list = [] | |
| for rust_source in target_info['sources']: | |
| blueprint_source_path = gn_path_to_blueprint_path(rust_source) | |
| rust_ffi_header_bp = {'name': get_rust_ffi_header_file_gen_target_name(rust_source)} | |
| rust_ffi_header_bp['tools'] = ['cxxbridge'] | |
| rust_ffi_header_bp['cmd'] = '$(location cxxbridge) $(in) --header > $(out)' | |
| rust_ffi_header_bp['srcs'] = [blueprint_source_path] | |
| rust_ffi_header_bp['out'] = [blueprint_source_path[len("src/"):] + ".h"] | |
| bp_list.append((blueprint_type, rust_ffi_header_bp)) | |
| add_rust_ffi_to_map(output_rust_ffi_list, rust_ffi_header_bp['out'][0], | |
| rust_ffi_header_bp['name']) | |
| rust_ffi_source_bp = {'name': get_rust_ffi_source_file_gen_target_name(rust_source)} | |
| rust_ffi_source_bp['tools'] = ['cxxbridge'] | |
| rust_ffi_source_bp['cmd'] = '$(location cxxbridge) $(in) > $(out)' | |
| rust_ffi_source_bp['srcs'] = [blueprint_source_path] | |
| rust_ffi_source_bp['out'] = [blueprint_source_path[len("src/"):] + ".cc"] | |
| bp_list.append((blueprint_type, rust_ffi_source_bp)) | |
| add_rust_ffi_to_map(output_rust_ffi_list, rust_ffi_source_bp['out'][0], | |
| rust_ffi_source_bp['name']) | |
| return bp_list | |
| def action_target_to_blueprint(abi, target, build_info) -> list[tuple[str, dict]]: | |
| target_info = build_info[abi][target] | |
| blueprint_type = blueprint_gen_types[target_info['type']] | |
| bp = {'name': gn_target_to_blueprint_target(target, target_info)} | |
| # Blueprints use only one 'srcs', merge all gn inputs into one list. | |
| gn_inputs = [] | |
| if 'inputs' in target_info: | |
| for input in target_info['inputs']: | |
| if input not in inputs_blocklist: | |
| gn_inputs.append(input) | |
| if 'sources' in target_info: | |
| gn_inputs += target_info['sources'] | |
| # Filter out the 'script' entry since Android.bp doesn't like the duplicate entries | |
| if 'script' in target_info: | |
| gn_inputs = [ | |
| input for input in gn_inputs | |
| if not is_input_in_tool_files(target_info['script'], input) | |
| ] | |
| # Take "*.py" out from gn_inputs, they need to be listed in tool_files entry | |
| # in Android.bp. | |
| gn_inputs_without_python_script = gn_inputs | |
| extra_tool_files = [] | |
| if 'script' in target_info: | |
| gn_inputs_without_python_script, extra_tool_files = separate_py_files_from_bp_srcs_entry( | |
| gn_inputs) | |
| bp_srcs = gn_paths_to_blueprint_paths(gn_inputs_without_python_script) | |
| bp['srcs'] = bp_srcs | |
| # genrules generate the output right into the 'root' directory. Strip any path before the | |
| # file name. | |
| bp_outputs = [] | |
| for gn_output in target_info['outputs']: | |
| output = os.path.basename(gn_output) | |
| if output in outputs_remap.keys(): | |
| output = outputs_remap[output] | |
| bp_outputs.append(output) | |
| bp['out'] = bp_outputs | |
| bp['tool_files'] = [gn_path_to_blueprint_path(target_info['script'])] | |
| if 'script' in target_info: | |
| # if there are more than 1 python script listed in "tool_files" entry in | |
| # Android.bp action target, we need to specify which script we are | |
| # invoking. | |
| if len(extra_tool_files) > 0: | |
| location = '$(location ' + target_info['script'].lstrip('/') + ')' | |
| else: | |
| location = '$(location)' | |
| bp['tool_files'] += gn_paths_to_blueprint_paths(extra_tool_files) | |
| else: | |
| location = '$(location)' | |
| new_temporary_gn_response_file, updated_args = handle_gn_build_arg_response_file_name( | |
| target_info['args']) | |
| if new_temporary_gn_response_file: | |
| # add the command 'echo $(in) > $(genDir)/gn_response_file' to | |
| # write $response_file_contents into the new_temporary_gn_response_file. | |
| cmd = ['echo $(in) >', new_temporary_gn_response_file, '&&', location | |
| ] + gn_action_args_to_blueprint_args(bp_srcs, bp_outputs, updated_args) | |
| else: | |
| cmd = [location] + gn_action_args_to_blueprint_args(bp_srcs, bp_outputs, | |
| target_info['args']) | |
| bp['cmd'] = ' '.join(cmd) | |
| bp['sdk_version'] = CURRENT_SDK_VERSION | |
| return [(blueprint_type, bp)] | |
| def gn_target_to_blueprint(target, build_info) -> list[tuple[str, dict]]: | |
| for abi in ABI_TARGETS: | |
| gn_type = build_info[abi][target]['type'] | |
| if gn_type in blueprint_library_target_types: | |
| return library_target_to_blueprint(target, build_info) | |
| elif gn_type in blueprint_gen_types: | |
| return action_target_to_blueprint(abi, target, build_info) | |
| elif gn_type in blueprint_rust_library_target_types: | |
| return rustlibrary_target_to_blueprint(target, build_info) | |
| elif is_gn_target_rust_ffi(gn_type, build_info[abi][target]): | |
| return action_foreach_target_to_blueprint(abi, target, build_info) | |
| else: | |
| # Target is not supported by this ABI | |
| continue | |
| @functools.lru_cache(maxsize=None) | |
| def get_gn_target_dependencies(abi, target, build_info): | |
| result = collections.OrderedDict() | |
| action_foreach_result = collections.OrderedDict() | |
| if build_info[abi][target]['type'] == 'action_foreach': | |
| action_foreach_result[target] = 1 | |
| else: | |
| result[target] = 1 | |
| for dep in build_info[abi][target]['deps']: | |
| if dep in target_blockist: | |
| # Blocklisted dep | |
| continue | |
| if dep not in build_info[abi]: | |
| # No info for this dep, skip it | |
| continue | |
| # Recurse | |
| action_for_each_targets, other_targets = get_gn_target_dependencies(abi, dep, build_info) | |
| action_foreach_result.update(action_for_each_targets) | |
| result.update(other_targets) | |
| return action_foreach_result, result | |
| def get_angle_in_vendor_flag_config(): | |
| blueprint_results = [] | |
| blueprint_results.append(('soong_config_module_type', { | |
| 'name': 'angle_config_cc_defaults', | |
| 'module_type': 'cc_defaults', | |
| 'config_namespace': 'angle', | |
| 'bool_variables': ['angle_in_vendor',], | |
| 'properties': [ | |
| 'target.android.relative_install_path', | |
| 'vendor', | |
| ], | |
| })) | |
| blueprint_results.append(('soong_config_bool_variable', { | |
| 'name': 'angle_in_vendor', | |
| })) | |
| blueprint_results.append(( | |
| 'angle_config_cc_defaults', | |
| { | |
| 'name': 'angle_vendor_cc_defaults', | |
| 'vendor': False, | |
| 'target': { | |
| 'android': { | |
| # Android EGL loader can not load from /system/egl/${LIB} | |
| # path and hence don't set the relative path so that ANGLE | |
| # libraries get built into /system/${LIB} | |
| 'relative_install_path': '', | |
| }, | |
| }, | |
| 'soong_config_variables': { | |
| 'angle_in_vendor': { | |
| 'vendor': True, | |
| 'target': { | |
| 'android': { | |
| 'relative_install_path': 'egl', | |
| }, | |
| }, | |
| }, | |
| }, | |
| })) | |
| return blueprint_results | |
| def get_angle_android_dma_buf_flag_config(build_info): | |
| """ | |
| Generates a list of Android.bp definitions for angle_android_dma_buf flag. | |
| """ | |
| blueprint_results = [] | |
| blueprint_results.append(('soong_config_module_type', { | |
| 'name': 'angle_dma_buf_config_cc_defaults', | |
| 'module_type': 'cc_defaults', | |
| 'config_namespace': 'angle', | |
| 'bool_variables': ['angle_android_dma_buf'], | |
| 'properties': ['defaults'] | |
| })) | |
| blueprint_results.append(('soong_config_bool_variable', { | |
| 'name': 'angle_android_dma_buf', | |
| })) | |
| blueprint_results.append(('angle_dma_buf_config_cc_defaults', { | |
| BLUEPRINT_COMMENT_PROPERTY: | |
| ('Note: this is a no-op for most builds, only applies to products that explicitly ' | |
| 'enable the angle_android_dma_buf config flag. See b/353262025 for details of the ' | |
| 'products that use it.'), | |
| 'name': 'angle_dma_buf_cc_defaults', | |
| 'soong_config_variables': { | |
| 'angle_android_dma_buf': { | |
| 'defaults': [gn_target_to_blueprint_target(DMA_BUF_TARGET, {})], | |
| } | |
| }, | |
| })) | |
| return blueprint_results | |
| # returns list of (blueprint module type, dict with contents) | |
| def get_blueprint_targets_from_build_info(build_info: BuildInfo) -> List[Tuple[str, dict]]: | |
| non_action_foreach_targets_to_write = collections.OrderedDict() | |
| action_foreach_targets_to_write = collections.OrderedDict() | |
| for abi in ABI_TARGETS: | |
| for root_target in ROOT_TARGETS + [END2END_TEST_TARGET, DMA_BUF_TARGET]: | |
| action_foreach_targets, non_action_foreach_targets = get_gn_target_dependencies( | |
| abi, root_target, build_info) | |
| action_foreach_targets_to_write.update(action_foreach_targets) | |
| non_action_foreach_targets_to_write.update(non_action_foreach_targets) | |
| generated_targets = [] | |
| # Process target whose type is action_foreach | |
| # We need to insert generated header and source to rust_ffi_gen_output_map hash map first | |
| # before processing other targets | |
| for action_foreach_target in action_foreach_targets_to_write.keys(): | |
| generated_target_list = gn_target_to_blueprint(action_foreach_target, build_info) | |
| generated_targets.extend(generated_target_list) | |
| for target in reversed(non_action_foreach_targets_to_write.keys()): | |
| # Do not export angle_commit_id target in Android.bp, because the script | |
| # src/commit_id.py invoked by this target can't guarantee to generate a | |
| # meaningful ANGLE git hash during compile time, see b/348044346. | |
| # The script src/commit_id.py will be invoked by roll_aosp.h during | |
| # ANGLE to Android roll time, and the ANGLE git hash will be output in | |
| # {AndroidANGLERoot}/angle_commmit.h. | |
| if target == '//:angle_commit_id': | |
| continue | |
| generated_target_list = gn_target_to_blueprint(target, build_info) | |
| generated_targets.extend(generated_target_list) | |
| return generated_targets | |
| def handle_angle_non_conformant_extensions_and_versions( | |
| generated_targets: List[Tuple[str, dict]], | |
| blueprint_targets: List[dict], | |
| ): | |
| """Replace the non conformant cflags with a separate cc_defaults. | |
| The downstream can custom the cflags easier. | |
| """ | |
| non_conform_cflag = '-DANGLE_EXPOSE_NON_CONFORMANT_EXTENSIONS_AND_VERSIONS' | |
| non_conform_defaults = 'angle_non_conformant_extensions_and_versions_cflags' | |
| blueprint_targets.append(('cc_defaults', { | |
| 'name': non_conform_defaults, | |
| 'cflags': [non_conform_cflag], | |
| })) | |
| for _, bp in generated_targets: | |
| if 'cflags' in bp and non_conform_cflag in bp['cflags']: | |
| bp['cflags'] = list(set(bp['cflags']) - {non_conform_cflag}) | |
| bp['defaults'].append(non_conform_defaults) | |
| def fix_platform_default_defines(cflags: list[str]) -> None: | |
| """Avoids macro redefinition errors for flags specified by Android. | |
| Android may specify platform defaults for macros like `_FORTIFY_SOURCE` and | |
| `_LIBCPP_HARDENING_MODE`. If both Android and Angle provide different values | |
| for these (e.g., `-D_FORTIFY_SOURCE=3 -D_FORTIFY_SOURCE=2`), Clang will emit | |
| errors about macro redefinitions. | |
| This inserts `-U${FLAG}` before Angle's definitions, to avoid those errors. | |
| """ | |
| flags_to_check = ( | |
| '_FORTIFY_SOURCE', | |
| '_LIBCPP_HARDENING_MODE', | |
| ) | |
| for flag in flags_to_check: | |
| d_flag = f'-D{flag}' | |
| # It's _technically_ possible that we have multiple instances of these | |
| # flags in a single command. If these flags _differ_ in value, they | |
| # should already be a build error, so we only need to worry about adding | |
| # the `-U` flag before the first. | |
| flag_index = next((i for i, cflag in enumerate(cflags) if d_flag in cflag), None) | |
| if flag_index is not None: | |
| cflags.insert(flag_index, f'-U{flag}') | |
| def main(): | |
| parser = argparse.ArgumentParser( | |
| description='Generate Android blueprints from gn descriptions.') | |
| for abi in ABI_TARGETS: | |
| parser.add_argument( | |
| '--gn_json_' + gn_abi(abi), | |
| help=gn_abi(abi) + | |
| ' gn desc file in json format. Generated with \'gn desc <out_dir> --format=json "*"\'.', | |
| required=True) | |
| parser.add_argument('--output', help='output file (e.g. Android.bp)') | |
| args = vars(parser.parse_args()) | |
| infos = {} | |
| for abi in ABI_TARGETS: | |
| with open(args['gn_json_' + gn_abi(abi)], 'r') as f: | |
| infos[abi] = json.load(f) | |
| build_info = BuildInfo(infos) | |
| blueprint_targets = [] | |
| blueprint_targets.extend(get_angle_in_vendor_flag_config()) | |
| blueprint_targets.extend(get_angle_android_dma_buf_flag_config(build_info)) | |
| blueprint_targets.append(( | |
| 'cc_defaults', | |
| { | |
| 'name': | |
| 'angle_common_library_cflags', | |
| 'cflags': [ | |
| # Chrome and Android use different versions of Clang which support differnt warning options. | |
| # Ignore errors about unrecognized warning flags. | |
| '-Wno-unknown-warning-option', | |
| '-O2', | |
| # Override AOSP build flags to match ANGLE's CQ testing and reduce binary size | |
| '-fno-unwind-tables', | |
| # Disable stack protector to reduce cpu overhead. | |
| '-fno-stack-protector', | |
| ], | |
| })) | |
| generated_targets = get_blueprint_targets_from_build_info(build_info) | |
| # Will modify generated_targets and blueprint_targets in-place to handle the | |
| # angle_expose_non_conformant_extensions_and_versions gn argument. | |
| handle_angle_non_conformant_extensions_and_versions(generated_targets, blueprint_targets) | |
| # Move cflags that are repeated in each target to cc_defaults | |
| # 1. Identify common cflags across all generated targets. | |
| targets_with_cflags = [bp for _, bp in generated_targets if 'cflags' in bp] | |
| if targets_with_cflags: | |
| common_cflags = set.intersection(*(set(bp['cflags']) for bp in targets_with_cflags)) | |
| else: | |
| common_cflags = set() | |
| # 2. Identify common arch-specific cflags. | |
| common_arch_cflags = {} | |
| for abi in ABI_TARGETS: | |
| targets_with_arch_cflags = [ | |
| bp for _, bp in generated_targets | |
| if 'arch' in bp and abi in bp['arch'] and 'cflags' in bp['arch'][abi] | |
| ] | |
| if targets_with_arch_cflags: | |
| common_arch_cflags[abi] = set.intersection( | |
| *(set(bp['arch'][abi]['cflags']) for bp in targets_with_arch_cflags)) | |
| else: | |
| common_arch_cflags[abi] = set() | |
| # 3. Move these common flags to angle_common_auto_cflags and remove from individual targets. | |
| for _, bp in generated_targets: | |
| if 'cflags' in bp: | |
| bp['cflags'] = list(set(bp['cflags']) - common_cflags) | |
| # Every generated target that had cflags should inherit from angle_common_auto_cflags | |
| if 'angle_common_auto_cflags' not in bp.get('defaults', []): | |
| bp.setdefault('defaults', []).append('angle_common_auto_cflags') | |
| for abi, flags in common_arch_cflags.items(): | |
| if flags and 'arch' in bp and abi in bp['arch'] and 'cflags' in bp['arch'][abi]: | |
| bp['arch'][abi]['cflags'] = list(set(bp['arch'][abi]['cflags']) - flags) | |
| # Cleanup empty structures | |
| if not bp['arch'][abi]['cflags']: | |
| del bp['arch'][abi]['cflags'] | |
| if not bp['arch'][abi]: | |
| del bp['arch'][abi] | |
| if not bp['arch']: | |
| del bp['arch'] | |
| # 4. Create the consolidated cc_defaults | |
| auto_cflags_bp = { | |
| 'name': 'angle_common_auto_cflags', | |
| 'cflags': list(common_cflags), | |
| } | |
| if any(common_arch_cflags.values()): | |
| auto_cflags_bp['arch'] = {} | |
| for abi, flags in common_arch_cflags.items(): | |
| if flags: | |
| auto_cflags_bp['arch'][abi] = {'cflags': list(flags)} | |
| blueprint_targets.append(('cc_defaults', auto_cflags_bp)) | |
| blueprint_targets.extend(generated_targets) | |
| # Add license build rules | |
| blueprint_targets.append(('package', { | |
| 'default_applicable_licenses': ['external_angle_license'], | |
| })) | |
| blueprint_targets.append(('license', { | |
| 'name': | |
| 'external_angle_license', | |
| 'visibility': [':__subpackages__'], | |
| 'license_kinds': [ | |
| 'SPDX-license-identifier-Apache-2.0', | |
| 'SPDX-license-identifier-BSD', | |
| 'SPDX-license-identifier-GPL', | |
| 'SPDX-license-identifier-GPL-2.0', | |
| 'SPDX-license-identifier-GPL-3.0', | |
| 'SPDX-license-identifier-LGPL', | |
| 'SPDX-license-identifier-MIT', | |
| 'SPDX-license-identifier-Zlib', | |
| 'legacy_unencumbered', | |
| ], | |
| 'license_text': [ | |
| 'LICENSE', | |
| 'src/common/third_party/xxhash/src/LICENSE', | |
| 'src/libANGLE/renderer/vulkan/shaders/src/third_party/ffx_spd/src/LICENSE', | |
| 'src/tests/test_utils/third_party/LICENSE', | |
| 'src/third_party/libxnvctrl/LICENSE', | |
| 'src/third_party/volk/src/LICENSE.md', | |
| 'third_party/abseil-cpp/LICENSE', | |
| 'third_party/glslang/LICENSE', | |
| 'third_party/glslang/src/LICENSE.txt', | |
| 'third_party/re2/LICENSE', | |
| 'third_party/spirv-headers/LICENSE', | |
| 'third_party/spirv-headers/src/LICENSE', | |
| 'third_party/spirv-tools/LICENSE', | |
| 'third_party/spirv-tools/src/LICENSE', | |
| 'third_party/spirv-tools/src/utils/vscode/src/lsp/LICENSE', | |
| 'third_party/vulkan-headers/LICENSE.txt', | |
| 'third_party/vulkan-headers/src/LICENSE.md', | |
| 'third_party/vulkan_memory_allocator/LICENSE.txt', | |
| 'tools/flex-bison/third_party/m4sugar/LICENSE', | |
| 'tools/flex-bison/third_party/skeletons/LICENSE', | |
| 'util/windows/third_party/StackWalker/LICENSE', | |
| ], | |
| })) | |
| # Add APKs with all of the root libraries and permissions xml | |
| blueprint_targets.append(( | |
| 'filegroup', | |
| { | |
| 'name': | |
| 'ANGLE_srcs', | |
| # We only need EmptyMainActivity.java since we just need to be able to reply to the intent | |
| # android.app.action.ANGLE_FOR_ANDROID to indicate ANGLE is present on the device. | |
| # However, the internal branch currently uses these files with patches in that branch. | |
| 'srcs': [ | |
| 'src/android_system_settings/src/com/android/angle/MainActivity.java', | |
| 'src/android_system_settings/src/com/android/angle/common/AngleRuleHelper.java', | |
| 'src/android_system_settings/src/com/android/angle/common/GlobalSettings.java', | |
| 'src/android_system_settings/src/com/android/angle/common/MainFragment.java', | |
| 'src/android_system_settings/src/com/android/angle/common/Receiver.java', | |
| 'src/android_system_settings/src/com/android/angle/common/SearchProvider.java', | |
| ], | |
| })) | |
| blueprint_targets.append(('prebuilt_etc', { | |
| 'name': 'android.software.angle.xml', | |
| 'src': 'android/android.software.angle.xml', | |
| 'product_specific': True, | |
| 'sub_dir': 'permissions', | |
| })) | |
| blueprint_targets.append(('android_library', { | |
| 'name': 'ANGLE_library', | |
| 'sdk_version': 'system_current', | |
| 'target_sdk_version': TARGET_SDK_VERSION, | |
| 'min_sdk_version': MIN_SDK_VERSION, | |
| 'resource_dirs': ['src/android_system_settings/res',], | |
| 'asset_dirs': ['src/android_system_settings/assets',], | |
| 'aaptflags': ['-0 .json',], | |
| 'manifest': 'src/android_system_settings/src/com/android/angle/AndroidManifest.xml', | |
| 'static_libs': ['androidx.preference_preference',], | |
| })) | |
| blueprint_targets.append(( | |
| 'java_defaults', | |
| { | |
| 'name': 'ANGLE_java_settings_defaults', | |
| 'sdk_version': 'system_current', | |
| 'target_sdk_version': TARGET_SDK_VERSION, | |
| 'min_sdk_version': MIN_SDK_VERSION, | |
| 'compile_multilib': 'both', | |
| 'use_embedded_native_libs': True, | |
| 'aaptflags': [ | |
| '-0 .json', # Don't compress *.json files | |
| "--extra-packages com.android.angle.common", | |
| ], | |
| 'srcs': [':ANGLE_srcs'], | |
| 'privileged': True, | |
| 'product_specific': True, | |
| 'owner': 'google', | |
| 'required': ['android.software.angle.xml'], | |
| })) | |
| blueprint_targets.append(('android_app', { | |
| 'name': 'ANGLE_settings', | |
| 'defaults': ['ANGLE_java_settings_defaults'], | |
| 'manifest': 'src/android_system_settings/src/com/android/angle/AndroidManifest.xml', | |
| 'static_libs': ['ANGLE_library'], | |
| 'optimize': { | |
| 'enabled': True, | |
| 'shrink': True, | |
| }, | |
| 'asset_dirs': ['src/android_system_settings/assets',], | |
| })) | |
| output = [ | |
| """// GENERATED FILE - DO NOT EDIT. | |
| // Generated by %s | |
| // | |
| // Copyright 2020 The ANGLE Project Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| //""" % sys.argv[0] | |
| ] | |
| for (blueprint_type, blueprint_data) in blueprint_targets: | |
| write_blueprint(output, blueprint_type, blueprint_data) | |
| with open(args['output'], 'w') as f: | |
| f.write('\n'.join(output) + '\n') | |
| if __name__ == '__main__': | |
| sys.exit(main()) |