|  | #!/usr/bin/env python | 
|  |  | 
|  | 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:]) |