1061da546Spatrick#!/usr/bin/env python 2061da546Spatrick 3061da546Spatrickimport subprocess 4061da546Spatrickimport optparse 5061da546Spatrickimport os 6061da546Spatrickimport os.path 7061da546Spatrickimport re 8061da546Spatrickimport sys 9061da546Spatrick 10061da546Spatrick 11061da546Spatrickdef extract_exe_symbol_names(arch, exe_path, match_str): 12061da546Spatrick command = 'dsymutil --arch %s -s "%s" | grep "%s" | colrm 1 69' % ( 13061da546Spatrick arch, exe_path, match_str) 14061da546Spatrick (command_exit_status, command_output) = subprocess.getstatusoutput(command) 15061da546Spatrick if command_exit_status == 0: 16061da546Spatrick if command_output: 17061da546Spatrick return command_output[0:-1].split("'\n") 18061da546Spatrick else: 19061da546Spatrick print('error: command returned no output') 20061da546Spatrick else: 21061da546Spatrick print('error: command failed with exit status %i\n command: %s' % (command_exit_status, command)) 22061da546Spatrick return list() 23061da546Spatrick 24061da546Spatrick 25061da546Spatrickdef verify_api(all_args): 26061da546Spatrick '''Verify the API in the specified library is valid given one or more binaries.''' 27061da546Spatrick usage = "usage: verify_api --library <path> [ --library <path> ...] executable1 [executable2 ...]" 28061da546Spatrick description = '''Verify the API in the specified library is valid given one or more binaries. 29061da546Spatrick 30061da546Spatrick Example: 31061da546Spatrick 32061da546Spatrick 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 33061da546Spatrick ''' 34061da546Spatrick parser = optparse.OptionParser( 35061da546Spatrick description=description, 36061da546Spatrick prog='verify_api', 37061da546Spatrick usage=usage) 38061da546Spatrick parser.add_option( 39061da546Spatrick '-v', 40061da546Spatrick '--verbose', 41061da546Spatrick action='store_true', 42061da546Spatrick dest='verbose', 43061da546Spatrick help='display verbose debug info', 44061da546Spatrick default=False) 45061da546Spatrick parser.add_option( 46061da546Spatrick '-a', 47061da546Spatrick '--arch', 48061da546Spatrick type='string', 49061da546Spatrick action='append', 50061da546Spatrick dest='archs', 51*dda28197Spatrick help='architecture to use when checking the api') 52061da546Spatrick parser.add_option( 53061da546Spatrick '-r', 54061da546Spatrick '--api-regex', 55061da546Spatrick type='string', 56061da546Spatrick dest='api_regex_str', 57061da546Spatrick help='Exclude any undefined symbols that do not match this regular expression when searching for missing APIs.') 58061da546Spatrick parser.add_option( 59061da546Spatrick '-l', 60061da546Spatrick '--library', 61061da546Spatrick type='string', 62061da546Spatrick action='append', 63061da546Spatrick dest='libraries', 64061da546Spatrick help='Specify one or more libraries that will contain all needed APIs for the executables.') 65061da546Spatrick (options, args) = parser.parse_args(all_args) 66061da546Spatrick 67061da546Spatrick api_external_symbols = list() 68061da546Spatrick if options.archs: 69061da546Spatrick for arch in options.archs: 70061da546Spatrick for library in options.libraries: 71061da546Spatrick external_symbols = extract_exe_symbol_names( 72061da546Spatrick arch, library, "( SECT EXT)") 73061da546Spatrick if external_symbols: 74061da546Spatrick for external_symbol in external_symbols: 75061da546Spatrick api_external_symbols.append(external_symbol) 76061da546Spatrick else: 77061da546Spatrick sys.exit(1) 78061da546Spatrick else: 79061da546Spatrick print('error: must specify one or more architectures with the --arch option') 80061da546Spatrick sys.exit(4) 81061da546Spatrick if options.verbose: 82061da546Spatrick print("API symbols:") 83061da546Spatrick for (i, external_symbol) in enumerate(api_external_symbols): 84061da546Spatrick print("[%u] %s" % (i, external_symbol)) 85061da546Spatrick 86061da546Spatrick api_regex = None 87061da546Spatrick if options.api_regex_str: 88061da546Spatrick api_regex = re.compile(options.api_regex_str) 89061da546Spatrick 90061da546Spatrick for arch in options.archs: 91061da546Spatrick for exe_path in args: 92061da546Spatrick print('Verifying (%s) "%s"...' % (arch, exe_path)) 93061da546Spatrick exe_errors = 0 94061da546Spatrick undefined_symbols = extract_exe_symbol_names( 95061da546Spatrick arch, exe_path, "( UNDF EXT)") 96061da546Spatrick for undefined_symbol in undefined_symbols: 97061da546Spatrick if api_regex: 98061da546Spatrick match = api_regex.search(undefined_symbol) 99061da546Spatrick if not match: 100061da546Spatrick if options.verbose: 101061da546Spatrick print('ignoring symbol: %s' % (undefined_symbol)) 102061da546Spatrick continue 103061da546Spatrick if undefined_symbol in api_external_symbols: 104061da546Spatrick if options.verbose: 105061da546Spatrick print('verified symbol: %s' % (undefined_symbol)) 106061da546Spatrick else: 107061da546Spatrick print('missing symbol: %s' % (undefined_symbol)) 108061da546Spatrick exe_errors += 1 109061da546Spatrick if exe_errors: 110061da546Spatrick print('error: missing %u API symbols from %s' % (exe_errors, options.libraries)) 111061da546Spatrick else: 112061da546Spatrick print('success') 113061da546Spatrick 114061da546Spatrickif __name__ == '__main__': 115061da546Spatrick verify_api(sys.argv[1:]) 116