1#!/usr/bin/env python3 2 3# This tool updates extracts the information from Android.bp and updates the 4# datasets for eligible VNDK libraries. 5 6import argparse 7import collections 8import csv 9import json 10import os.path 11import posixpath 12import re 13import sys 14 15def load_make_vars(path): 16 result = collections.OrderedDict([ 17 ('SOONG_LLNDK_LIBRARIES', set()), 18 ('SOONG_VNDK_SAMEPROCESS_LIBRARIES', set()), 19 ('SOONG_VNDK_CORE_LIBRARIES', set()), 20 ('SOONG_VNDK_PRIVATE_LIBRARIES', set()), 21 ]) 22 23 assign_len = len(' := ') 24 25 with open(path, 'r') as fp: 26 for line in fp: 27 for key, value in result.items(): 28 if line.startswith(key): 29 mod_names = line[len(key) + assign_len:].strip().split(' ') 30 value.update(mod_names) 31 32 return result.values() 33 34def load_install_paths(module_info_path): 35 with open(module_info_path, 'r') as fp: 36 data = json.load(fp) 37 38 result = set() 39 name_path_dict = {} 40 patt = re.compile( 41 '.*[\\\\/]target[\\\\/]product[\\\\/][^\\\\/]+([\\\\/].*)$') 42 for name, module in data.items(): 43 for path in module['installed']: 44 match = patt.match(path) 45 if not match: 46 continue 47 path = match.group(1) 48 path = path.replace(os.path.sep, '/') 49 path = path.replace('/lib/', '/${LIB}/') 50 path = path.replace('/lib64/', '/${LIB}/') 51 path = re.sub('/vndk-sp(?:-[^/$]*)/', '/vndk-sp${VNDK_VER}/', path) 52 path = re.sub('/vndk(?:-[^/$]*)/', '/vndk${VNDK_VER}/', path) 53 result.add(path) 54 55 if name.endswith('_32'): 56 name = name[0:-3] 57 58 name_path_dict[name] = path 59 60 return (result, name_path_dict) 61 62def _is_stale_module(path, installed_paths): 63 if path in installed_paths: 64 return False 65 # libclang_rt.asan-${arch}-android and 66 # libclang_rt.ubsan_standalone-${arch}-android may vary between different 67 # architectures. 68 if posixpath.basename(path).startswith('libclang_rt'): 69 return False 70 return True 71 72def remove_stale_modules(data, installed_paths): 73 result = {} 74 for path, row in data.items(): 75 if not _is_stale_module(path, installed_paths): 76 result[path] = row 77 return result 78 79def _parse_args(): 80 parser = argparse.ArgumentParser() 81 parser.add_argument('tag_file') 82 parser.add_argument('-o', '--output', required=True) 83 parser.add_argument('--make-vars', required=True, 84 help='out/soong/make_vars-$(TARGET).mk') 85 parser.add_argument('--module-info', required=True, 86 help='out/target/product/$(TARGET)/module-info.json') 87 return parser.parse_args() 88 89def main(): 90 args = _parse_args() 91 92 # Load libraries from `out/soong/make_vars-$(TARGET).mk`. 93 llndk, vndk_sp, vndk, vndk_private = load_make_vars(args.make_vars) 94 95 # Load eligible list csv file. 96 with open(args.tag_file, 'r') as fp: 97 reader = csv.reader(fp) 98 header = next(reader) 99 data = dict() 100 regex_patterns = [] 101 for path, tag, comments in reader: 102 if path.startswith('[regex]'): 103 regex_patterns.append([path, tag, comments]) 104 else: 105 data[path] = [path, tag, comments] 106 107 # Delete non-existing libraries. 108 installed_paths, name_path_dict = load_install_paths(args.module_info) 109 data = remove_stale_modules(data, installed_paths) 110 111 # Reset all /system/${LIB} libraries to FWK-ONLY. 112 for path, row in data.items(): 113 if posixpath.dirname(path) == '/system/${LIB}': 114 row[1] = 'FWK-ONLY' 115 116 # Helper function to update the tag and the comments of an entry 117 def update_tag(path, tag, comments=None): 118 try: 119 data[path][1] = tag 120 if comments is not None: 121 data[path][2] = comments 122 except KeyError: 123 data[path] = [path, tag, comments if comments is not None else ''] 124 125 # Helper function to find the subdir and the module name 126 def get_subdir_and_name(name, name_path_dict, prefix_core, prefix_vendor): 127 try: 128 path = name_path_dict[name + '.vendor'] 129 assert path.startswith(prefix_vendor) 130 name_vendor = path[len(prefix_vendor):] 131 except KeyError: 132 name_vendor = name + '.so' 133 134 try: 135 path = name_path_dict[name] 136 assert path.startswith(prefix_core) 137 name_core = path[len(prefix_core):] 138 except KeyError: 139 name_core = name + '.so' 140 141 assert name_core == name_vendor 142 return name_core 143 144 # Update LL-NDK tags 145 prefix_core = '/system/${LIB}/' 146 for name in llndk: 147 try: 148 path = name_path_dict[name] 149 assert path.startswith(prefix_core) 150 name = path[len(prefix_core):] 151 except KeyError: 152 name = name + '.so' 153 update_tag('/system/${LIB}/' + name, 'LL-NDK') 154 155 # Update VNDK-SP and VNDK-SP-Private tags 156 prefix_core = '/system/${LIB}/' 157 prefix_vendor = '/system/${LIB}/vndk-sp${VNDK_VER}/' 158 159 for name in (vndk_sp - vndk_private): 160 name = get_subdir_and_name(name, name_path_dict, prefix_core, 161 prefix_vendor) 162 update_tag(prefix_core + name, 'VNDK-SP') 163 update_tag(prefix_vendor + name, 'VNDK-SP') 164 165 for name in (vndk_sp & vndk_private): 166 name = get_subdir_and_name(name, name_path_dict, prefix_core, 167 prefix_vendor) 168 update_tag(prefix_core + name, 'VNDK-SP-Private') 169 update_tag(prefix_vendor + name, 'VNDK-SP-Private') 170 171 # Update VNDK and VNDK-Private tags 172 prefix_core = '/system/${LIB}/' 173 prefix_vendor = '/system/${LIB}/vndk${VNDK_VER}/' 174 175 for name in (vndk - vndk_private): 176 name = get_subdir_and_name(name, name_path_dict, prefix_core, 177 prefix_vendor) 178 update_tag(prefix_core + name, 'VNDK') 179 update_tag(prefix_vendor + name, 'VNDK') 180 181 for name in (vndk & vndk_private): 182 name = get_subdir_and_name(name, name_path_dict, prefix_core, 183 prefix_vendor) 184 update_tag(prefix_core + name, 'VNDK-Private') 185 update_tag(prefix_vendor + name, 'VNDK-Private') 186 187 # Workaround for FWK-ONLY-RS 188 libs = [ 189 'libft2', 190 'libmediandk', 191 ] 192 for name in libs: 193 update_tag('/system/${LIB}/' + name + '.so', 'FWK-ONLY-RS') 194 195 # Workaround for LL-NDK APEX bionic 196 libs = [ 197 'libc', 198 'libdl', 199 'libm', 200 ] 201 for name in libs: 202 update_tag('/apex/com.android.runtime/${LIB}/bionic/' + name + '.so', 203 'LL-NDK') 204 205 # Workaround for LL-NDK-Private 206 libs = [ 207 'ld-android', 208 'libc_malloc_debug', 209 'libdl_android', 210 'libnetd_client', 211 'libtextclassifier_hash', 212 ] 213 for name in libs: 214 update_tag('/system/${LIB}/' + name + '.so', 'LL-NDK-Private') 215 216 # Workaround for libclang_rt.*.so 217 lib_sets = { 218 'LL-NDK': llndk, 219 'VNDK': vndk, 220 } 221 prefixes = { 222 'libclang_rt.asan': 'LL-NDK', 223 'libclang_rt.hwasan': 'LL-NDK', 224 'libclang_rt.scudo': 'VNDK', 225 'libclang_rt.ubsan_standalone': 'VNDK', 226 } 227 for prefix, tag in prefixes.items(): 228 if any(name.startswith(prefix) for name in lib_sets[tag]): 229 for path in list(data.keys()): 230 if os.path.basename(path).startswith(prefix): 231 update_tag(path, tag) 232 233 # Merge regular expression patterns into final dataset 234 for regex in regex_patterns: 235 data[regex[0]] = regex 236 237 # Write updated eligible list file 238 with open(args.output, 'w') as fp: 239 writer = csv.writer(fp, lineterminator='\n') 240 writer.writerow(header) 241 writer.writerows(sorted(data.values())) 242 243if __name__ == '__main__': 244 sys.exit(main()) 245