1#!/usr/bin/env python 2# 3# Copyright (C) 2017 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18import argparse 19import os 20import subprocess 21import sys 22 23import extract_lsdump 24 25 26def _ExecuteCommand(cmd, **kwargs): 27 """Executes a command and returns stdout. 28 29 Args: 30 cmd: A list of strings, the command to execute. 31 **kwargs: The arguments passed to subprocess.Popen. 32 33 Returns: 34 A string, the stdout. 35 """ 36 proc = subprocess.Popen( 37 cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs) 38 stdout, stderr = proc.communicate() 39 if proc.returncode: 40 sys.exit("Command failed: %s\nstdout=%s\nstderr=%s" % ( 41 cmd, stdout, stderr)) 42 if stderr: 43 print("Warning: cmd=%s\nstdout=%s\nstderr=%s" % (cmd, stdout, stderr)) 44 return stdout.strip() 45 46 47def GetBuildVariables(build_top_dir, abs_path, vars): 48 """Gets values of variables from build config. 49 50 Args: 51 build_top_dir: The path to root directory of Android source. 52 abs_path: A boolean, whether to convert the values to absolute paths. 53 vars: A list of strings, the names of the variables. 54 55 Returns: 56 A list of strings which are the values of the variables. 57 """ 58 cmd = ["build/soong/soong_ui.bash", "--dumpvars-mode", 59 ("--abs-vars" if abs_path else "--vars"), " ".join(vars)] 60 stdout = _ExecuteCommand(cmd, cwd=build_top_dir) 61 print(stdout) 62 return [line.split("=", 1)[1].strip("'") for line in stdout.splitlines()] 63 64 65def _LoadLibraryNamesFromTxt(vndk_lib_list_file): 66 """Loads VNDK and VNDK-SP library names from a VNDK library list. 67 68 Args: 69 vndk_lib_list_file: A file object of 70 build/make/target/product/vndk/current.txt 71 72 Returns: 73 A list of strings, the VNDK and VNDK-SP library names with vndk/vndk-sp 74 directory prefixes. 75 """ 76 tags = ( 77 ("VNDK-core: ", len("VNDK-core: "), False), 78 ("VNDK-SP: ", len("VNDK-SP: "), False), 79 ("VNDK-private: ", len("VNDK-private: "), True), 80 ("VNDK-SP-private: ", len("VNDK-SP-private: "), True), 81 ) 82 lib_names = set() 83 lib_names_exclude = set() 84 for line in vndk_lib_list_file: 85 for tag, tag_len, is_exclude in tags: 86 if line.startswith(tag): 87 lib_name = line[tag_len:].strip() 88 if is_exclude: 89 lib_names_exclude.add(lib_name) 90 else: 91 lib_names.add(lib_name) 92 return sorted(lib_names - lib_names_exclude) 93 94 95def _LoadLibraryNames(file_names): 96 """Loads library names from files. 97 98 Each element in the input list can be a .so file or a .txt file. The 99 returned list consists of: 100 - The .so file names in the input list. 101 - The libraries tagged with VNDK-core or VNDK-SP in the .txt file. 102 103 Args: 104 file_names: A list of strings, the library or text file names. 105 106 Returns: 107 A list of strings, the library names (probably with vndk/vndk-sp 108 directory prefixes). 109 """ 110 lib_names = [] 111 for file_name in file_names: 112 if file_name.endswith(".so"): 113 lib_names.append(file_name) 114 else: 115 with open(file_name, "r") as txt_file: 116 lib_names.extend(_LoadLibraryNamesFromTxt(txt_file)) 117 return lib_names 118 119 120def DumpAbi(output_dir, lib_names, lsdump_path): 121 """Generates ABI dumps from library lsdumps. 122 123 Args: 124 output_dir: The output directory of dump files. 125 lib_names: The names of the libraries to dump. 126 lsdump_path: The path to the directory containing lsdumps. 127 128 Returns: 129 A list of strings, the libraries whose ABI dump fails to be created. 130 """ 131 missing_dumps = [] 132 for lib_name in lib_names: 133 dump_path = os.path.join(output_dir, lib_name + '.abi.dump') 134 lib_lsdump_path = os.path.join(lsdump_path, lib_name + '.lsdump') 135 if os.path.isfile(lib_lsdump_path + '.gz'): 136 lib_lsdump_path += '.gz' 137 138 print(lib_lsdump_path) 139 try: 140 extract_lsdump.ParseLsdumpFile(lib_lsdump_path, dump_path) 141 except extract_lsdump.LsdumpError as e: 142 missing_dumps.append(lib_name) 143 print(e) 144 else: 145 print('Output: ' + dump_path) 146 print('') 147 return missing_dumps 148 149 150def _GetTargetArchDir(target_arch, target_arch_variant): 151 if target_arch == target_arch_variant: 152 return target_arch 153 return '{}_{}'.format(target_arch, target_arch_variant) 154 155 156def _GetAbiBitnessFromArch(target_arch): 157 arch_bitness = { 158 'arm': '32', 159 'arm64': '64', 160 'x86': '32', 161 'x86_64': '64', 162 } 163 return arch_bitness[target_arch] 164 165 166def main(): 167 # Parse arguments 168 description = ( 169 'Generates VTS VNDK ABI test abidumps from lsdump. ' 170 'Option values are read from build variables if no value is given. ' 171 'If none of the options are specified, then abidumps for target second ' 172 'arch are also generated.' 173 ) 174 arg_parser = argparse.ArgumentParser(description=description) 175 arg_parser.add_argument("file", nargs="*", 176 help="the libraries to dump. Each file can be " 177 ".so or .txt. The text file can be found at " 178 "build/make/target/product/vndk/current.txt.") 179 arg_parser.add_argument("--output", "-o", action="store", 180 help="output directory for ABI reference dump. " 181 "Default value is PLATFORM_VNDK_VERSION.") 182 arg_parser.add_argument('--platform-vndk-version', 183 help='platform VNDK version. ' 184 'Default value is PLATFORM_VNDK_VERSION.') 185 arg_parser.add_argument('--binder-bitness', 186 choices=['32', '64'], 187 help='bitness of binder interface. ' 188 'Default value is 32 if BINDER32BIT is set ' 189 'else is 64.') 190 arg_parser.add_argument('--target-main-arch', 191 choices=['arm', 'arm64', 'x86', 'x86_64'], 192 help='main CPU arch of the device. ' 193 'Default value is TARGET_ARCH.') 194 arg_parser.add_argument('--target-arch', 195 choices=['arm', 'arm64', 'x86', 'x86_64'], 196 help='CPU arch of the libraries to dump. ' 197 'Default value is TARGET_ARCH.') 198 arg_parser.add_argument('--target-arch-variant', 199 help='CPU arch variant of the libraries to dump. ' 200 'Default value is TARGET_ARCH_VARIANT.') 201 202 args = arg_parser.parse_args() 203 204 build_top_dir = os.getenv("ANDROID_BUILD_TOP") 205 if not build_top_dir: 206 sys.exit("env var ANDROID_BUILD_TOP is not set") 207 208 # If some options are not specified, read build variables as default values. 209 if not all([args.platform_vndk_version, 210 args.binder_bitness, 211 args.target_main_arch, 212 args.target_arch, 213 args.target_arch_variant]): 214 [platform_vndk_version, 215 binder_32_bit, 216 target_arch, 217 target_arch_variant, 218 target_2nd_arch, 219 target_2nd_arch_variant] = GetBuildVariables( 220 build_top_dir, 221 False, 222 ['PLATFORM_VNDK_VERSION', 223 'BINDER32BIT', 224 'TARGET_ARCH', 225 'TARGET_ARCH_VARIANT', 226 'TARGET_2ND_ARCH', 227 'TARGET_2ND_ARCH_VARIANT'] 228 ) 229 target_main_arch = target_arch 230 binder_bitness = '32' if binder_32_bit else '64' 231 232 if args.platform_vndk_version: 233 platform_vndk_version = args.platform_vndk_version 234 235 if args.binder_bitness: 236 binder_bitness = args.binder_bitness 237 238 if args.target_main_arch: 239 target_main_arch = args.target_main_arch 240 241 if args.target_arch: 242 target_arch = args.target_arch 243 244 if args.target_arch_variant: 245 target_arch_variant = args.target_arch_variant 246 247 dump_targets = [(platform_vndk_version, 248 binder_bitness, 249 target_main_arch, 250 target_arch, 251 target_arch_variant)] 252 253 # If all options are not specified, then also create dump for 2nd arch. 254 if not any([args.platform_vndk_version, 255 args.binder_bitness, 256 args.target_main_arch, 257 args.target_arch, 258 args.target_arch_variant]): 259 dump_targets.append((platform_vndk_version, 260 binder_bitness, 261 target_main_arch, 262 target_2nd_arch, 263 target_2nd_arch_variant)) 264 265 for target_tuple in dump_targets: 266 (platform_vndk_version, 267 binder_bitness, 268 target_main_arch, 269 target_arch, 270 target_arch_variant) = target_tuple 271 272 # Determine abi_bitness from target architecture 273 abi_bitness = _GetAbiBitnessFromArch(target_arch) 274 275 # Generate ABI dump from lsdump in TOP/prebuilts/abi-dumps 276 lsdump_path = os.path.join( 277 build_top_dir, 278 'prebuilts', 279 'abi-dumps', 280 'vndk', 281 platform_vndk_version, 282 binder_bitness, 283 _GetTargetArchDir(target_arch, target_arch_variant), 284 'source-based') 285 if not os.path.exists(lsdump_path): 286 print('Warning: lsdump path does not exist: ' + lsdump_path) 287 print('No abidump created.') 288 continue 289 290 output_dir = os.path.join( 291 args.output if args.output else platform_vndk_version, 292 'binder' + binder_bitness, 293 target_main_arch, 294 'lib64' if abi_bitness == '64' else 'lib') 295 print("OUTPUT_DIR=" + output_dir) 296 297 lib_names = _LoadLibraryNames(args.file) 298 299 missing_dumps = DumpAbi(output_dir, lib_names, lsdump_path) 300 301 if missing_dumps: 302 print('Warning: Fails to create ABI dumps for libraries:') 303 for lib_name in missing_dumps: 304 print(lib_name) 305 306 307if __name__ == "__main__": 308 main() 309