# Python module containing general build functions
# for OpenVPN on Windows

import os, re, shutil

autogen = "Automatically generated by OpenVPN Windows build system"

def get_config():
    kv = {}
    parse_version_m4(kv, home_fn('version.m4'))
    parse_settings_in(kv, mod_fn('settings.in'))

    # config fixups
    kv['DDKVER'] = os.path.basename(kv['DDK_PATH'])
    kv['DDKVER_MAJOR'] = re.match(r'^(\d+)\.', kv['DDKVER']).groups()[0]

    if 'VERSION_SUFFIX' in kv:
        kv['PRODUCT_VERSION'] += kv['VERSION_SUFFIX']

    return kv

def mod_fn(fn, src=__file__, real=True):
    p = os.path.join(os.path.dirname(src), os.path.normpath(fn))
    if real:
        p = os.path.realpath(p)
    return p

def home_fn(fn, real=True):
    return mod_fn(os.path.join('..', fn), real=real)

def cd_home():
    os.chdir(os.path.join(os.path.dirname(__file__), '..'))

def system(cmd):
    print "RUN:", cmd
    os.system(cmd)

def parse_version_m4(kv, version_m4):
    r = re.compile(r'^define\((\w+),\[(.*)\]\)$')
    f = open(version_m4)
    for line in f:
        line = line.rstrip()
        m = re.match(r, line)
        if m:
            g = m.groups()
            kv[g[0]] = g[1]
    f.close()

def parse_settings_in(kv, settings_in):
    r = re.compile(r'^!define\s+(\w+)(?:\s+"?(.*?)"?)?$')
    f = open(settings_in)
    for line in f:
        line = line.rstrip()
        m = re.match(r, line)
        if m:
            g = m.groups()
            kv[g[0]] = g[1] or ''
    f.close()

def dict_def(dict, newdefs):
    ret = dict.copy()
    ret.update(newdefs)
    return ret

def build_autodefs(kv, autodefs_in, autodefs_out):
    preprocess(kv,
               in_fn=autodefs_in,
               out_fn=autodefs_out,
               quote_begin='@',
               quote_end='@',
               head_comment='/* %s */\n\n' % autogen)

def preprocess(kv, in_fn, out_fn, quote_begin=None, quote_end=None, if_prefix=None, head_comment=None):
    def repfn(m):
        var, = m.groups()
        return kv.get(var, '')

    re_macro = re_ifdef = None

    if quote_begin and quote_end:
        re_macro = re.compile(r'%s(\w+)%s' % (re.escape(quote_begin), re.escape(quote_end)))

    if if_prefix:
        re_ifdef = re.compile(r'^\s*%sifdef\s+(\w+)\b' % (re.escape(if_prefix),))
        re_else = re.compile(r'^\s*%selse\b' % (re.escape(if_prefix),))
        re_endif = re.compile(r'^\s*%sendif\b' % (re.escape(if_prefix),))

    if_stack = []
    fin = open(in_fn)
    fout = open(out_fn, 'w')
    if head_comment:
        fout.write(head_comment)
    for line in fin:
        if re_ifdef:
            m = re.match(re_ifdef, line)
            if m:
                var, = m.groups()
                if_stack.append(int(var in kv))
                continue
            elif re.match(re_else, line):
                if_stack[-1] ^= 1
                continue
            elif re.match(re_endif, line):
                if_stack.pop()
                continue
        if not if_stack or min(if_stack):
            if re_macro:
                line = re.sub(re_macro, repfn, line)
            fout.write(line)
    assert not if_stack
    fin.close()
    fout.close()

def print_key_values(kv):
    for k, v in sorted(kv.items()):
        print "%s%s%s" % (k, ' '*(32-len(k)), repr(v))

def get_sources(makefile_am):
    c = set()
    h = set()
    f = open(makefile_am)
    state = False
    for line in f:
        line = line.rstrip()
        if line == 'openvpn_SOURCES = \\':
            state = True
        elif not line:
            state = False
        elif state:
            for sf in line.split():
                if sf.endswith('.c'):
                    c.add(sf[:-2])
                elif sf.endswith('.h'):
                    h.add(sf[:-2])
                elif sf == '\\':
                    pass
                else:
                    print >>sys.stderr, "Unrecognized filename:", sf
    f.close()
    return [ sorted(list(s)) for s in (c, h) ]

def output_mak_list(title, srclist, ext):
    ret = "%s =" % (title,)
    for x in srclist:
        ret += " \\\n\t%s.%s" % (x, ext)
    ret += '\n\n'
    return ret

def make_headers_objs(makefile_am):
    c, h = get_sources(makefile_am)
    ret = output_mak_list('HEADERS', h, 'h')
    ret += output_mak_list('OBJS', c, 'obj')
    return ret

def choose_arch(arch_name):
    if arch_name == 'x64':
        return (True,)
    elif arch_name == 'x86':
        return (False,)
    elif arch_name == 'all':
        return (True, False)
    else:
        raise ValueError("architecture ('%s') must be x86, x64, or all" % (arch_name,))

def rm_rf(dir):
    print "REMOVE", dir
    shutil.rmtree(dir, ignore_errors=True)

def mkdir(dir):
    print "MKDIR", dir
    os.mkdir(dir)

def cp_a(src, dest, dest_is_dir=True):
    if dest_is_dir:
        dest = os.path.join(dest, os.path.basename(src))
    print "COPY_DIR %s %s" % (src, dest)
    shutil.copytree(src, dest)

def cp(src, dest, dest_is_dir=True):
    if dest_is_dir:
        dest = os.path.join(dest, os.path.basename(src))
    print "COPY %s %s" % (src, dest)
    shutil.copyfile(src, dest)

config = get_config()