xref: /llvm-project/lldb/scripts/verify_api.py (revision 602e47c5f9fd2e14c7bfb6111e6558fa0d27c87f)
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,
14        exe_path,
15        match_str,
16    )
17    (command_exit_status, command_output) = subprocess.getstatusoutput(command)
18    if command_exit_status == 0:
19        if command_output:
20            return command_output[0:-1].split("'\n")
21        else:
22            print("error: command returned no output")
23    else:
24        print(
25            "error: command failed with exit status %i\n    command: %s"
26            % (command_exit_status, command)
27        )
28    return list()
29
30
31def verify_api(all_args):
32    """Verify the API in the specified library is valid given one or more binaries."""
33    usage = "usage: verify_api --library <path> [ --library <path> ...] executable1 [executable2 ...]"
34    description = """Verify the API in the specified library is valid given one or more binaries.
35
36    Example:
37
38        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
39    """
40    parser = optparse.OptionParser(
41        description=description, prog="verify_api", usage=usage
42    )
43    parser.add_option(
44        "-v",
45        "--verbose",
46        action="store_true",
47        dest="verbose",
48        help="display verbose debug info",
49        default=False,
50    )
51    parser.add_option(
52        "-a",
53        "--arch",
54        type="string",
55        action="append",
56        dest="archs",
57        help="architecture to use when checking the api",
58    )
59    parser.add_option(
60        "-r",
61        "--api-regex",
62        type="string",
63        dest="api_regex_str",
64        help="Exclude any undefined symbols that do not match this regular expression when searching for missing APIs.",
65    )
66    parser.add_option(
67        "-l",
68        "--library",
69        type="string",
70        action="append",
71        dest="libraries",
72        help="Specify one or more libraries that will contain all needed APIs for the executables.",
73    )
74    (options, args) = parser.parse_args(all_args)
75
76    api_external_symbols = list()
77    if options.archs:
78        for arch in options.archs:
79            for library in options.libraries:
80                external_symbols = extract_exe_symbol_names(
81                    arch, library, "(     SECT EXT)"
82                )
83                if external_symbols:
84                    for external_symbol in external_symbols:
85                        api_external_symbols.append(external_symbol)
86                else:
87                    sys.exit(1)
88    else:
89        print("error: must specify one or more architectures with the --arch option")
90        sys.exit(4)
91    if options.verbose:
92        print("API symbols:")
93        for i, external_symbol in enumerate(api_external_symbols):
94            print("[%u] %s" % (i, external_symbol))
95
96    api_regex = None
97    if options.api_regex_str:
98        api_regex = re.compile(options.api_regex_str)
99
100    for arch in options.archs:
101        for exe_path in args:
102            print('Verifying (%s) "%s"...' % (arch, exe_path))
103            exe_errors = 0
104            undefined_symbols = extract_exe_symbol_names(
105                arch, exe_path, "(     UNDF EXT)"
106            )
107            for undefined_symbol in undefined_symbols:
108                if api_regex:
109                    match = api_regex.search(undefined_symbol)
110                    if not match:
111                        if options.verbose:
112                            print("ignoring symbol: %s" % (undefined_symbol))
113                        continue
114                if undefined_symbol in api_external_symbols:
115                    if options.verbose:
116                        print("verified symbol: %s" % (undefined_symbol))
117                else:
118                    print("missing symbol: %s" % (undefined_symbol))
119                    exe_errors += 1
120            if exe_errors:
121                print(
122                    "error: missing %u API symbols from %s"
123                    % (exe_errors, options.libraries)
124                )
125            else:
126                print("success")
127
128
129if __name__ == "__main__":
130    verify_api(sys.argv[1:])
131