summaryrefslogtreecommitdiff
path: root/win/wb.py
diff options
context:
space:
mode:
Diffstat (limited to 'win/wb.py')
-rw-r--r--win/wb.py322
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()