forked from ilastik/ilastik
-
Notifications
You must be signed in to change notification settings - Fork 0
/
setup_mac.py
255 lines (209 loc) · 8.94 KB
/
setup_mac.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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# Setup script that uses py2app to generate a redistributable binary from an existing ilastik build.
# To run: python setup_mac.py py2app [--include-meta-repo]
import sys
from setuptools import setup, find_packages
from ilastik import __version__
APP = ['ilastik.py']
DATA_FILES = []
includes = [\
'h5py', 'h5py.defs', 'h5py.utils', 'h5py._proxy', 'h5py._errors',
'PyQt4.pyqtconfig', 'PyQt4.uic','PyQt4.QtCore','PyQt4.QtGui',
'site', 'os',
'vtk',
'vtk.vtkCommonPythonSIP',
'sklearn', 'sklearn.utils',
]
# The py2app dependency walker finds this code, which is intended only for Python3.
# Exclude it!
excludes= ['PyQt4.uic.port_v3']
# By default, cplex is excluded from the bundle.
if '--include-cplex' in sys.argv:
sys.argv.remove('--include-cplex')
dylib_forced_removal = []
else:
# Since the cplex libs can't be found with macholib, the py2app dylib_excludes option doesn't work here.
# We'll handle this manually in the custom run() function, below.
dylib_forced_removal = ['libcplex.dylib', 'libconcert.dylib', 'libilocplex.dylib']
OPTIONS = {'argv_emulation': False, 'includes':includes, 'excludes':excludes, 'iconfile' : 'appIcon.icns'}
packages=find_packages(exclude=["tests", "tests.*"])
package_data={'ilastik': ['ilastik-splash.png',
'ilastik-splash.xcf'],
'ilastik.applets.dataSelection': ['*.ui'],
'ilastik.applets.labeling': ['*.ui', 'icons/*.png', 'icons/*.jpg'],
'ilastik.shell.gui': ['ui/*.ui', '*.qss'],
'ilastik.ilastik_logging': ['logging_config.json'],
'ilastik.plugins': ['*.yapsy-plugin'],
'': ['*.ui']
}
class ilastik_recipe(object):
def check(self, dist, mf):
m = mf.findNode('ilastik')
if m is None:
return None
# Don't put ilastik in the site-packages.zip file
return dict(
packages=['ilastik']
)
class volumina_recipe(object):
def check(self, dist, mf):
m = mf.findNode('volumina')
if m is None:
return None
# Don't put volumina in the site-packages.zip file
return dict(
packages=['volumina']
)
class lazyflow_recipe(object):
def check(self, dist, mf):
m = mf.findNode('lazyflow')
if m is None:
return None
# Don't put lazyflow in the site-packages.zip file
return dict(
packages=['lazyflow']
)
class vtk_recipe(object):
def check(self, dist, mf):
m = mf.findNode('vtk')
if m is None:
return None
# Don't put vtk in the site-packages.zip file
return dict(
packages=['vtk']
)
class sklearn_recipe(object):
def check(self, dist, mf):
m = mf.findNode('sklearn')
if m is None:
return None
# Don't put sklearn in the site-packages.zip file
return dict(
packages=['sklearn']
)
import py2app.recipes
py2app.recipes.ilastik = ilastik_recipe()
py2app.recipes.volumina = volumina_recipe()
py2app.recipes.lazyflow = lazyflow_recipe()
py2app.recipes.vtk = vtk_recipe()
py2app.recipes.sklearn = sklearn_recipe()
##
## The --include-meta-repo option is a special option added by this script.
## If given, we will include the entire ilastik-meta git repo directory (which includes ilastik, lazyflow, and volumina).
## (Otherwise, py2app just includes the individual python module directories, without the supporting files.)
##
include_meta_repo = False
if '--include-meta-repo' in sys.argv:
include_meta_repo = True
sys.argv.remove('--include-meta-repo')
# This hack allows us to run custom code before/after the py2app command executes.
# http://www.niteoweb.com/blog/setuptools-run-custom-code-during-install
import os
import shutil
import ilastik
ilastik_meta_repo = os.path.abspath( os.path.split(ilastik.__file__)[0] + '/../..')
assert os.path.exists(ilastik_meta_repo + '/ilastik')
assert os.path.exists(ilastik_meta_repo + '/lazyflow')
assert os.path.exists(ilastik_meta_repo + '/volumina')
import py2app.build_app
class custom_py2app(py2app.build_app.py2app):
__dist_dir = os.path.split( os.path.abspath(__file__) )[0] + '/dist'
__destination_libpython_dir = __dist_dir + '/ilastik.app/Contents/Resources/lib/python2.7'
__replace_modules = ['ilastik', 'volumina', 'lazyflow']
def run(self):
"""
The normal py2app run() function copies the ilastik, volumina, and
lazyflow modules in the .app without the enclosing repo directory.
This function deletes those modules from the app (after saving the drtile.so binary),
copies the ENTIRE repo directory for each module, and then creates a symlink to the
inner module directory so that the final .app doesn't know the difference.
Just to be clear, our usual py2app command (including our recipes)
produces a lib/python2.7 directory that looks like this:
$ ls -l dist/ilastik.app/Contents/Resources/lib/python2.7/
ilastik/
lazyflow/
volumina/
site-packages.zip
...etc...
But with the --include-meta-repo option, we post-process the package so it looks like this:
$ ls -l dist/ilastik.app/Contents/Resources/lib/python2.7/
ilastik-meta
ilastik@ -> ilastik-meta/ilastik/ilastik
lazyflow@ -> ilastik-meta/lazyflow/lazyflow
volumina@ -> ilastik-meta/volumina/volumina
site-packages.zip
...etc...
Hence, the ilastik, lazyflow, and volumina modules are present via symlinks,
so the .app doesn't know the difference.
Also, this function removes any dylibs in the dylib_forced_removal list from the final distribution.
"""
# Remove modules/repos from an earlier build (if any)
self.remove_repos()
# Run the normal py2app command
py2app.build_app.py2app.run(self)
if include_meta_repo:
# Save drtile.so first!
shutil.move( self.__destination_libpython_dir + '/lazyflow/drtile/drtile.so', self.__dist_dir )
# Copy repos and create symlinks to modules
self.install_repos()
# Replace drtile.so
shutil.move( self.__dist_dir + '/drtile.so', self.__destination_libpython_dir + '/ilastik-meta/lazyflow/lazyflow/drtile/drtile.so' )
# Remove excluded dylibs.
# (The py2app exclude_dylib feature doesn't work if macholib can't find the dylib.)
for dylib in dylib_forced_removal:
dylib_path = self.__dist_dir + '/ilastik.app/Contents/Frameworks/' + dylib
try:
os.remove(dylib_path)
print "Excluded {} from distribution.".format( dylib )
except OSError as ex:
if ex.errno != 2:
raise
def install_repos(self):
self.remove_repos()
src = ilastik_meta_repo
dst = self.__destination_libpython_dir + '/ilastik-meta'
print "Copying {} to {}".format(src, dst )
# Don't copy copy the .app itself!
# (which would lead to infinite recursion)
def ignore(d, contents):
return ['dist', 'build']
# Copy the whole repo
shutil.copytree(src, dst, symlinks=True, ignore=ignore)
# symlink to the actual module within the meta-repo
for module in self.__replace_modules:
relative_link = os.path.relpath( dst + '/' + module + '/' + module, self.__destination_libpython_dir )
os.symlink( relative_link, self.__destination_libpython_dir + '/' + module )
def remove_repos(self):
"""
Remove the existing modules/repos left in the .app tree.
"""
try:
# repo dir created by this custom post-processing step (if present from an earlier build)
p = self.__destination_libpython_dir + '/' + ilastik_meta_repo
shutil.rmtree(p)
except Exception as ex:
pass
for module in self.__replace_modules:
# Remove symlink (if present from a previous build)
try:
p = self.__destination_libpython_dir + '/' + module
os.remove( p )
except Exception as ex:
pass
# Module created by py2app
try:
p = self.__destination_libpython_dir + '/' + module
shutil.rmtree(p)
except Exception as ex:
pass
setup(
cmdclass={ 'py2app' : custom_py2app }, # See hack above.
app=APP,
data_files=DATA_FILES,
options={'py2app': OPTIONS},
setup_requires=['py2app'],
version=__version__,
description='Interactive Image Analysis',
url='http://github.com/ilastik',
packages=packages,
package_data=package_data
)