OpenSlides/extras/win32-portable/prepare_portable.py
2012-04-27 22:33:40 +02:00

330 lines
8.9 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
:copyright: 2012 by OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
import errno
import glob
import os
import re
import shutil
import sys
import zlib
zlib.Z_DEFAULT_COMPRESSION = zlib.Z_BEST_COMPRESSION
import zipfile
import distutils.ccompiler
import distutils.sysconfig
from contextlib import nested
from string import Template
import pkg_resources
sys.path.insert(0, os.getcwd())
import openslides
COMMON_EXCLUDE = [
r".pyc$",
r".pyo$",
r".po$",
r".egg-info",
r"\blocale/(?!de/|en/)[^/]+/"
]
LIBEXCLUDE = [
r"^site-packages/",
r"^test/",
r"^curses/",
r"^idlelib/",
r"^lib2to3/",
r"^lib-tk/",
r"^msilib/",
r"^multiprocessing/",
r"^unittest/",
]
OPENSLIDES_EXCLUDE = [
r"^openslides/settings.py"
]
SITE_PACKAGES = {
"django": {
"copy": ["django"],
"exclude": [
r"^contrib/admindocs/",
r"^contrib/comments/",
r"^contrib/databrowse/",
r"^contrib/flatpages/",
r"^contrib/formtools/",
r"^contrib/gis/",
r"^contrib/humanize/",
r"^contrib/localflavor/",
r"^contrib/markup/",
r"^contrib/redirects/",
r"^contrib/sitemaps/",
r"^contrib/syndication/",
r"^contrib/webdesign/",
]
},
"django-mptt": {
"copy": ["mptt"],
},
"reportlab": {
"copy": [
"reportlab",
"_renderPM.pyd",
"_rl_accel.pyd",
"sgmlop.pyd",
"pyHnj.pyd",
],
},
"pil": {
# NOTE: PIL is a special case, see copy_pil
"copy": [],
}
}
PY_DLLS = [
"unicodedata.pyd",
"sqlite3.dll",
"_sqlite3.pyd",
"_socket.pyd",
"select.pyd",
]
MSVCR_PUBLIC_KEY = "1fc8b3b9a1e18e3b"
MSVCR_VERSION = "9.0.21022.8"
MSVCR_NAME = "Microsoft.VC90.CRT"
def compile_re_list(patterns):
expr = "|".join("(?:{0})".format(x) for x in patterns)
return re.compile(expr)
def relpath(base, path, addslash = False):
b = os.path.normpath(os.path.abspath(base))
p = os.path.normpath(os.path.abspath(path))
if p == b:
p = "."
if addslash:
p += "/"
return p
b += os.sep
if not p.startswith(b):
raise ValueError("{0!r} is not relative to {1!r}".format(path, base))
p = p[len(b):].replace(os.sep, "/")
if addslash:
p += "/"
return p
def filter_excluded_dirs(exclude_pattern, basedir, dirpath, dnames):
i, l = 0, len(dnames)
while i < l:
rp = relpath(basedir, os.path.join(dirpath, dnames[i]), True)
if exclude_pattern.search(rp):
del dnames[i]
l -= 1
else:
i += 1
def copy_dir_exclude(exclude, basedir, srcdir, destdir):
for dp, dnames, fnames in os.walk(srcdir):
filter_excluded_dirs(exclude, basedir, dp, dnames)
rp = relpath(basedir, dp)
target_dir = os.path.join(destdir, rp)
if not os.path.exists(target_dir):
os.makedirs(target_dir)
for fn in fnames:
fp = os.path.join(dp, fn)
rp = relpath(basedir, fp)
if exclude.search(rp):
continue
shutil.copyfile(fp, os.path.join(destdir, rp))
def collect_lib(libdir, odir):
exclude = compile_re_list(COMMON_EXCLUDE + LIBEXCLUDE)
copy_dir_exclude(exclude, libdir, libdir, os.path.join(odir, "Lib"))
def get_pkg_exclude(name, extra = ()):
exclude = COMMON_EXCLUDE[:]
exclude.extend(SITE_PACKAGES.get(name, {}).get("exclude", []))
exclude.extend(extra)
return compile_re_list(exclude)
def copy_package(name, info, odir):
dist = pkg_resources.get_distribution(name)
exclude = get_pkg_exclude(name)
site_dir = dist.location
for thing in info.get("copy", []):
fp = os.path.join(site_dir, thing)
if not os.path.isdir(fp):
rp = relpath(site_dir, fp)
ofp = os.path.join(odir, rp)
shutil.copyfile(fp, ofp)
else:
copy_dir_exclude(exclude, site_dir, fp, odir)
def copy_pil(odir):
dist = pkg_resources.get_distribution("pil")
exclude = get_pkg_exclude("pil")
dest_dir = os.path.join(odir, "PIL")
copy_dir_exclude(exclude, dist.location, dist.location, dest_dir)
fp = os.path.join(dest_dir, "PIL.pth")
if os.path.isfile(fp):
os.rename(fp, os.path.join(odir, "PIL.pth"))
else:
fp = os.path.join(os.path.dirname(dist.location), "PIL.pth")
shutil.copyfile(fp, os.path.join(odir, "PIL.pth"))
def collect_site_packages(sitedir, odir):
if not os.path.exists(odir):
os.makedirs(odir)
for name, info in SITE_PACKAGES.iteritems():
copy_package(name, info, odir)
assert "pil" in SITE_PACKAGES
copy_pil(odir)
def compile_openslides_launcher():
try:
cc = distutils.ccompiler.new_compiler()
if not cc.initialized:
cc.initialize()
except distutils.errors.DistutilsError:
return False
cc.add_include_dir(distutils.sysconfig.get_python_inc())
cc.add_library_dir(os.path.join(sys.exec_prefix, "Libs"))
objs = cc.compile(["extras/win32-portable/openslides.c"])
cc.link_executable(objs, "extras/win32-portable/openslides")
return True
def copy_dlls(odir):
dll_src = os.path.join(sys.exec_prefix, "DLLs")
dll_dest = os.path.join(odir, "DLLs")
if not os.path.exists(dll_dest):
os.makedirs(dll_dest)
for dll_name in PY_DLLS:
src = os.path.join(dll_src, dll_name)
dest = os.path.join(dll_dest, dll_name)
shutil.copyfile(src, dest)
pydllname = "python{0}{1}.dll".format(*sys.version_info[:2])
src = os.path.join(os.environ["WINDIR"], "System32", pydllname)
dest = os.path.join(odir, pydllname)
shutil.copyfile(src, dest)
def copy_msvcr(odir):
candidates = glob.glob("{0}/x86_*{1}_{2}*".format(
os.path.join(os.environ["WINDIR"], "winsxs"),
MSVCR_NAME, MSVCR_PUBLIC_KEY))
msvcr_local_name = None
msvcr_dll_dir = None
for dp in candidates:
bn = os.path.basename(dp)
if MSVCR_VERSION in bn:
msvcr_local_name = bn
msvcr_dll_dir = dp
break
else:
sys.stderr.write("Warning could not determine msvcr runtime location\n")
sys.stderr.write("Private asssembly for VC runtime must be added manually\n")
return
msvcr_dest_dir = os.path.join(odir, MSVCR_NAME)
if not os.path.exists(msvcr_dest_dir):
os.makedirs(msvcr_dest_dir)
for fn in os.listdir(msvcr_dll_dir):
src = os.path.join(msvcr_dll_dir, fn)
dest = os.path.join(msvcr_dest_dir, fn)
shutil.copyfile(src, dest)
src = os.path.join(os.environ["WINDIR"], "winsxs", "Manifests",
"{0}.manifest".format(msvcr_local_name))
dest = os.path.join(msvcr_dest_dir, "{0}.manifest".format(MSVCR_NAME))
shutil.copyfile(src, dest)
def write_readme(template_file, outfile):
with open(template_file, "rU") as f:
tmpl = Template(f.read())
packages = ["openslides-{0}".format(openslides.get_version())]
for pkg in SITE_PACKAGES:
dist = pkg_resources.get_distribution(pkg)
packages.append("{0}-{1}".format(dist.project_name, dist.version))
tmpl_vars = {"PACKAGE_LIST": "\n".join(packages),
"VERSION": openslides.get_version()}
with open(outfile, "w") as f:
f.write(tmpl.substitute(tmpl_vars))
def main():
prefix = os.path.dirname(sys.executable)
libdir = os.path.join(prefix, "Lib")
sitedir = os.path.join(libdir, "site-packages")
odir = "dist/openslides-{0}-portable".format(openslides.get_version())
try:
shutil.rmtree(odir)
except OSError as e:
if e.errno != errno.ENOENT:
raise
os.makedirs(odir)
collect_lib(libdir, odir)
collect_site_packages(sitedir, os.path.join(odir, "site-packages"))
exclude = get_pkg_exclude("openslides", OPENSLIDES_EXCLUDE)
copy_dir_exclude(exclude, ".", "openslides", odir)
if not compile_openslides_launcher():
sys.stdout.write("Using prebuild openslides.exe\n")
shutil.copyfile("extras/win32-portable/openslides.exe",
os.path.join(odir, "openslides.exe"))
copy_dlls(odir)
copy_msvcr(odir)
shutil.copytree("extras/win32-portable/licenses",
os.path.join(odir, "licenses"))
zip_fp = os.path.join("dist", "openslides-{0}-portable.zip".format(
openslides.get_version()))
write_readme("extras/win32-portable/README.txt.in",
os.path.join(odir, "README.txt"))
with zipfile.ZipFile(zip_fp, "w", zipfile.ZIP_DEFLATED) as zf:
for dp, dnames, fnames in os.walk(odir):
for fn in fnames:
fp = os.path.join(dp, fn)
rp = relpath(odir, fp)
zf.write(fp, rp)
print("Successfully build {0}".format(zip_fp))
if __name__ == "__main__":
main()