/
setup.py
183 lines (148 loc) · 5.57 KB
/
setup.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
# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)
"""
Create a stand-alone macOS app using py2app
To be used like this:
$ python setup.py
"""
import sys
import shutil
from logging import getLogger, StreamHandler, Formatter
from pathlib import Path
from setuptools import setup
from spyder import get_versions
# Setup logger
fmt = Formatter('%(asctime)s [%(levelname)s] [%(name)s] -> %(message)s')
h = StreamHandler()
h.setFormatter(fmt)
logger = getLogger('macOS-installer')
logger.addHandler(h)
logger.setLevel('INFO')
# Define paths
THISDIR = Path(__file__).resolve().parent
SPYREPO = (THISDIR / '..' / '..').resolve()
ICONFILE = SPYREPO / 'img_src' / 'spyder.icns'
APPSCRIPT = SPYREPO / 'scripts' / 'spyder'
MAC_APP_NAME = 'Spyder.app'
APP_BASE_NAME = MAC_APP_NAME[:-4]
# Python version
PYVER = [sys.version_info.major, sys.version_info.minor,
sys.version_info.micro]
version = get_versions()
SPYVER = version['spyder']
SPYCOM = version['revision']
SPYBRA = version['branch']
def make_app_bundle(dist_dir, make_lite=False):
"""
Make macOS application bundle.
Parameters
----------
dist_dir : pathlib.Path
Directory in which to put the application bundle.
make_lite : bool, optional
Whether to create the application bundle with minimal packages.
The default is False.
"""
from spyder.config.utils import EDIT_FILETYPES, _get_extensions
build_type = 'lite' if make_lite else 'full'
logger.info('Creating %s app bundle...', build_type)
from packages import PACKAGES, INCLUDES, EXCLUDES, SCIENTIFIC
if make_lite:
EXCLUDES.extend(SCIENTIFIC)
else:
INCLUDES.extend(SCIENTIFIC)
EDIT_EXT = [ext[1:] for ext in _get_extensions(EDIT_FILETYPES)]
OPTIONS = {
'optimize': 0,
'packages': PACKAGES,
'includes': INCLUDES,
'excludes': EXCLUDES,
'iconfile': ICONFILE.as_posix(),
'dist_dir': dist_dir.as_posix(),
'emulate_shell_environment': True,
'plist': {
'CFBundleDocumentTypes': [{'CFBundleTypeExtensions': EDIT_EXT,
'CFBundleTypeName': 'Text File',
'CFBundleTypeRole': 'Editor'}],
'CFBundleIdentifier': 'org.spyder-ide.Spyder',
'CFBundleShortVersionString': SPYVER,
'NSRequiresAquaSystemAppearance': False, # Darkmode support
'LSEnvironment': {'SPY_COMMIT': SPYCOM, 'SPY_BRANCH': SPYBRA}
}
}
# Build the application
setup(name=APP_BASE_NAME, app=[APPSCRIPT.as_posix()],
options={'py2app': OPTIONS})
return
def make_disk_image(dist_dir, make_lite=False):
"""
Make macOS disk image containing Spyder.app application bundle.
Parameters
----------
dist_dir : pathlib.Path
Directory in which to put the disk image.
make_lite : bool, optional
Whether to append the disk image file and volume name with 'Lite'.
The default is False.
"""
logger.info('Creating disk image...')
from dmgbuild import build_dmg
from dmgbuild.core import DMGError
volume_name = '{}-{} Py-{}.{}.{}'.format(APP_BASE_NAME, SPYVER, *PYVER)
dmg_name = 'Spyder'
if make_lite:
volume_name += ' Lite'
dmg_name += '-Lite'
dmg_name += '.dmg'
dmgfile = (dist_dir / dmg_name).as_posix()
settings_file = (THISDIR / 'dmg_settings.py').as_posix()
settings = {
'files': [(dist_dir / MAC_APP_NAME).as_posix()],
'badge_icon': ICONFILE.as_posix(),
'icon_locations': {MAC_APP_NAME: (140, 120),
'Applications': (500, 120)}
}
try:
build_dmg(dmgfile, volume_name, settings_file=settings_file,
settings=settings, detach_retries=30)
logger.info('Building disk image complete.')
except DMGError as exc:
if exc.args[0] == 'Unable to detach device cleanly':
# don't raise this error since the dmg is forced to detach
logger.warning(exc.args[0])
else:
raise exc
return
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-n', '--no-app', dest='make_app',
action='store_false', default=True,
help='Do not create application bundle')
parser.add_argument('-l', '--lite', dest='make_lite', action='store_true',
default=False,
help='Build with minimal internal packages')
parser.add_argument('-i', '--dmg', dest='make_dmg', action='store_true',
default=False, help='Create disk image')
parser.add_argument('-d', '--dist-dir', dest='dist_dir', default='dist',
help='Distribution directory; passed to py2app')
parser.add_argument('-b', '--bdist-base', dest='build_dir',
default='build',
help='Build directory; passed to py2app')
args, rem = parser.parse_known_args()
# Groom sys.argv for py2app
sys.argv = sys.argv[:1] + ['py2app'] + rem
dist_dir = Path(args.dist_dir).resolve()
build_dir = Path(args.build_dir).resolve()
if args.make_app:
shutil.rmtree(build_dir, ignore_errors=True)
make_app_bundle(dist_dir, make_lite=args.make_lite)
else:
logger.info('Skipping app bundle.')
if args.make_dmg:
make_disk_image(dist_dir, make_lite=args.make_lite)
else:
logger.info('Skipping disk image.')