Blob Blame History Raw
#!/usr/bin/python3
#
# Makefile modificator
#
# Should help in building bin/tests/system tests standalone,
# linked to libraries installed into the system.
# TODO:
# - Fix top_srcdir, because dyndb/driver/Makefile uses $TOPSRC/mkinstalldirs
# - Fix conf.sh to contain paths to system tools
# - Export $TOP/version somewhere, where it would be used
# - system tests needs bin/tests code. Do not include just bin/tests/system
#
# Possible solution:
#
# sed -e 's/$TOP\/s\?bin\/\(delv\|confgen\|named\|nsupdate\|pkcs11\|python\|rndc\|check\|dig\|dnssec\|tools\)\/\([[:alnum:]-]\+\)/`type -p \2`/' conf.sh
# sed -e 's,../../../../\(isc-config.sh\),\1,' builtin/tests.sh
# or use: $NAMED -V | head -1 | cut -d ' ' -f 2

import re
import argparse

"""
Script for replacing Makefile ISC_INCLUDES with runtime flags.

Should translate part of Makefile to use isc-config.sh instead static linked sources.
ISC_INCLUDES = -I/home/pemensik/rhel/bind/bind-9.11.12/build/lib/isc/include \
        -I${top_srcdir}/lib/isc \
        -I${top_srcdir}/lib/isc/include \
        -I${top_srcdir}/lib/isc/unix/include \
        -I${top_srcdir}/lib/isc/pthreads/include \
        -I${top_srcdir}/lib/isc/x86_32/include

Should be translated to:
ISC_INCLUDES = $(shell isc-config.sh --cflags isc)
"""

def isc_config(mode, lib):
    if mode:
        return '$(shell isc-config.sh {mode} {lib})'.format(mode=mode, lib=lib)
    else:
        return ''

def check_match(match, debug=False):
    """
    Check this definition is handled by internal library
    """
    if not match:
        return False
    lib = match.group(2).lower()
    ok = not lib_filter or lib in lib_filter
    if debug:
        print('{status} {lib}: {text}'.format(status=ok, lib=lib, text=match.group(1)))
    return ok

def fix_line(match, mode):
    lib = match.group(2).lower()
    return match.group(1)+isc_config(mode, lib)+"\n"

def fix_file_lines(path, debug=False):
    """
    Opens file and scans fixes selected parameters

    Returns list of lines if something should be changed,
    None if no action is required
    """
    fixed = []
    changed = False
    with open(path, 'r') as fin:
        fout = None

        line = next(fin, None)
        while line:
            appended = False
            while line.endswith("\\\n"):
                line += next(fin, None)

            inc = re_includes.match(line)
            deplibs = re_deplibs.match(line)
            libs = re_libs.match(line)
            newline = None
            if check_match(inc, debug=debug):
                newline = fix_line(inc, '--cflags')
            elif check_match(deplibs, debug=debug):
                newline = fix_line(libs, None)
            elif check_match(libs, debug=debug):
                newline = fix_line(libs, '--libs')

            if newline and line != newline:
                changed = True
                line = newline

            fixed.append(line)
            line = next(fin, None)

    if not changed:
        return None
    else:
        return fixed

def write_lines(path, lines):
    fout = open(path, 'w')
    for line in lines:
        fout.write(line)
    fout.close()

def print_lines(lines):
    for line in lines:
        print(line, end='')

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Makefile multiline include replacer')
    parser.add_argument('files', nargs='+')
    parser.add_argument('--filter', type=str,
            default='isc isccc isccfg dns lwres bind9 irs',
            help='List of libraries supported by isc-config.sh')
    parser.add_argument('--check', action='store_true',
            help='Test file only')
    parser.add_argument('--print', action='store_true',
            help='Print changed file only')
    parser.add_argument('--debug', action='store_true',
            help='Enable debug outputs')

    args = parser.parse_args()
    lib_filter = None

    re_includes = re.compile(r'^\s*((\w+)_INCLUDES\s+=\s*).*')
    re_deplibs = re.compile(r'^\s*((\w+)DEPLIBS\s*=).*')
    re_libs = re.compile(r'^\s*((\w+)LIBS\s*=).*')

    if args.filter:
        lib_filter = set(args.filter.split(' '))
        pass

    for path in args.files:
        lines = fix_file_lines(path, debug=args.debug)
        if lines:
            if args.print:
                print_lines(lines)
            elif not args.check:
                write_lines(path, lines)
                print('File {path} was fixed'.format(path=path))
        else:
            print('File {path} does not need fixing'.format(path=path))