mirror of
https://github.com/OpenVPN/openvpn.git
synced 2025-05-09 13:41:06 +08:00

Signed-off-by: Samuli Seppänen <samuli@openvpn.net> Acked-by: David Sommerseth <davids@redhat.com> Signed-off-by: David Sommerseth <davids@redhat.com> (cherry picked from commit 6b2883a637fe73492f09816ee95b00c1b88d5fcb)
323 lines
9.0 KiB
Python
323 lines
9.0 KiB
Python
# 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()
|