1#!/usr/bin/env python3 2 3import argparse 4import os 5import time 6 7from utils import ( 8 AOSP_DIR, SOURCE_ABI_DUMP_EXT_END, SO_EXT, BuildTarget, Arch, 9 copy_reference_dump, find_lib_lsdumps, get_build_vars, 10 make_libraries, make_targets, read_lsdump_paths) 11 12 13PRODUCTS_DEFAULT = ['aosp_arm', 'aosp_arm64', 'aosp_x86', 'aosp_x86_64'] 14 15PREBUILTS_ABI_DUMPS_DIR = os.path.join(AOSP_DIR, 'prebuilts', 'abi-dumps') 16PREBUILTS_ABI_DUMPS_SUBDIRS = ('ndk', 'platform', 'vndk') 17KNOWN_TAGS = {'APEX', 'LLNDK', 'NDK', 'PLATFORM', 'VENDOR', 'PRODUCT'} 18NON_AOSP_TAGS = {'VENDOR', 'PRODUCT'} 19 20SOONG_DIR = os.path.join(AOSP_DIR, 'out', 'soong', '.intermediates') 21 22 23class GetRefDumpDirStem: 24 def __init__(self, ref_dump_dir): 25 self.ref_dump_dir = ref_dump_dir 26 27 def __call__(self, subdir, arch_str): 28 return os.path.join(self.ref_dump_dir, arch_str) 29 30 31class GetVersionedRefDumpDirStem: 32 def __init__(self, board_api_level, chosen_platform_version, 33 binder_bitness): 34 self.board_api_level = board_api_level 35 self.chosen_platform_version = chosen_platform_version 36 self.binder_bitness = binder_bitness 37 38 def __call__(self, subdir, arch_str): 39 if subdir not in PREBUILTS_ABI_DUMPS_SUBDIRS: 40 raise ValueError(f'"{subdir}" is not a valid dump directory under ' 41 f'{PREBUILTS_ABI_DUMPS_DIR}.') 42 version_stem = (self.board_api_level if subdir == 'vndk' else 43 self.chosen_platform_version) 44 return os.path.join(PREBUILTS_ABI_DUMPS_DIR, subdir, version_stem, 45 self.binder_bitness, arch_str) 46 47 48class LsdumpFilter: 49 def __init__(self, include_names, include_tags, exclude_tags): 50 self.include_names = include_names 51 self.include_tags = include_tags 52 self.exclude_tags = exclude_tags 53 54 def __call__(self, tag, lib_name): 55 """Determine whether to dump the library. 56 57 lib_name does not contain '.so'. 58 """ 59 if self.include_names and lib_name not in self.include_names: 60 return False 61 if self.include_tags and tag not in self.include_tags: 62 return False 63 if tag in self.exclude_tags: 64 return False 65 return True 66 67 68def tag_to_dir_name(tag): 69 if tag in NON_AOSP_TAGS: 70 return '' 71 if tag == 'NDK': 72 return 'ndk' 73 if tag in ('APEX', 'PLATFORM'): 74 return 'platform' 75 if tag == 'LLNDK': 76 return 'vndk' 77 raise ValueError(tag + ' is not a known tag.') 78 79 80def find_and_copy_lib_lsdumps(get_ref_dump_dir_stem, arch, libs, 81 lsdump_paths): 82 arch_lsdump_paths = find_lib_lsdumps(lsdump_paths, libs, arch) 83 num_created = 0 84 for tag, path in arch_lsdump_paths: 85 ref_dump_dir_stem = get_ref_dump_dir_stem(tag_to_dir_name(tag), 86 arch.get_arch_str()) 87 copy_reference_dump( 88 path, os.path.join(ref_dump_dir_stem, 'source-based')) 89 num_created += 1 90 return num_created 91 92 93def create_source_abi_reference_dumps(args, get_ref_dump_dir_stem, 94 lsdump_paths, arches): 95 num_libs_copied = 0 96 for arch in arches: 97 assert arch.primary_arch != '' 98 print(f'Creating dumps for arch: {arch.arch}, ' 99 f'primary arch: {arch.primary_arch}') 100 101 num_libs_copied += find_and_copy_lib_lsdumps( 102 get_ref_dump_dir_stem, arch, args.libs, lsdump_paths) 103 return num_libs_copied 104 105 106def create_source_abi_reference_dumps_for_all_products(args): 107 """Create reference ABI dumps for all specified products.""" 108 num_processed = 0 109 110 for product in args.products: 111 build_target = BuildTarget(product, args.release, args.build_variant) 112 ( 113 release_board_api_level, binder_32_bit, 114 platform_version_codename, platform_sdk_version, 115 ) = get_build_vars( 116 ['RELEASE_BOARD_API_LEVEL', 'BINDER32BIT', 117 'PLATFORM_VERSION_CODENAME', 'PLATFORM_SDK_VERSION'], 118 build_target 119 ) 120 if binder_32_bit == 'true': 121 binder_bitness = '32' 122 else: 123 binder_bitness = '64' 124 125 # chosen_platform_version is expected to be the finalized 126 # PLATFORM_SDK_VERSION if the codename is REL. 127 chosen_platform_version = (platform_sdk_version 128 if platform_version_codename == 'REL' 129 else 'current') 130 131 arches = [arch for arch in 132 (Arch(True, build_target), Arch(False, build_target)) 133 if arch.arch] 134 135 if args.ref_dump_dir: 136 get_ref_dump_dir_stem = GetRefDumpDirStem(args.ref_dump_dir) 137 exclude_tags = set() 138 else: 139 get_ref_dump_dir_stem = GetVersionedRefDumpDirStem( 140 release_board_api_level, chosen_platform_version, 141 binder_bitness) 142 exclude_tags = NON_AOSP_TAGS 143 144 lsdump_filter = LsdumpFilter(args.libs, args.include_tags, 145 exclude_tags) 146 147 try: 148 if not args.no_make_lib: 149 print('making libs for', '-'.join(filter(None, build_target))) 150 if args.libs: 151 make_libraries(build_target, arches, args.libs, 152 lsdump_filter) 153 elif args.include_tags: 154 make_targets( 155 build_target, 156 ['findlsdumps_' + tag for tag in args.include_tags]) 157 else: 158 make_targets(build_target, ['findlsdumps']) 159 160 lsdump_paths = read_lsdump_paths(build_target, arches, 161 lsdump_filter, build=False) 162 163 num_processed += create_source_abi_reference_dumps( 164 args, get_ref_dump_dir_stem, lsdump_paths, arches) 165 except KeyError as e: 166 if args.libs or not args.ref_dump_dir: 167 raise RuntimeError('Please check the lib name, --lib-variant ' 168 'and -ref-dump-dir if you are updating ' 169 'reference dumps for product or vendor ' 170 'libraries.') from e 171 raise 172 173 return num_processed 174 175 176def _parse_args(): 177 """Parse the command line arguments.""" 178 179 parser = argparse.ArgumentParser() 180 parser.add_argument('--no-make-lib', action='store_true', 181 help='skip building dumps while creating references') 182 parser.add_argument('--lib', '-libs', action='append', 183 dest='libs', metavar='LIB', 184 help='libs to create references for') 185 parser.add_argument('--product', '-products', action='append', 186 dest='products', metavar='PRODUCT', 187 help='products to create references for') 188 parser.add_argument('--release', '-release', 189 help='release configuration to create references for. ' 190 'e.g., trunk_staging, next.') 191 parser.add_argument('--build-variant', default='userdebug', 192 help='build variant to create references for') 193 parser.add_argument('--lib-variant', action='append', dest='include_tags', 194 default=[], choices=KNOWN_TAGS, 195 help='library variant to create references for.') 196 parser.add_argument('--ref-dump-dir', '-ref-dump-dir', 197 help='directory to copy reference abi dumps into') 198 args = parser.parse_args() 199 200 if args.libs: 201 if any(lib_name.endswith(SOURCE_ABI_DUMP_EXT_END) or 202 lib_name.endswith(SO_EXT) for lib_name in args.libs): 203 parser.error('--lib should be followed by a base name without ' 204 'file extension.') 205 206 if NON_AOSP_TAGS.intersection(args.include_tags) and not args.libs: 207 parser.error('--lib must be given if --lib-variant is any of ' + 208 str(NON_AOSP_TAGS)) 209 210 if args.ref_dump_dir and not args.libs: 211 parser.error('--lib must be given if --ref-dump-dir is given.') 212 213 if args.ref_dump_dir and len(args.include_tags) != 1: 214 print('WARNING: Exactly one --lib-variant should be specified if ' 215 '--ref-dump-dir is given.') 216 217 if args.products is None: 218 # If `args.products` is unspecified, generate reference ABI dumps for 219 # all products. 220 args.products = PRODUCTS_DEFAULT 221 222 return args 223 224 225def main(): 226 args = _parse_args() 227 228 # Clear SKIP_ABI_CHECKS as it forbids ABI dumps from being built. 229 os.environ.pop('SKIP_ABI_CHECKS', None) 230 231 if os.environ.get('KEEP_VNDK') == 'true': 232 raise RuntimeError('KEEP_VNDK is not supported. Please undefine it.') 233 234 start = time.time() 235 num_processed = create_source_abi_reference_dumps_for_all_products(args) 236 end = time.time() 237 238 print() 239 print('msg: Processed', num_processed, 'libraries in ', (end - start) / 60, 240 ' minutes') 241 242 243if __name__ == '__main__': 244 main() 245