| #!/usr/bin/env python3 | |
| import subprocess | |
| import optparse | |
| import os | |
| import os.path | |
| import re | |
| import sys | |
| def extract_exe_symbol_names(arch, exe_path, match_str): | |
| command = 'dsymutil --arch %s -s "%s" | grep "%s" | colrm 1 69' % ( | |
| arch, | |
| exe_path, | |
| match_str, | |
| ) | |
| (command_exit_status, command_output) = subprocess.getstatusoutput(command) | |
| if command_exit_status == 0: | |
| if command_output: | |
| return command_output[0:-1].split("'\n") | |
| else: | |
| print("error: command returned no output") | |
| else: | |
| print( | |
| "error: command failed with exit status %i\n command: %s" | |
| % (command_exit_status, command) | |
| ) | |
| return list() | |
| def verify_api(all_args): | |
| """Verify the API in the specified library is valid given one or more binaries.""" | |
| usage = "usage: verify_api --library <path> [ --library <path> ...] executable1 [executable2 ...]" | |
| description = """Verify the API in the specified library is valid given one or more binaries. | |
| Example: | |
| 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 | |
| """ | |
| parser = optparse.OptionParser( | |
| description=description, prog="verify_api", usage=usage | |
| ) | |
| parser.add_option( | |
| "-v", | |
| "--verbose", | |
| action="store_true", | |
| dest="verbose", | |
| help="display verbose debug info", | |
| default=False, | |
| ) | |
| parser.add_option( | |
| "-a", | |
| "--arch", | |
| type="string", | |
| action="append", | |
| dest="archs", | |
| help="architecture to use when checking the api", | |
| ) | |
| parser.add_option( | |
| "-r", | |
| "--api-regex", | |
| type="string", | |
| dest="api_regex_str", | |
| help="Exclude any undefined symbols that do not match this regular expression when searching for missing APIs.", | |
| ) | |
| parser.add_option( | |
| "-l", | |
| "--library", | |
| type="string", | |
| action="append", | |
| dest="libraries", | |
| help="Specify one or more libraries that will contain all needed APIs for the executables.", | |
| ) | |
| (options, args) = parser.parse_args(all_args) | |
| api_external_symbols = list() | |
| if options.archs: | |
| for arch in options.archs: | |
| for library in options.libraries: | |
| external_symbols = extract_exe_symbol_names( | |
| arch, library, "( SECT EXT)" | |
| ) | |
| if external_symbols: | |
| for external_symbol in external_symbols: | |
| api_external_symbols.append(external_symbol) | |
| else: | |
| sys.exit(1) | |
| else: | |
| print("error: must specify one or more architectures with the --arch option") | |
| sys.exit(4) | |
| if options.verbose: | |
| print("API symbols:") | |
| for i, external_symbol in enumerate(api_external_symbols): | |
| print("[%u] %s" % (i, external_symbol)) | |
| api_regex = None | |
| if options.api_regex_str: | |
| api_regex = re.compile(options.api_regex_str) | |
| for arch in options.archs: | |
| for exe_path in args: | |
| print('Verifying (%s) "%s"...' % (arch, exe_path)) | |
| exe_errors = 0 | |
| undefined_symbols = extract_exe_symbol_names( | |
| arch, exe_path, "( UNDF EXT)" | |
| ) | |
| for undefined_symbol in undefined_symbols: | |
| if api_regex: | |
| match = api_regex.search(undefined_symbol) | |
| if not match: | |
| if options.verbose: | |
| print("ignoring symbol: %s" % (undefined_symbol)) | |
| continue | |
| if undefined_symbol in api_external_symbols: | |
| if options.verbose: | |
| print("verified symbol: %s" % (undefined_symbol)) | |
| else: | |
| print("missing symbol: %s" % (undefined_symbol)) | |
| exe_errors += 1 | |
| if exe_errors: | |
| print( | |
| "error: missing %u API symbols from %s" | |
| % (exe_errors, options.libraries) | |
| ) | |
| else: | |
| print("success") | |
| if __name__ == "__main__": | |
| verify_api(sys.argv[1:]) |