-
Notifications
You must be signed in to change notification settings - Fork 53
/
buildmsi.py
180 lines (158 loc) · 5.92 KB
/
buildmsi.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#!/usr/bin/env python2.7
""" Build Windows MSI distributions.
Requirements:
- A 'windeps' folder with all of the *exe installers listed in
BINARY_PACKAGES, available at http://www.lfd.uci.edu/~gohlke/pythonlibs/
in both 32 and 64 bit.
- 'pywin32' installers for 32 and 64 bit in 'windeps' folder, , available
at http://sourceforge.net/projects/pywin32/files/pywin32/
- spreads and all of its dependencies installed in the present Python
environment (*not* with pip's '-e' flag!)
- 'pynsist' package must be installed in the present Python environment,
currently (2014/04/11) from GitHub master, not from PyPi.
- 'nsis' executable must be on $PATH ('apt-get install nsis' on Debian
systems)
Run:
$ python buildmsi.py
When complete, MSIs can be found under 'build/msi{32,64}/spreads_{version}.exe'
"""
import os
import shutil
import sys
import tempfile
import zipfile
from collections import namedtuple
import pkg_resources
from nsist import InstallerBuilder
from pathlib import Path
import spreads
class SourceDep(namedtuple('SourceDep', ['project_name', 'module_name'])):
def __new__(cls, project_name, module_name=None):
if module_name is None:
module_name = project_name
return super(SourceDep, cls).__new__(cls, project_name, module_name)
BINARY_PACKAGES = {
"MarkupSafe": "MarkupSafe-0.19.{arch}-py2.7.exe",
"psutil": "psutil-2.1.0.{arch}-py2.7.exe",
"PyYAML": "PyYAML-3.11.{arch}-py2.7.exe",
"tornado": "tornado-3.2.{arch}-py2.7.exe",
"setuptools": "setuptools-3.4.1.{arch}-py2.7.exe"
}
SOURCE_PACKAGES = [
SourceDep("spreads"),
SourceDep(None, "spreadsplug"),
SourceDep("Flask", "flask"),
SourceDep("Jinja2", "jinja2"),
SourceDep("Werkzeug", "werkzeug"),
SourceDep("backports.ssl-match-hostname", "backports"),
SourceDep("blinker"),
SourceDep("colorama"),
SourceDep("futures", "concurrent"),
SourceDep("itsdangerous"),
SourceDep("requests"),
SourceDep("waitress"),
SourceDep("zipstream"),
SourceDep("roman"),
SourceDep("Wand", "wand"),
SourceDep("isbnlib"),
]
EXTRA_FILES = [
"ImageMagick-6.5.6-8-Q8-windows-dll.exe",
"pyexiv2-0.3.2{arch}.exe",
"pywin32-2.7.6{arch}.exe",
"scantailor-enhanced-20140214-32bit-install.exe",
"tesseract-ocr-setup-3.02.02.exe",
"pdfbeads.exe",
"jbig2.exe",
]
def extract_native_pkg(fname, pkg_dir):
zf = zipfile.ZipFile(unicode(Path('win_deps')/'python'/fname))
tmpdir = Path(tempfile.mkdtemp())
zf.extractall(unicode(tmpdir))
fpaths = []
if (tmpdir/'PLATLIB').exists():
fpaths += [p for p in (tmpdir/'PLATLIB').iterdir()]
if (tmpdir/'PURELIB').exists():
fpaths += [p for p in (tmpdir/'PURELIB').iterdir()]
for path in fpaths:
if path.is_dir():
shutil.copytree(unicode(path), unicode(pkg_dir/path.name))
else:
shutil.copy2(unicode(path), unicode(pkg_dir/path.name))
shutil.rmtree(unicode(tmpdir))
def copy_info(pkg, pkg_dir):
try:
dist = pkg_resources.get_distribution(pkg)
except pkg_resources.DistributionNotFound:
raise IOError("No distribution could be found for {0}!".format(pkg))
if dist.location == os.getcwd():
egg_name = dist.project_name
else:
egg_name = dist.egg_name()
egg_path = Path(dist.location)/(egg_name + ".egg-info")
dist_path = Path(dist.location)/(dist.project_name + "-" + dist.version
+ ".dist-info")
if egg_path.exists():
src_path = egg_path
elif dist_path.exists():
src_path = dist_path
else:
raise IOError("No egg-info or dist-info could be found for {0}!"
.format(pkg))
if src_path.is_dir():
shutil.copytree(unicode(src_path), unicode(pkg_dir/src_path.name))
else:
shutil.copy2(unicode(src_path), unicode(pkg_dir/src_path.name))
def build_msi(bitness=32):
egg_path = Path('spreads.egg-info')
if egg_path.exists():
shutil.rmtree(unicode(egg_path))
build_path = Path('build')
if not build_path.exists():
build_path.mkdir()
pkg_dir = build_path/'pynsist_pkgs'
if pkg_dir.exists():
shutil.rmtree(unicode(pkg_dir))
pkg_dir.mkdir()
for pkg in BINARY_PACKAGES.itervalues():
arch = 'win32' if bitness == 32 else 'win-amd64'
extract_native_pkg(pkg.format(arch=arch), pkg_dir)
for pkg in (x.project_name for x in SOURCE_PACKAGES
if x.project_name is not None):
copy_info(pkg, pkg_dir)
icon = os.path.abspath("spreads.ico")
extra_files = [(unicode((Path('win_deps') / 'extra' /
x.format(arch='.amd64' if bitness == 64 else ''))
.absolute()), None) for x in EXTRA_FILES]
nsi_template = os.path.abspath("template.nsi")
# NOTE: We need to remove the working directory from sys.path to force
# pynsist to copy all of our modules, including 'spreads' and 'spreadsplug'
# from the site-packages. Additionally, we need to change into the
# build directory.
if os.getcwd() in sys.path:
sys.path.remove(os.getcwd())
os.chdir(unicode(build_path))
builder = InstallerBuilder(
appname="spreads",
version=spreads.__version__,
packages=[x.module_name for x in SOURCE_PACKAGES],
extra_files=extra_files,
py_version="2.7.6",
py_bitness=bitness,
build_dir='msi{0}'.format(bitness),
installer_name=None,
nsi_template=nsi_template,
icon=icon,
shortcuts={
'Configure spreads': {
'entry_point': 'spreads.main:run_config_windows',
'icon': icon,
'console': False},
'Spreads Web Service': {
'entry_point': 'spreads.main:run_service_windows',
'icon': icon,
'console': False}
}
)
builder.run()
os.chdir('..')