Blame symbols-check.py

Packit Service 103f6b
#!/usr/bin/env python3
Packit Service 103f6b
Packit Service 103f6b
import argparse
Packit Service 103f6b
import os
Packit Service 103f6b
import platform
Packit Service 103f6b
import subprocess
Packit Service 103f6b
Packit Service 103f6b
# This list contains symbols that _might_ be exported for some platforms
Packit Service 103f6b
PLATFORM_SYMBOLS = [
Packit Service 103f6b
    '__bss_end__',
Packit Service 103f6b
    '__bss_start__',
Packit Service 103f6b
    '__bss_start',
Packit Service 103f6b
    '__end__',
Packit Service 103f6b
    '_bss_end__',
Packit Service 103f6b
    '_edata',
Packit Service 103f6b
    '_end',
Packit Service 103f6b
    '_fini',
Packit Service 103f6b
    '_init',
Packit Service 103f6b
]
Packit Service 103f6b
Packit Service 103f6b
Packit Service 103f6b
def get_symbols(nm, lib):
Packit Service 103f6b
    '''
Packit Service 103f6b
    List all the (non platform-specific) symbols exported by the library
Packit Service 103f6b
    '''
Packit Service 103f6b
    symbols = []
Packit Service 103f6b
    platform_name = platform.system()
Packit Service 103f6b
    output = subprocess.check_output([nm, '-gP', lib],
Packit Service 103f6b
                                     stderr=open(os.devnull, 'w')).decode("ascii")
Packit Service 103f6b
    for line in output.splitlines():
Packit Service 103f6b
        fields = line.split()
Packit Service 103f6b
        if len(fields) == 2 or fields[1] == 'U':
Packit Service 103f6b
            continue
Packit Service 103f6b
        symbol_name = fields[0]
Packit Service 103f6b
        if platform_name == 'Linux':
Packit Service 103f6b
            if symbol_name in PLATFORM_SYMBOLS:
Packit Service 103f6b
                continue
Packit Service 103f6b
        elif platform_name == 'Darwin':
Packit Service 103f6b
            assert symbol_name[0] == '_'
Packit Service 103f6b
            symbol_name = symbol_name[1:]
Packit Service 103f6b
        symbols.append(symbol_name)
Packit Service 103f6b
Packit Service 103f6b
    return symbols
Packit Service 103f6b
Packit Service 103f6b
Packit Service 103f6b
def main():
Packit Service 103f6b
    parser = argparse.ArgumentParser()
Packit Service 103f6b
    parser.add_argument('--symbols-file',
Packit Service 103f6b
                        action='store',
Packit Service 103f6b
                        required=True,
Packit Service 103f6b
                        help='path to file containing symbols')
Packit Service 103f6b
    parser.add_argument('--lib',
Packit Service 103f6b
                        action='store',
Packit Service 103f6b
                        required=True,
Packit Service 103f6b
                        help='path to library')
Packit Service 103f6b
    parser.add_argument('--nm',
Packit Service 103f6b
                        action='store',
Packit Service 103f6b
                        required=True,
Packit Service 103f6b
                        help='path to binary (or name in $PATH)')
Packit Service 103f6b
    args = parser.parse_args()
Packit Service 103f6b
Packit Service 103f6b
    try:
Packit Service 103f6b
        lib_symbols = get_symbols(args.nm, args.lib)
Packit Service 103f6b
    except:
Packit Service 103f6b
        # We can't run this test, but we haven't technically failed it either
Packit Service 103f6b
        # Return the GNU "skip" error code
Packit Service 103f6b
        exit(77)
Packit Service 103f6b
    mandatory_symbols = []
Packit Service 103f6b
    optional_symbols = []
Packit Service 103f6b
    with open(args.symbols_file) as symbols_file:
Packit Service 103f6b
        qualifier_optional = '(optional)'
Packit Service 103f6b
        for line in symbols_file.readlines():
Packit Service 103f6b
Packit Service 103f6b
            # Strip comments
Packit Service 103f6b
            line = line.split('#')[0]
Packit Service 103f6b
            line = line.strip()
Packit Service 103f6b
            if not line:
Packit Service 103f6b
                continue
Packit Service 103f6b
Packit Service 103f6b
            # Line format:
Packit Service 103f6b
            # [qualifier] symbol
Packit Service 103f6b
            qualifier = None
Packit Service 103f6b
            symbol = None
Packit Service 103f6b
Packit Service 103f6b
            fields = line.split()
Packit Service 103f6b
            if len(fields) == 1:
Packit Service 103f6b
                symbol = fields[0]
Packit Service 103f6b
            elif len(fields) == 2:
Packit Service 103f6b
                qualifier = fields[0]
Packit Service 103f6b
                symbol = fields[1]
Packit Service 103f6b
            else:
Packit Service 103f6b
                print(args.symbols_file + ': invalid format: ' + line)
Packit Service 103f6b
                exit(1)
Packit Service 103f6b
Packit Service 103f6b
            # The only supported qualifier is 'optional', which means the
Packit Service 103f6b
            # symbol doesn't have to be exported by the library
Packit Service 103f6b
            if qualifier and not qualifier == qualifier_optional:
Packit Service 103f6b
                print(args.symbols_file + ': invalid qualifier: ' + qualifier)
Packit Service 103f6b
                exit(1)
Packit Service 103f6b
Packit Service 103f6b
            if qualifier == qualifier_optional:
Packit Service 103f6b
                optional_symbols.append(symbol)
Packit Service 103f6b
            else:
Packit Service 103f6b
                mandatory_symbols.append(symbol)
Packit Service 103f6b
Packit Service 103f6b
    unknown_symbols = []
Packit Service 103f6b
    for symbol in lib_symbols:
Packit Service 103f6b
        if symbol in mandatory_symbols:
Packit Service 103f6b
            continue
Packit Service 103f6b
        if symbol in optional_symbols:
Packit Service 103f6b
            continue
Packit Service 103f6b
        unknown_symbols.append(symbol)
Packit Service 103f6b
Packit Service 103f6b
    missing_symbols = [
Packit Service 103f6b
        sym for sym in mandatory_symbols if sym not in lib_symbols
Packit Service 103f6b
    ]
Packit Service 103f6b
Packit Service 103f6b
    for symbol in unknown_symbols:
Packit Service 103f6b
        print(args.lib + ': unknown symbol exported: ' + symbol)
Packit Service 103f6b
Packit Service 103f6b
    for symbol in missing_symbols:
Packit Service 103f6b
        print(args.lib + ': missing symbol: ' + symbol)
Packit Service 103f6b
Packit Service 103f6b
    if unknown_symbols or missing_symbols:
Packit Service 103f6b
        exit(1)
Packit Service 103f6b
    exit(0)
Packit Service 103f6b
Packit Service 103f6b
Packit Service 103f6b
if __name__ == '__main__':
Packit Service 103f6b
    main()