diff options
Diffstat (limited to 'win/wb.py')
-rw-r--r-- | win/wb.py | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/win/wb.py b/win/wb.py new file mode 100644 index 0000000..39eadec --- /dev/null +++ b/win/wb.py @@ -0,0 +1,322 @@ +# Python module containing general build functions +# for OpenVPN on Windows + +import os, re, shutil, stat + +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 get_build_params(): + kv = {} + parse_build_params(kv,mod_fn('settings.in')) + + 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 cd_service_win32(): + os.chdir(os.path.join(os.path.dirname(__file__), '../service-win32')) + +def system(cmd): + print "RUN:", cmd + os.system(cmd) + +def run_in_vs_shell(cmd): + """Make sure environment variables are setup before running command""" + os.environ['PATH'] += ";%s\\VC" % (os.path.normpath(config['MSVC']),) + system('cmd /c "vcvarsall.bat x86 && %s"' % (cmd,)) + +def parse_version_m4(kv, version_m4): + '''Parse define lines in 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() + + # If we encounter PRODUCT_TAP_WIN32_MIN_MAJOR or + # PRODUCT_TAP_WIN32_MIN_MAJOR then we need to generate extra + # variables, PRODUCT_TAP_MAJOR_VER and PRODUCT_TAP_MINOR_VER with + # the same contents. This is necessary because tap-win32/tapdrv.c + # build depends on those. + if g[0] == 'PRODUCT_TAP_WIN32_MIN_MAJOR': + kv['PRODUCT_TAP_MAJOR_VER'] = g[1] + elif g[0] == 'PRODUCT_TAP_WIN32_MIN_MINOR': + kv['PRODUCT_TAP_MINOR_VER'] = g[1] + + # Add the variable to build configuration + 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 parse_build_params(kv, settings_in): + r = re.compile(r'^!define\s+(ENABLE_\w+)\s+(\w+)') + + f = open(settings_in) + + for line in f: + line = line.rstrip() + + # Check if this is a #define line starts with ENABLE_ + 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 build_config_h(kv): + """Generate static win/config.h to config.h to mimic autotools behavior""" + preprocess(kv, + in_fn=mod_fn('config.h.in'), + out_fn=home_fn('config.h'), + quote_begin='@', + quote_end='@', + head_comment='/* %s */\n\n' % autogen) + +def build_configure_h(kv, configure_h_out, head_comment): + """Generate a configure.h dynamically""" + fout = open(configure_h_out, 'w') + + # These two variables are required to view build parameters during runtime + configure_defines='#define CONFIGURE_DEFINES \"' + configure_call='#define CONFIGURE_CALL \" config_all.py \"' + + # Initialize the list of enabled features + features = '' + + # Write the header + fout.write(head_comment) + + dict = get_build_params() + + for key, value in dict.iteritems(): + # Add enabled features + features = features + "#define " + key + " " + value + "\n" + + # Add each enabled feature to CONFIGURE_DEFINES list + configure_defines = configure_defines + " " + key + "=" + value + "," + + configure_defines = configure_defines + "\"" + "\n" + + fout.write(features) + fout.write(configure_defines) + fout.write(configure_call) + + + fout.close() + +def build_version_m4_vars(version_m4_vars_out, head_comment): + """Generate a temporary file containing variables from version.m4 in +win/settings.in format. This done to allow importing them in win/openvpn.nsi""" + + fout = open(version_m4_vars_out, 'w') + fout.write(head_comment) + + kv = {} + parse_version_m4(kv, home_fn('version.m4')) + + for key, value in kv.iteritems(): + line = "!define " + key + "\t" + "\"" + value + "\"" + "\n" + fout.write(line) + + fout.close() + +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): + """Parse ../Makefile.am to obtain a list of .h and .c files""" + 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): + """Generate HEADER and OBJS entries dynamically from ../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) + +def rename(src, dest): + print "RENAME %s %s" % (src, dest) + shutil.move(src, dest) + +def rm_rf(path): + try: + shutil.rmtree(path, onerror=onerror) + except: + pass + +def onerror(func, path, exc_info): + """ + Error handler for ``shutil.rmtree``. + + If the error is due to an access error (read only file) + it attempts to add write permission and then retries. + + If the error is for another reason it re-raises the error. + + Usage : ``shutil.rmtree(path, onerror=onerror)`` + """ + if not os.access(path, os.W_OK): + # Is the error an access error ? + os.chmod(path, stat.S_IWUSR) + func(path) + else: + raise + +def mkdir_silent(dir): + try: + os.mkdir(dir) + except: + pass + +config = get_config() |