Blame symbols-check.py

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