| #!/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:]) |