1#!/usr/bin/env python 2 3import subprocess 4import optparse 5import os 6import os.path 7import re 8import sys 9 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, exe_path, match_str) 14 (command_exit_status, command_output) = subprocess.getstatusoutput(command) 15 if command_exit_status == 0: 16 if command_output: 17 return command_output[0:-1].split("'\n") 18 else: 19 print('error: command returned no output') 20 else: 21 print('error: command failed with exit status %i\n command: %s' % (command_exit_status, command)) 22 return list() 23 24 25def verify_api(all_args): 26 '''Verify the API in the specified library is valid given one or more binaries.''' 27 usage = "usage: verify_api --library <path> [ --library <path> ...] executable1 [executable2 ...]" 28 description = '''Verify the API in the specified library is valid given one or more binaries. 29 30 Example: 31 32 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 lldb 33 ''' 34 parser = optparse.OptionParser( 35 description=description, 36 prog='verify_api', 37 usage=usage) 38 parser.add_option( 39 '-v', 40 '--verbose', 41 action='store_true', 42 dest='verbose', 43 help='display verbose debug info', 44 default=False) 45 parser.add_option( 46 '-a', 47 '--arch', 48 type='string', 49 action='append', 50 dest='archs', 51 help='architecture to use when checking the api') 52 parser.add_option( 53 '-r', 54 '--api-regex', 55 type='string', 56 dest='api_regex_str', 57 help='Exclude any undefined symbols that do not match this regular expression when searching for missing APIs.') 58 parser.add_option( 59 '-l', 60 '--library', 61 type='string', 62 action='append', 63 dest='libraries', 64 help='Specify one or more libraries that will contain all needed APIs for the executables.') 65 (options, args) = parser.parse_args(all_args) 66 67 api_external_symbols = list() 68 if options.archs: 69 for arch in options.archs: 70 for library in options.libraries: 71 external_symbols = extract_exe_symbol_names( 72 arch, library, "( SECT EXT)") 73 if external_symbols: 74 for external_symbol in external_symbols: 75 api_external_symbols.append(external_symbol) 76 else: 77 sys.exit(1) 78 else: 79 print('error: must specify one or more architectures with the --arch option') 80 sys.exit(4) 81 if options.verbose: 82 print("API symbols:") 83 for (i, external_symbol) in enumerate(api_external_symbols): 84 print("[%u] %s" % (i, external_symbol)) 85 86 api_regex = None 87 if options.api_regex_str: 88 api_regex = re.compile(options.api_regex_str) 89 90 for arch in options.archs: 91 for exe_path in args: 92 print('Verifying (%s) "%s"...' % (arch, exe_path)) 93 exe_errors = 0 94 undefined_symbols = extract_exe_symbol_names( 95 arch, exe_path, "( UNDF EXT)") 96 for undefined_symbol in undefined_symbols: 97 if api_regex: 98 match = api_regex.search(undefined_symbol) 99 if not match: 100 if options.verbose: 101 print('ignoring symbol: %s' % (undefined_symbol)) 102 continue 103 if undefined_symbol in api_external_symbols: 104 if options.verbose: 105 print('verified symbol: %s' % (undefined_symbol)) 106 else: 107 print('missing symbol: %s' % (undefined_symbol)) 108 exe_errors += 1 109 if exe_errors: 110 print('error: missing %u API symbols from %s' % (exe_errors, options.libraries)) 111 else: 112 print('success') 113 114if __name__ == '__main__': 115 verify_api(sys.argv[1:]) 116