verify_api.py revision 1.1.1.2
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