131 lines · python
1#!/usr/bin/env python2 3import subprocess4import optparse5import os6import os.path7import re8import sys9 10 11def extract_exe_symbol_names(arch, exe_path, match_str):12 command = 'dsymutil --arch %s -s "%s" | grep "%s" | colrm 1 69' % (13 arch,14 exe_path,15 match_str,16 )17 (command_exit_status, command_output) = subprocess.getstatusoutput(command)18 if command_exit_status == 0:19 if command_output:20 return command_output[0:-1].split("'\n")21 else:22 print("error: command returned no output")23 else:24 print(25 "error: command failed with exit status %i\n command: %s"26 % (command_exit_status, command)27 )28 return list()29 30 31def verify_api(all_args):32 """Verify the API in the specified library is valid given one or more binaries."""33 usage = "usage: verify_api --library <path> [ --library <path> ...] executable1 [executable2 ...]"34 description = """Verify the API in the specified library is valid given one or more binaries.35 36 Example:37 38 verify_api.py --library ~/Documents/src/lldb/build/Debug/LLDB.framework/LLDB --arch x86_64 /Applications/Xcode.app/Contents/PlugIns/DebuggerLLDB.ideplugin/Contents/MacOS/DebuggerLLDB --api-regex lldb39 """40 parser = optparse.OptionParser(41 description=description, prog="verify_api", usage=usage42 )43 parser.add_option(44 "-v",45 "--verbose",46 action="store_true",47 dest="verbose",48 help="display verbose debug info",49 default=False,50 )51 parser.add_option(52 "-a",53 "--arch",54 type="string",55 action="append",56 dest="archs",57 help="architecture to use when checking the api",58 )59 parser.add_option(60 "-r",61 "--api-regex",62 type="string",63 dest="api_regex_str",64 help="Exclude any undefined symbols that do not match this regular expression when searching for missing APIs.",65 )66 parser.add_option(67 "-l",68 "--library",69 type="string",70 action="append",71 dest="libraries",72 help="Specify one or more libraries that will contain all needed APIs for the executables.",73 )74 (options, args) = parser.parse_args(all_args)75 76 api_external_symbols = list()77 if options.archs:78 for arch in options.archs:79 for library in options.libraries:80 external_symbols = extract_exe_symbol_names(81 arch, library, "( SECT EXT)"82 )83 if external_symbols:84 for external_symbol in external_symbols:85 api_external_symbols.append(external_symbol)86 else:87 sys.exit(1)88 else:89 print("error: must specify one or more architectures with the --arch option")90 sys.exit(4)91 if options.verbose:92 print("API symbols:")93 for i, external_symbol in enumerate(api_external_symbols):94 print("[%u] %s" % (i, external_symbol))95 96 api_regex = None97 if options.api_regex_str:98 api_regex = re.compile(options.api_regex_str)99 100 for arch in options.archs:101 for exe_path in args:102 print('Verifying (%s) "%s"...' % (arch, exe_path))103 exe_errors = 0104 undefined_symbols = extract_exe_symbol_names(105 arch, exe_path, "( UNDF EXT)"106 )107 for undefined_symbol in undefined_symbols:108 if api_regex:109 match = api_regex.search(undefined_symbol)110 if not match:111 if options.verbose:112 print("ignoring symbol: %s" % (undefined_symbol))113 continue114 if undefined_symbol in api_external_symbols:115 if options.verbose:116 print("verified symbol: %s" % (undefined_symbol))117 else:118 print("missing symbol: %s" % (undefined_symbol))119 exe_errors += 1120 if exe_errors:121 print(122 "error: missing %u API symbols from %s"123 % (exe_errors, options.libraries)124 )125 else:126 print("success")127 128 129if __name__ == "__main__":130 verify_api(sys.argv[1:])131