##// END OF EJS Templates
Install on Windows without using setuptools...
Thomas Kluyver -
Show More
@@ -1,355 +1,349 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
2 # -*- coding: utf-8 -*-
3 """Setup script for IPython.
3 """Setup script for IPython.
4
4
5 Under Posix environments it works like a typical setup.py script.
5 Under Posix environments it works like a typical setup.py script.
6 Under Windows, the command sdist is not supported, since IPython
6 Under Windows, the command sdist is not supported, since IPython
7 requires utilities which are not available under Windows."""
7 requires utilities which are not available under Windows."""
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (c) 2008-2011, IPython Development Team.
10 # Copyright (c) 2008-2011, IPython Development Team.
11 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
11 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
12 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
12 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
13 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
13 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
14 #
14 #
15 # Distributed under the terms of the Modified BSD License.
15 # Distributed under the terms of the Modified BSD License.
16 #
16 #
17 # The full license is in the file COPYING.rst, distributed with this software.
17 # The full license is in the file COPYING.rst, distributed with this software.
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Minimal Python version sanity check
21 # Minimal Python version sanity check
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 from __future__ import print_function
23 from __future__ import print_function
24
24
25 import sys
25 import sys
26
26
27 # This check is also made in IPython/__init__, don't forget to update both when
27 # This check is also made in IPython/__init__, don't forget to update both when
28 # changing Python version requirements.
28 # changing Python version requirements.
29 v = sys.version_info
29 v = sys.version_info
30 if v[:2] < (2,7) or (v[0] >= 3 and v[:2] < (3,3)):
30 if v[:2] < (2,7) or (v[0] >= 3 and v[:2] < (3,3)):
31 error = "ERROR: IPython requires Python version 2.7 or 3.3 or above."
31 error = "ERROR: IPython requires Python version 2.7 or 3.3 or above."
32 print(error, file=sys.stderr)
32 print(error, file=sys.stderr)
33 sys.exit(1)
33 sys.exit(1)
34
34
35 PY3 = (sys.version_info[0] >= 3)
35 PY3 = (sys.version_info[0] >= 3)
36
36
37 # At least we're on the python version we need, move on.
37 # At least we're on the python version we need, move on.
38
38
39 #-------------------------------------------------------------------------------
39 #-------------------------------------------------------------------------------
40 # Imports
40 # Imports
41 #-------------------------------------------------------------------------------
41 #-------------------------------------------------------------------------------
42
42
43 # Stdlib imports
43 # Stdlib imports
44 import os
44 import os
45 import shutil
45 import shutil
46
46
47 from glob import glob
47 from glob import glob
48
48
49 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
49 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
50 # update it when the contents of directories change.
50 # update it when the contents of directories change.
51 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
51 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
52
52
53 from distutils.core import setup
53 from distutils.core import setup
54
54
55 # Our own imports
55 # Our own imports
56 from setupbase import target_update
56 from setupbase import target_update
57
57
58 from setupbase import (
58 from setupbase import (
59 setup_args,
59 setup_args,
60 find_packages,
60 find_packages,
61 find_package_data,
61 find_package_data,
62 check_package_data_first,
62 check_package_data_first,
63 find_entry_points,
63 find_entry_points,
64 build_scripts_entrypt,
64 build_scripts_entrypt,
65 find_data_files,
65 find_data_files,
66 check_for_dependencies,
66 check_for_dependencies,
67 git_prebuild,
67 git_prebuild,
68 check_submodule_status,
68 check_submodule_status,
69 update_submodules,
69 update_submodules,
70 require_submodules,
70 require_submodules,
71 UpdateSubmodules,
71 UpdateSubmodules,
72 get_bdist_wheel,
72 get_bdist_wheel,
73 CompileCSS,
73 CompileCSS,
74 JavascriptVersion,
74 JavascriptVersion,
75 css_js_prerelease,
75 css_js_prerelease,
76 install_symlinked,
76 install_symlinked,
77 install_lib_symlink,
77 install_lib_symlink,
78 install_scripts_for_symlink,
78 install_scripts_for_symlink,
79 unsymlink,
79 unsymlink,
80 )
80 )
81 from setupext import setupext
81 from setupext import setupext
82
82
83 isfile = os.path.isfile
83 isfile = os.path.isfile
84 pjoin = os.path.join
84 pjoin = os.path.join
85
85
86 #-----------------------------------------------------------------------------
86 #-----------------------------------------------------------------------------
87 # Function definitions
87 # Function definitions
88 #-----------------------------------------------------------------------------
88 #-----------------------------------------------------------------------------
89
89
90 def cleanup():
90 def cleanup():
91 """Clean up the junk left around by the build process"""
91 """Clean up the junk left around by the build process"""
92 if "develop" not in sys.argv and "egg_info" not in sys.argv:
92 if "develop" not in sys.argv and "egg_info" not in sys.argv:
93 try:
93 try:
94 shutil.rmtree('ipython.egg-info')
94 shutil.rmtree('ipython.egg-info')
95 except:
95 except:
96 try:
96 try:
97 os.unlink('ipython.egg-info')
97 os.unlink('ipython.egg-info')
98 except:
98 except:
99 pass
99 pass
100
100
101 #-------------------------------------------------------------------------------
101 #-------------------------------------------------------------------------------
102 # Handle OS specific things
102 # Handle OS specific things
103 #-------------------------------------------------------------------------------
103 #-------------------------------------------------------------------------------
104
104
105 if os.name in ('nt','dos'):
105 if os.name in ('nt','dos'):
106 os_name = 'windows'
106 os_name = 'windows'
107 else:
107 else:
108 os_name = os.name
108 os_name = os.name
109
109
110 # Under Windows, 'sdist' has not been supported. Now that the docs build with
110 # Under Windows, 'sdist' has not been supported. Now that the docs build with
111 # Sphinx it might work, but let's not turn it on until someone confirms that it
111 # Sphinx it might work, but let's not turn it on until someone confirms that it
112 # actually works.
112 # actually works.
113 if os_name == 'windows' and 'sdist' in sys.argv:
113 if os_name == 'windows' and 'sdist' in sys.argv:
114 print('The sdist command is not available under Windows. Exiting.')
114 print('The sdist command is not available under Windows. Exiting.')
115 sys.exit(1)
115 sys.exit(1)
116
116
117 #-------------------------------------------------------------------------------
117 #-------------------------------------------------------------------------------
118 # Make sure we aren't trying to run without submodules
118 # Make sure we aren't trying to run without submodules
119 #-------------------------------------------------------------------------------
119 #-------------------------------------------------------------------------------
120 here = os.path.abspath(os.path.dirname(__file__))
120 here = os.path.abspath(os.path.dirname(__file__))
121
121
122 def require_clean_submodules():
122 def require_clean_submodules():
123 """Check on git submodules before distutils can do anything
123 """Check on git submodules before distutils can do anything
124
124
125 Since distutils cannot be trusted to update the tree
125 Since distutils cannot be trusted to update the tree
126 after everything has been set in motion,
126 after everything has been set in motion,
127 this is not a distutils command.
127 this is not a distutils command.
128 """
128 """
129 # PACKAGERS: Add a return here to skip checks for git submodules
129 # PACKAGERS: Add a return here to skip checks for git submodules
130
130
131 # don't do anything if nothing is actually supposed to happen
131 # don't do anything if nothing is actually supposed to happen
132 for do_nothing in ('-h', '--help', '--help-commands', 'clean', 'submodule'):
132 for do_nothing in ('-h', '--help', '--help-commands', 'clean', 'submodule'):
133 if do_nothing in sys.argv:
133 if do_nothing in sys.argv:
134 return
134 return
135
135
136 status = check_submodule_status(here)
136 status = check_submodule_status(here)
137
137
138 if status == "missing":
138 if status == "missing":
139 print("checking out submodules for the first time")
139 print("checking out submodules for the first time")
140 update_submodules(here)
140 update_submodules(here)
141 elif status == "unclean":
141 elif status == "unclean":
142 print('\n'.join([
142 print('\n'.join([
143 "Cannot build / install IPython with unclean submodules",
143 "Cannot build / install IPython with unclean submodules",
144 "Please update submodules with",
144 "Please update submodules with",
145 " python setup.py submodule",
145 " python setup.py submodule",
146 "or",
146 "or",
147 " git submodule update",
147 " git submodule update",
148 "or commit any submodule changes you have made."
148 "or commit any submodule changes you have made."
149 ]))
149 ]))
150 sys.exit(1)
150 sys.exit(1)
151
151
152 require_clean_submodules()
152 require_clean_submodules()
153
153
154 #-------------------------------------------------------------------------------
154 #-------------------------------------------------------------------------------
155 # Things related to the IPython documentation
155 # Things related to the IPython documentation
156 #-------------------------------------------------------------------------------
156 #-------------------------------------------------------------------------------
157
157
158 # update the manuals when building a source dist
158 # update the manuals when building a source dist
159 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
159 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
160
160
161 # List of things to be updated. Each entry is a triplet of args for
161 # List of things to be updated. Each entry is a triplet of args for
162 # target_update()
162 # target_update()
163 to_update = [
163 to_update = [
164 # FIXME - Disabled for now: we need to redo an automatic way
164 # FIXME - Disabled for now: we need to redo an automatic way
165 # of generating the magic info inside the rst.
165 # of generating the magic info inside the rst.
166 #('docs/magic.tex',
166 #('docs/magic.tex',
167 #['IPython/Magic.py'],
167 #['IPython/Magic.py'],
168 #"cd doc && ./update_magic.sh" ),
168 #"cd doc && ./update_magic.sh" ),
169
169
170 ('docs/man/ipcluster.1.gz',
170 ('docs/man/ipcluster.1.gz',
171 ['docs/man/ipcluster.1'],
171 ['docs/man/ipcluster.1'],
172 'cd docs/man && gzip -9c ipcluster.1 > ipcluster.1.gz'),
172 'cd docs/man && gzip -9c ipcluster.1 > ipcluster.1.gz'),
173
173
174 ('docs/man/ipcontroller.1.gz',
174 ('docs/man/ipcontroller.1.gz',
175 ['docs/man/ipcontroller.1'],
175 ['docs/man/ipcontroller.1'],
176 'cd docs/man && gzip -9c ipcontroller.1 > ipcontroller.1.gz'),
176 'cd docs/man && gzip -9c ipcontroller.1 > ipcontroller.1.gz'),
177
177
178 ('docs/man/ipengine.1.gz',
178 ('docs/man/ipengine.1.gz',
179 ['docs/man/ipengine.1'],
179 ['docs/man/ipengine.1'],
180 'cd docs/man && gzip -9c ipengine.1 > ipengine.1.gz'),
180 'cd docs/man && gzip -9c ipengine.1 > ipengine.1.gz'),
181
181
182 ('docs/man/ipython.1.gz',
182 ('docs/man/ipython.1.gz',
183 ['docs/man/ipython.1'],
183 ['docs/man/ipython.1'],
184 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
184 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
185
185
186 ]
186 ]
187
187
188
188
189 [ target_update(*t) for t in to_update ]
189 [ target_update(*t) for t in to_update ]
190
190
191 #---------------------------------------------------------------------------
191 #---------------------------------------------------------------------------
192 # Find all the packages, package data, and data_files
192 # Find all the packages, package data, and data_files
193 #---------------------------------------------------------------------------
193 #---------------------------------------------------------------------------
194
194
195 packages = find_packages()
195 packages = find_packages()
196 package_data = find_package_data()
196 package_data = find_package_data()
197
197
198 data_files = find_data_files()
198 data_files = find_data_files()
199
199
200 setup_args['packages'] = packages
200 setup_args['packages'] = packages
201 setup_args['package_data'] = package_data
201 setup_args['package_data'] = package_data
202 setup_args['data_files'] = data_files
202 setup_args['data_files'] = data_files
203
203
204 #---------------------------------------------------------------------------
204 #---------------------------------------------------------------------------
205 # custom distutils commands
205 # custom distutils commands
206 #---------------------------------------------------------------------------
206 #---------------------------------------------------------------------------
207 # imports here, so they are after setuptools import if there was one
207 # imports here, so they are after setuptools import if there was one
208 from distutils.command.sdist import sdist
208 from distutils.command.sdist import sdist
209 from distutils.command.upload import upload
209 from distutils.command.upload import upload
210
210
211 class UploadWindowsInstallers(upload):
211 class UploadWindowsInstallers(upload):
212
212
213 description = "Upload Windows installers to PyPI (only used from tools/release_windows.py)"
213 description = "Upload Windows installers to PyPI (only used from tools/release_windows.py)"
214 user_options = upload.user_options + [
214 user_options = upload.user_options + [
215 ('files=', 'f', 'exe file (or glob) to upload')
215 ('files=', 'f', 'exe file (or glob) to upload')
216 ]
216 ]
217 def initialize_options(self):
217 def initialize_options(self):
218 upload.initialize_options(self)
218 upload.initialize_options(self)
219 meta = self.distribution.metadata
219 meta = self.distribution.metadata
220 base = '{name}-{version}'.format(
220 base = '{name}-{version}'.format(
221 name=meta.get_name(),
221 name=meta.get_name(),
222 version=meta.get_version()
222 version=meta.get_version()
223 )
223 )
224 self.files = os.path.join('dist', '%s.*.exe' % base)
224 self.files = os.path.join('dist', '%s.*.exe' % base)
225
225
226 def run(self):
226 def run(self):
227 for dist_file in glob(self.files):
227 for dist_file in glob(self.files):
228 self.upload_file('bdist_wininst', 'any', dist_file)
228 self.upload_file('bdist_wininst', 'any', dist_file)
229
229
230 setup_args['cmdclass'] = {
230 setup_args['cmdclass'] = {
231 'build_py': css_js_prerelease(
231 'build_py': css_js_prerelease(
232 check_package_data_first(git_prebuild('IPython')),
232 check_package_data_first(git_prebuild('IPython')),
233 strict=False),
233 strict=False),
234 'sdist' : css_js_prerelease(git_prebuild('IPython', sdist)),
234 'sdist' : css_js_prerelease(git_prebuild('IPython', sdist)),
235 'upload_wininst' : UploadWindowsInstallers,
235 'upload_wininst' : UploadWindowsInstallers,
236 'submodule' : UpdateSubmodules,
236 'submodule' : UpdateSubmodules,
237 'css' : CompileCSS,
237 'css' : CompileCSS,
238 'symlink': install_symlinked,
238 'symlink': install_symlinked,
239 'install_lib_symlink': install_lib_symlink,
239 'install_lib_symlink': install_lib_symlink,
240 'install_scripts_sym': install_scripts_for_symlink,
240 'install_scripts_sym': install_scripts_for_symlink,
241 'unsymlink': unsymlink,
241 'unsymlink': unsymlink,
242 'jsversion' : JavascriptVersion,
242 'jsversion' : JavascriptVersion,
243 }
243 }
244
244
245 #---------------------------------------------------------------------------
245 #---------------------------------------------------------------------------
246 # Handle scripts, dependencies, and setuptools specific things
246 # Handle scripts, dependencies, and setuptools specific things
247 #---------------------------------------------------------------------------
247 #---------------------------------------------------------------------------
248
248
249 # For some commands, use setuptools. Note that we do NOT list install here!
249 # For some commands, use setuptools. Note that we do NOT list install here!
250 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
250 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
251 needs_setuptools = set(('develop', 'release', 'bdist_egg', 'bdist_rpm',
251 needs_setuptools = set(('develop', 'release', 'bdist_egg', 'bdist_rpm',
252 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
252 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
253 'egg_info', 'easy_install', 'upload', 'install_egg_info',
253 'egg_info', 'easy_install', 'upload', 'install_egg_info',
254 ))
254 ))
255 if sys.platform == 'win32':
256 # Depend on setuptools for install on *Windows only*
257 # If we get script-installation working without setuptools,
258 # then we can back off, but until then use it.
259 # See Issue #369 on GitHub for more
260 needs_setuptools.add('install')
261
255
262 if len(needs_setuptools.intersection(sys.argv)) > 0:
256 if len(needs_setuptools.intersection(sys.argv)) > 0:
263 import setuptools
257 import setuptools
264
258
265 # This dict is used for passing extra arguments that are setuptools
259 # This dict is used for passing extra arguments that are setuptools
266 # specific to setup
260 # specific to setup
267 setuptools_extra_args = {}
261 setuptools_extra_args = {}
268
262
269 # setuptools requirements
263 # setuptools requirements
270
264
271 extras_require = dict(
265 extras_require = dict(
272 parallel = ['pyzmq>=2.1.11'],
266 parallel = ['pyzmq>=2.1.11'],
273 qtconsole = ['pyzmq>=2.1.11', 'pygments'],
267 qtconsole = ['pyzmq>=2.1.11', 'pygments'],
274 zmq = ['pyzmq>=2.1.11'],
268 zmq = ['pyzmq>=2.1.11'],
275 doc = ['Sphinx>=1.1', 'numpydoc'],
269 doc = ['Sphinx>=1.1', 'numpydoc'],
276 test = ['nose>=0.10.1', 'requests'],
270 test = ['nose>=0.10.1', 'requests'],
277 terminal = [],
271 terminal = [],
278 nbformat = ['jsonschema>=2.0'],
272 nbformat = ['jsonschema>=2.0'],
279 notebook = ['tornado>=3.1', 'pyzmq>=2.1.11', 'jinja2', 'pygments', 'mistune>=0.3.1'],
273 notebook = ['tornado>=3.1', 'pyzmq>=2.1.11', 'jinja2', 'pygments', 'mistune>=0.3.1'],
280 nbconvert = ['pygments', 'jinja2', 'mistune>=0.3.1']
274 nbconvert = ['pygments', 'jinja2', 'mistune>=0.3.1']
281 )
275 )
282
276
283 if sys.version_info < (3, 3):
277 if sys.version_info < (3, 3):
284 extras_require['test'].append('mock')
278 extras_require['test'].append('mock')
285
279
286 extras_require['notebook'].extend(extras_require['nbformat'])
280 extras_require['notebook'].extend(extras_require['nbformat'])
287 extras_require['nbconvert'].extend(extras_require['nbformat'])
281 extras_require['nbconvert'].extend(extras_require['nbformat'])
288
282
289 everything = set()
283 everything = set()
290 for deps in extras_require.values():
284 for deps in extras_require.values():
291 everything.update(deps)
285 everything.update(deps)
292 extras_require['all'] = everything
286 extras_require['all'] = everything
293
287
294 install_requires = []
288 install_requires = []
295
289
296 # add readline
290 # add readline
297 if sys.platform == 'darwin':
291 if sys.platform == 'darwin':
298 if any(arg.startswith('bdist') for arg in sys.argv) or not setupext.check_for_readline():
292 if any(arg.startswith('bdist') for arg in sys.argv) or not setupext.check_for_readline():
299 install_requires.append('gnureadline')
293 install_requires.append('gnureadline')
300 elif sys.platform.startswith('win'):
294 elif sys.platform.startswith('win'):
301 extras_require['terminal'].append('pyreadline>=2.0')
295 extras_require['terminal'].append('pyreadline>=2.0')
302
296
303
297
304 if 'setuptools' in sys.modules:
298 if 'setuptools' in sys.modules:
305 # setup.py develop should check for submodules
299 # setup.py develop should check for submodules
306 from setuptools.command.develop import develop
300 from setuptools.command.develop import develop
307 setup_args['cmdclass']['develop'] = require_submodules(develop)
301 setup_args['cmdclass']['develop'] = require_submodules(develop)
308 setup_args['cmdclass']['bdist_wheel'] = css_js_prerelease(get_bdist_wheel())
302 setup_args['cmdclass']['bdist_wheel'] = css_js_prerelease(get_bdist_wheel())
309
303
310 setuptools_extra_args['zip_safe'] = False
304 setuptools_extra_args['zip_safe'] = False
311 setuptools_extra_args['entry_points'] = {'console_scripts':find_entry_points()}
305 setuptools_extra_args['entry_points'] = {'console_scripts':find_entry_points()}
312 setup_args['extras_require'] = extras_require
306 setup_args['extras_require'] = extras_require
313 requires = setup_args['install_requires'] = install_requires
307 requires = setup_args['install_requires'] = install_requires
314
308
315 # Script to be run by the windows binary installer after the default setup
309 # Script to be run by the windows binary installer after the default setup
316 # routine, to add shortcuts and similar windows-only things. Windows
310 # routine, to add shortcuts and similar windows-only things. Windows
317 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
311 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
318 # doesn't find them.
312 # doesn't find them.
319 if 'bdist_wininst' in sys.argv:
313 if 'bdist_wininst' in sys.argv:
320 if len(sys.argv) > 2 and \
314 if len(sys.argv) > 2 and \
321 ('sdist' in sys.argv or 'bdist_rpm' in sys.argv):
315 ('sdist' in sys.argv or 'bdist_rpm' in sys.argv):
322 print >> sys.stderr, "ERROR: bdist_wininst must be run alone. Exiting."
316 print >> sys.stderr, "ERROR: bdist_wininst must be run alone. Exiting."
323 sys.exit(1)
317 sys.exit(1)
324 setup_args['data_files'].append(
318 setup_args['data_files'].append(
325 ['Scripts', ('scripts/ipython.ico', 'scripts/ipython_nb.ico')])
319 ['Scripts', ('scripts/ipython.ico', 'scripts/ipython_nb.ico')])
326 setup_args['scripts'] = [pjoin('scripts','ipython_win_post_install.py')]
320 setup_args['scripts'] = [pjoin('scripts','ipython_win_post_install.py')]
327 setup_args['options'] = {"bdist_wininst":
321 setup_args['options'] = {"bdist_wininst":
328 {"install_script":
322 {"install_script":
329 "ipython_win_post_install.py"}}
323 "ipython_win_post_install.py"}}
330
324
331 else:
325 else:
332 # If we are installing without setuptools, call this function which will
326 # If we are installing without setuptools, call this function which will
333 # check for dependencies an inform the user what is needed. This is
327 # check for dependencies an inform the user what is needed. This is
334 # just to make life easy for users.
328 # just to make life easy for users.
335 for install_cmd in ('install', 'symlink'):
329 for install_cmd in ('install', 'symlink'):
336 if install_cmd in sys.argv:
330 if install_cmd in sys.argv:
337 check_for_dependencies()
331 check_for_dependencies()
338 break
332 break
339 # scripts has to be a non-empty list, or install_scripts isn't called
333 # scripts has to be a non-empty list, or install_scripts isn't called
340 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
334 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
341
335
342 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
336 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
343
337
344 #---------------------------------------------------------------------------
338 #---------------------------------------------------------------------------
345 # Do the actual setup now
339 # Do the actual setup now
346 #---------------------------------------------------------------------------
340 #---------------------------------------------------------------------------
347
341
348 setup_args.update(setuptools_extra_args)
342 setup_args.update(setuptools_extra_args)
349
343
350 def main():
344 def main():
351 setup(**setup_args)
345 setup(**setup_args)
352 cleanup()
346 cleanup()
353
347
354 if __name__ == '__main__':
348 if __name__ == '__main__':
355 main()
349 main()
@@ -1,737 +1,747 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 This module defines the things that are used in setup.py for building IPython
3 This module defines the things that are used in setup.py for building IPython
4
4
5 This includes:
5 This includes:
6
6
7 * The basic arguments to setup
7 * The basic arguments to setup
8 * Functions for finding things like packages, package data, etc.
8 * Functions for finding things like packages, package data, etc.
9 * A function for checking dependencies.
9 * A function for checking dependencies.
10 """
10 """
11
11
12 # Copyright (c) IPython Development Team.
12 # Copyright (c) IPython Development Team.
13 # Distributed under the terms of the Modified BSD License.
13 # Distributed under the terms of the Modified BSD License.
14
14
15 from __future__ import print_function
15 from __future__ import print_function
16
16
17 import errno
17 import errno
18 import os
18 import os
19 import sys
19 import sys
20
20
21 from distutils import log
21 from distutils import log
22 from distutils.command.build_py import build_py
22 from distutils.command.build_py import build_py
23 from distutils.command.build_scripts import build_scripts
23 from distutils.command.build_scripts import build_scripts
24 from distutils.command.install import install
24 from distutils.command.install import install
25 from distutils.command.install_scripts import install_scripts
25 from distutils.command.install_scripts import install_scripts
26 from distutils.cmd import Command
26 from distutils.cmd import Command
27 from fnmatch import fnmatch
27 from fnmatch import fnmatch
28 from glob import glob
28 from glob import glob
29 from subprocess import check_call
29 from subprocess import check_call
30
30
31 from setupext import install_data_ext
31 from setupext import install_data_ext
32
32
33 #-------------------------------------------------------------------------------
33 #-------------------------------------------------------------------------------
34 # Useful globals and utility functions
34 # Useful globals and utility functions
35 #-------------------------------------------------------------------------------
35 #-------------------------------------------------------------------------------
36
36
37 # A few handy globals
37 # A few handy globals
38 isfile = os.path.isfile
38 isfile = os.path.isfile
39 pjoin = os.path.join
39 pjoin = os.path.join
40 repo_root = os.path.dirname(os.path.abspath(__file__))
40 repo_root = os.path.dirname(os.path.abspath(__file__))
41
41
42 def oscmd(s):
42 def oscmd(s):
43 print(">", s)
43 print(">", s)
44 os.system(s)
44 os.system(s)
45
45
46 # Py3 compatibility hacks, without assuming IPython itself is installed with
46 # Py3 compatibility hacks, without assuming IPython itself is installed with
47 # the full py3compat machinery.
47 # the full py3compat machinery.
48
48
49 try:
49 try:
50 execfile
50 execfile
51 except NameError:
51 except NameError:
52 def execfile(fname, globs, locs=None):
52 def execfile(fname, globs, locs=None):
53 locs = locs or globs
53 locs = locs or globs
54 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
54 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
55
55
56 # A little utility we'll need below, since glob() does NOT allow you to do
56 # A little utility we'll need below, since glob() does NOT allow you to do
57 # exclusion on multiple endings!
57 # exclusion on multiple endings!
58 def file_doesnt_endwith(test,endings):
58 def file_doesnt_endwith(test,endings):
59 """Return true if test is a file and its name does NOT end with any
59 """Return true if test is a file and its name does NOT end with any
60 of the strings listed in endings."""
60 of the strings listed in endings."""
61 if not isfile(test):
61 if not isfile(test):
62 return False
62 return False
63 for e in endings:
63 for e in endings:
64 if test.endswith(e):
64 if test.endswith(e):
65 return False
65 return False
66 return True
66 return True
67
67
68 #---------------------------------------------------------------------------
68 #---------------------------------------------------------------------------
69 # Basic project information
69 # Basic project information
70 #---------------------------------------------------------------------------
70 #---------------------------------------------------------------------------
71
71
72 # release.py contains version, authors, license, url, keywords, etc.
72 # release.py contains version, authors, license, url, keywords, etc.
73 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
73 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
74
74
75 # Create a dict with the basic information
75 # Create a dict with the basic information
76 # This dict is eventually passed to setup after additional keys are added.
76 # This dict is eventually passed to setup after additional keys are added.
77 setup_args = dict(
77 setup_args = dict(
78 name = name,
78 name = name,
79 version = version,
79 version = version,
80 description = description,
80 description = description,
81 long_description = long_description,
81 long_description = long_description,
82 author = author,
82 author = author,
83 author_email = author_email,
83 author_email = author_email,
84 url = url,
84 url = url,
85 download_url = download_url,
85 download_url = download_url,
86 license = license,
86 license = license,
87 platforms = platforms,
87 platforms = platforms,
88 keywords = keywords,
88 keywords = keywords,
89 classifiers = classifiers,
89 classifiers = classifiers,
90 cmdclass = {'install_data': install_data_ext},
90 cmdclass = {'install_data': install_data_ext},
91 )
91 )
92
92
93
93
94 #---------------------------------------------------------------------------
94 #---------------------------------------------------------------------------
95 # Find packages
95 # Find packages
96 #---------------------------------------------------------------------------
96 #---------------------------------------------------------------------------
97
97
98 def find_packages():
98 def find_packages():
99 """
99 """
100 Find all of IPython's packages.
100 Find all of IPython's packages.
101 """
101 """
102 excludes = ['deathrow', 'quarantine']
102 excludes = ['deathrow', 'quarantine']
103 packages = []
103 packages = []
104 for dir,subdirs,files in os.walk('IPython'):
104 for dir,subdirs,files in os.walk('IPython'):
105 package = dir.replace(os.path.sep, '.')
105 package = dir.replace(os.path.sep, '.')
106 if any(package.startswith('IPython.'+exc) for exc in excludes):
106 if any(package.startswith('IPython.'+exc) for exc in excludes):
107 # package is to be excluded (e.g. deathrow)
107 # package is to be excluded (e.g. deathrow)
108 continue
108 continue
109 if '__init__.py' not in files:
109 if '__init__.py' not in files:
110 # not a package
110 # not a package
111 continue
111 continue
112 packages.append(package)
112 packages.append(package)
113 return packages
113 return packages
114
114
115 #---------------------------------------------------------------------------
115 #---------------------------------------------------------------------------
116 # Find package data
116 # Find package data
117 #---------------------------------------------------------------------------
117 #---------------------------------------------------------------------------
118
118
119 def find_package_data():
119 def find_package_data():
120 """
120 """
121 Find IPython's package_data.
121 Find IPython's package_data.
122 """
122 """
123 # This is not enough for these things to appear in an sdist.
123 # This is not enough for these things to appear in an sdist.
124 # We need to muck with the MANIFEST to get this to work
124 # We need to muck with the MANIFEST to get this to work
125
125
126 # exclude components and less from the walk;
126 # exclude components and less from the walk;
127 # we will build the components separately
127 # we will build the components separately
128 excludes = [
128 excludes = [
129 pjoin('static', 'components'),
129 pjoin('static', 'components'),
130 pjoin('static', '*', 'less'),
130 pjoin('static', '*', 'less'),
131 ]
131 ]
132
132
133 # walk notebook resources:
133 # walk notebook resources:
134 cwd = os.getcwd()
134 cwd = os.getcwd()
135 os.chdir(os.path.join('IPython', 'html'))
135 os.chdir(os.path.join('IPython', 'html'))
136 static_data = []
136 static_data = []
137 for parent, dirs, files in os.walk('static'):
137 for parent, dirs, files in os.walk('static'):
138 if any(fnmatch(parent, pat) for pat in excludes):
138 if any(fnmatch(parent, pat) for pat in excludes):
139 # prevent descending into subdirs
139 # prevent descending into subdirs
140 dirs[:] = []
140 dirs[:] = []
141 continue
141 continue
142 for f in files:
142 for f in files:
143 static_data.append(pjoin(parent, f))
143 static_data.append(pjoin(parent, f))
144
144
145 components = pjoin("static", "components")
145 components = pjoin("static", "components")
146 # select the components we actually need to install
146 # select the components we actually need to install
147 # (there are lots of resources we bundle for sdist-reasons that we don't actually use)
147 # (there are lots of resources we bundle for sdist-reasons that we don't actually use)
148 static_data.extend([
148 static_data.extend([
149 pjoin(components, "backbone", "backbone-min.js"),
149 pjoin(components, "backbone", "backbone-min.js"),
150 pjoin(components, "bootstrap", "js", "bootstrap.min.js"),
150 pjoin(components, "bootstrap", "js", "bootstrap.min.js"),
151 pjoin(components, "bootstrap-tour", "build", "css", "bootstrap-tour.min.css"),
151 pjoin(components, "bootstrap-tour", "build", "css", "bootstrap-tour.min.css"),
152 pjoin(components, "bootstrap-tour", "build", "js", "bootstrap-tour.min.js"),
152 pjoin(components, "bootstrap-tour", "build", "js", "bootstrap-tour.min.js"),
153 pjoin(components, "font-awesome", "fonts", "*.*"),
153 pjoin(components, "font-awesome", "fonts", "*.*"),
154 pjoin(components, "google-caja", "html-css-sanitizer-minified.js"),
154 pjoin(components, "google-caja", "html-css-sanitizer-minified.js"),
155 pjoin(components, "highlight.js", "build", "highlight.pack.js"),
155 pjoin(components, "highlight.js", "build", "highlight.pack.js"),
156 pjoin(components, "jquery", "jquery.min.js"),
156 pjoin(components, "jquery", "jquery.min.js"),
157 pjoin(components, "jquery-ui", "ui", "minified", "jquery-ui.min.js"),
157 pjoin(components, "jquery-ui", "ui", "minified", "jquery-ui.min.js"),
158 pjoin(components, "jquery-ui", "themes", "smoothness", "jquery-ui.min.css"),
158 pjoin(components, "jquery-ui", "themes", "smoothness", "jquery-ui.min.css"),
159 pjoin(components, "jquery-ui", "themes", "smoothness", "images", "*"),
159 pjoin(components, "jquery-ui", "themes", "smoothness", "images", "*"),
160 pjoin(components, "marked", "lib", "marked.js"),
160 pjoin(components, "marked", "lib", "marked.js"),
161 pjoin(components, "requirejs", "require.js"),
161 pjoin(components, "requirejs", "require.js"),
162 pjoin(components, "underscore", "underscore-min.js"),
162 pjoin(components, "underscore", "underscore-min.js"),
163 pjoin(components, "moment", "moment.js"),
163 pjoin(components, "moment", "moment.js"),
164 pjoin(components, "moment", "min", "moment.min.js"),
164 pjoin(components, "moment", "min", "moment.min.js"),
165 pjoin(components, "term.js", "src", "term.js"),
165 pjoin(components, "term.js", "src", "term.js"),
166 pjoin(components, "text-encoding", "lib", "encoding.js"),
166 pjoin(components, "text-encoding", "lib", "encoding.js"),
167 ])
167 ])
168
168
169 # Ship all of Codemirror's CSS and JS
169 # Ship all of Codemirror's CSS and JS
170 for parent, dirs, files in os.walk(pjoin(components, 'codemirror')):
170 for parent, dirs, files in os.walk(pjoin(components, 'codemirror')):
171 for f in files:
171 for f in files:
172 if f.endswith(('.js', '.css')):
172 if f.endswith(('.js', '.css')):
173 static_data.append(pjoin(parent, f))
173 static_data.append(pjoin(parent, f))
174
174
175 os.chdir(os.path.join('tests',))
175 os.chdir(os.path.join('tests',))
176 js_tests = glob('*.js') + glob('*/*.js')
176 js_tests = glob('*.js') + glob('*/*.js')
177
177
178 os.chdir(os.path.join(cwd, 'IPython', 'nbconvert'))
178 os.chdir(os.path.join(cwd, 'IPython', 'nbconvert'))
179 nbconvert_templates = [os.path.join(dirpath, '*.*')
179 nbconvert_templates = [os.path.join(dirpath, '*.*')
180 for dirpath, _, _ in os.walk('templates')]
180 for dirpath, _, _ in os.walk('templates')]
181
181
182 os.chdir(cwd)
182 os.chdir(cwd)
183
183
184 package_data = {
184 package_data = {
185 'IPython.config.profile' : ['README*', '*/*.py'],
185 'IPython.config.profile' : ['README*', '*/*.py'],
186 'IPython.core.tests' : ['*.png', '*.jpg'],
186 'IPython.core.tests' : ['*.png', '*.jpg'],
187 'IPython.lib.tests' : ['*.wav'],
187 'IPython.lib.tests' : ['*.wav'],
188 'IPython.testing.plugin' : ['*.txt'],
188 'IPython.testing.plugin' : ['*.txt'],
189 'IPython.html' : ['templates/*'] + static_data,
189 'IPython.html' : ['templates/*'] + static_data,
190 'IPython.html.tests' : js_tests,
190 'IPython.html.tests' : js_tests,
191 'IPython.qt.console' : ['resources/icon/*.svg'],
191 'IPython.qt.console' : ['resources/icon/*.svg'],
192 'IPython.nbconvert' : nbconvert_templates +
192 'IPython.nbconvert' : nbconvert_templates +
193 [
193 [
194 'tests/files/*.*',
194 'tests/files/*.*',
195 'exporters/tests/files/*.*',
195 'exporters/tests/files/*.*',
196 'preprocessors/tests/files/*.*',
196 'preprocessors/tests/files/*.*',
197 ],
197 ],
198 'IPython.nbconvert.filters' : ['marked.js'],
198 'IPython.nbconvert.filters' : ['marked.js'],
199 'IPython.nbformat' : [
199 'IPython.nbformat' : [
200 'tests/*.ipynb',
200 'tests/*.ipynb',
201 'v3/nbformat.v3.schema.json',
201 'v3/nbformat.v3.schema.json',
202 'v4/nbformat.v4.schema.json',
202 'v4/nbformat.v4.schema.json',
203 ]
203 ]
204 }
204 }
205
205
206 return package_data
206 return package_data
207
207
208
208
209 def check_package_data(package_data):
209 def check_package_data(package_data):
210 """verify that package_data globs make sense"""
210 """verify that package_data globs make sense"""
211 print("checking package data")
211 print("checking package data")
212 for pkg, data in package_data.items():
212 for pkg, data in package_data.items():
213 pkg_root = pjoin(*pkg.split('.'))
213 pkg_root = pjoin(*pkg.split('.'))
214 for d in data:
214 for d in data:
215 path = pjoin(pkg_root, d)
215 path = pjoin(pkg_root, d)
216 if '*' in path:
216 if '*' in path:
217 assert len(glob(path)) > 0, "No files match pattern %s" % path
217 assert len(glob(path)) > 0, "No files match pattern %s" % path
218 else:
218 else:
219 assert os.path.exists(path), "Missing package data: %s" % path
219 assert os.path.exists(path), "Missing package data: %s" % path
220
220
221
221
222 def check_package_data_first(command):
222 def check_package_data_first(command):
223 """decorator for checking package_data before running a given command
223 """decorator for checking package_data before running a given command
224
224
225 Probably only needs to wrap build_py
225 Probably only needs to wrap build_py
226 """
226 """
227 class DecoratedCommand(command):
227 class DecoratedCommand(command):
228 def run(self):
228 def run(self):
229 check_package_data(self.package_data)
229 check_package_data(self.package_data)
230 command.run(self)
230 command.run(self)
231 return DecoratedCommand
231 return DecoratedCommand
232
232
233
233
234 #---------------------------------------------------------------------------
234 #---------------------------------------------------------------------------
235 # Find data files
235 # Find data files
236 #---------------------------------------------------------------------------
236 #---------------------------------------------------------------------------
237
237
238 def make_dir_struct(tag,base,out_base):
238 def make_dir_struct(tag,base,out_base):
239 """Make the directory structure of all files below a starting dir.
239 """Make the directory structure of all files below a starting dir.
240
240
241 This is just a convenience routine to help build a nested directory
241 This is just a convenience routine to help build a nested directory
242 hierarchy because distutils is too stupid to do this by itself.
242 hierarchy because distutils is too stupid to do this by itself.
243
243
244 XXX - this needs a proper docstring!
244 XXX - this needs a proper docstring!
245 """
245 """
246
246
247 # we'll use these a lot below
247 # we'll use these a lot below
248 lbase = len(base)
248 lbase = len(base)
249 pathsep = os.path.sep
249 pathsep = os.path.sep
250 lpathsep = len(pathsep)
250 lpathsep = len(pathsep)
251
251
252 out = []
252 out = []
253 for (dirpath,dirnames,filenames) in os.walk(base):
253 for (dirpath,dirnames,filenames) in os.walk(base):
254 # we need to strip out the dirpath from the base to map it to the
254 # we need to strip out the dirpath from the base to map it to the
255 # output (installation) path. This requires possibly stripping the
255 # output (installation) path. This requires possibly stripping the
256 # path separator, because otherwise pjoin will not work correctly
256 # path separator, because otherwise pjoin will not work correctly
257 # (pjoin('foo/','/bar') returns '/bar').
257 # (pjoin('foo/','/bar') returns '/bar').
258
258
259 dp_eff = dirpath[lbase:]
259 dp_eff = dirpath[lbase:]
260 if dp_eff.startswith(pathsep):
260 if dp_eff.startswith(pathsep):
261 dp_eff = dp_eff[lpathsep:]
261 dp_eff = dp_eff[lpathsep:]
262 # The output path must be anchored at the out_base marker
262 # The output path must be anchored at the out_base marker
263 out_path = pjoin(out_base,dp_eff)
263 out_path = pjoin(out_base,dp_eff)
264 # Now we can generate the final filenames. Since os.walk only produces
264 # Now we can generate the final filenames. Since os.walk only produces
265 # filenames, we must join back with the dirpath to get full valid file
265 # filenames, we must join back with the dirpath to get full valid file
266 # paths:
266 # paths:
267 pfiles = [pjoin(dirpath,f) for f in filenames]
267 pfiles = [pjoin(dirpath,f) for f in filenames]
268 # Finally, generate the entry we need, which is a pari of (output
268 # Finally, generate the entry we need, which is a pari of (output
269 # path, files) for use as a data_files parameter in install_data.
269 # path, files) for use as a data_files parameter in install_data.
270 out.append((out_path, pfiles))
270 out.append((out_path, pfiles))
271
271
272 return out
272 return out
273
273
274
274
275 def find_data_files():
275 def find_data_files():
276 """
276 """
277 Find IPython's data_files.
277 Find IPython's data_files.
278
278
279 Just man pages at this point.
279 Just man pages at this point.
280 """
280 """
281
281
282 manpagebase = pjoin('share', 'man', 'man1')
282 manpagebase = pjoin('share', 'man', 'man1')
283
283
284 # Simple file lists can be made by hand
284 # Simple file lists can be made by hand
285 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
285 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
286 if not manpages:
286 if not manpages:
287 # When running from a source tree, the manpages aren't gzipped
287 # When running from a source tree, the manpages aren't gzipped
288 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
288 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
289
289
290 # And assemble the entire output list
290 # And assemble the entire output list
291 data_files = [ (manpagebase, manpages) ]
291 data_files = [ (manpagebase, manpages) ]
292
292
293 return data_files
293 return data_files
294
294
295
295
296 def make_man_update_target(manpage):
296 def make_man_update_target(manpage):
297 """Return a target_update-compliant tuple for the given manpage.
297 """Return a target_update-compliant tuple for the given manpage.
298
298
299 Parameters
299 Parameters
300 ----------
300 ----------
301 manpage : string
301 manpage : string
302 Name of the manpage, must include the section number (trailing number).
302 Name of the manpage, must include the section number (trailing number).
303
303
304 Example
304 Example
305 -------
305 -------
306
306
307 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
307 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
308 ('docs/man/ipython.1.gz',
308 ('docs/man/ipython.1.gz',
309 ['docs/man/ipython.1'],
309 ['docs/man/ipython.1'],
310 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
310 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
311 """
311 """
312 man_dir = pjoin('docs', 'man')
312 man_dir = pjoin('docs', 'man')
313 manpage_gz = manpage + '.gz'
313 manpage_gz = manpage + '.gz'
314 manpath = pjoin(man_dir, manpage)
314 manpath = pjoin(man_dir, manpage)
315 manpath_gz = pjoin(man_dir, manpage_gz)
315 manpath_gz = pjoin(man_dir, manpage_gz)
316 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
316 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
317 locals() )
317 locals() )
318 return (manpath_gz, [manpath], gz_cmd)
318 return (manpath_gz, [manpath], gz_cmd)
319
319
320 # The two functions below are copied from IPython.utils.path, so we don't need
320 # The two functions below are copied from IPython.utils.path, so we don't need
321 # to import IPython during setup, which fails on Python 3.
321 # to import IPython during setup, which fails on Python 3.
322
322
323 def target_outdated(target,deps):
323 def target_outdated(target,deps):
324 """Determine whether a target is out of date.
324 """Determine whether a target is out of date.
325
325
326 target_outdated(target,deps) -> 1/0
326 target_outdated(target,deps) -> 1/0
327
327
328 deps: list of filenames which MUST exist.
328 deps: list of filenames which MUST exist.
329 target: single filename which may or may not exist.
329 target: single filename which may or may not exist.
330
330
331 If target doesn't exist or is older than any file listed in deps, return
331 If target doesn't exist or is older than any file listed in deps, return
332 true, otherwise return false.
332 true, otherwise return false.
333 """
333 """
334 try:
334 try:
335 target_time = os.path.getmtime(target)
335 target_time = os.path.getmtime(target)
336 except os.error:
336 except os.error:
337 return 1
337 return 1
338 for dep in deps:
338 for dep in deps:
339 dep_time = os.path.getmtime(dep)
339 dep_time = os.path.getmtime(dep)
340 if dep_time > target_time:
340 if dep_time > target_time:
341 #print "For target",target,"Dep failed:",dep # dbg
341 #print "For target",target,"Dep failed:",dep # dbg
342 #print "times (dep,tar):",dep_time,target_time # dbg
342 #print "times (dep,tar):",dep_time,target_time # dbg
343 return 1
343 return 1
344 return 0
344 return 0
345
345
346
346
347 def target_update(target,deps,cmd):
347 def target_update(target,deps,cmd):
348 """Update a target with a given command given a list of dependencies.
348 """Update a target with a given command given a list of dependencies.
349
349
350 target_update(target,deps,cmd) -> runs cmd if target is outdated.
350 target_update(target,deps,cmd) -> runs cmd if target is outdated.
351
351
352 This is just a wrapper around target_outdated() which calls the given
352 This is just a wrapper around target_outdated() which calls the given
353 command if target is outdated."""
353 command if target is outdated."""
354
354
355 if target_outdated(target,deps):
355 if target_outdated(target,deps):
356 os.system(cmd)
356 os.system(cmd)
357
357
358 #---------------------------------------------------------------------------
358 #---------------------------------------------------------------------------
359 # Find scripts
359 # Find scripts
360 #---------------------------------------------------------------------------
360 #---------------------------------------------------------------------------
361
361
362 def find_entry_points():
362 def find_entry_points():
363 """Find IPython's scripts.
363 """Find IPython's scripts.
364
364
365 if entry_points is True:
365 if entry_points is True:
366 return setuptools entry_point-style definitions
366 return setuptools entry_point-style definitions
367 else:
367 else:
368 return file paths of plain scripts [default]
368 return file paths of plain scripts [default]
369
369
370 suffix is appended to script names if entry_points is True, so that the
370 suffix is appended to script names if entry_points is True, so that the
371 Python 3 scripts get named "ipython3" etc.
371 Python 3 scripts get named "ipython3" etc.
372 """
372 """
373 ep = [
373 ep = [
374 'ipython%s = IPython:start_ipython',
374 'ipython%s = IPython:start_ipython',
375 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
375 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
376 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
376 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
377 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
377 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
378 'iptest%s = IPython.testing.iptestcontroller:main',
378 'iptest%s = IPython.testing.iptestcontroller:main',
379 ]
379 ]
380 suffix = str(sys.version_info[0])
380 suffix = str(sys.version_info[0])
381 return [e % '' for e in ep] + [e % suffix for e in ep]
381 return [e % '' for e in ep] + [e % suffix for e in ep]
382
382
383 script_src = """#!{executable}
383 script_src = """#!{executable}
384 # This script was automatically generated by setup.py
384 # This script was automatically generated by setup.py
385 if __name__ == '__main__':
385 if __name__ == '__main__':
386 from {mod} import {func}
386 from {mod} import {func}
387 {func}()
387 {func}()
388 """
388 """
389
389
390 class build_scripts_entrypt(build_scripts):
390 class build_scripts_entrypt(build_scripts):
391 def run(self):
391 def run(self):
392 self.mkpath(self.build_dir)
392 self.mkpath(self.build_dir)
393 outfiles = []
393 outfiles = []
394 for script in find_entry_points():
394 for script in find_entry_points():
395 name, entrypt = script.split('=')
395 name, entrypt = script.split('=')
396 name = name.strip()
396 name = name.strip()
397 entrypt = entrypt.strip()
397 entrypt = entrypt.strip()
398 outfile = os.path.join(self.build_dir, name)
398 outfile = os.path.join(self.build_dir, name)
399 outfiles.append(outfile)
399 outfiles.append(outfile)
400 print('Writing script to', outfile)
400 print('Writing script to', outfile)
401
401
402 mod, func = entrypt.split(':')
402 mod, func = entrypt.split(':')
403 with open(outfile, 'w') as f:
403 with open(outfile, 'w') as f:
404 f.write(script_src.format(executable=sys.executable,
404 f.write(script_src.format(executable=sys.executable,
405 mod=mod, func=func))
405 mod=mod, func=func))
406
407 if sys.platform == 'win32':
408 # Write .cmd wrappers for Windows so 'ipython' etc. work at the
409 # command line
410 cmd_file = os.path.join(self.build_dir, name + '.cmd')
411 cmd = '@"{python}" "%~dp0\{script}" %*\r\n'.format(
412 python=sys.executable, script=name)
413 log.info("Writing %s wrapper script" % cmd_file)
414 with open(cmd_file, 'w') as f:
415 f.write(cmd)
406
416
407 return outfiles, outfiles
417 return outfiles, outfiles
408
418
409 class install_lib_symlink(Command):
419 class install_lib_symlink(Command):
410 user_options = [
420 user_options = [
411 ('install-dir=', 'd', "directory to install to"),
421 ('install-dir=', 'd', "directory to install to"),
412 ]
422 ]
413
423
414 def initialize_options(self):
424 def initialize_options(self):
415 self.install_dir = None
425 self.install_dir = None
416
426
417 def finalize_options(self):
427 def finalize_options(self):
418 self.set_undefined_options('symlink',
428 self.set_undefined_options('symlink',
419 ('install_lib', 'install_dir'),
429 ('install_lib', 'install_dir'),
420 )
430 )
421
431
422 def run(self):
432 def run(self):
423 if sys.platform == 'win32':
433 if sys.platform == 'win32':
424 raise Exception("This doesn't work on Windows.")
434 raise Exception("This doesn't work on Windows.")
425 pkg = os.path.join(os.getcwd(), 'IPython')
435 pkg = os.path.join(os.getcwd(), 'IPython')
426 dest = os.path.join(self.install_dir, 'IPython')
436 dest = os.path.join(self.install_dir, 'IPython')
427 if os.path.islink(dest):
437 if os.path.islink(dest):
428 print('removing existing symlink at %s' % dest)
438 print('removing existing symlink at %s' % dest)
429 os.unlink(dest)
439 os.unlink(dest)
430 print('symlinking %s -> %s' % (pkg, dest))
440 print('symlinking %s -> %s' % (pkg, dest))
431 os.symlink(pkg, dest)
441 os.symlink(pkg, dest)
432
442
433 class unsymlink(install):
443 class unsymlink(install):
434 def run(self):
444 def run(self):
435 dest = os.path.join(self.install_lib, 'IPython')
445 dest = os.path.join(self.install_lib, 'IPython')
436 if os.path.islink(dest):
446 if os.path.islink(dest):
437 print('removing symlink at %s' % dest)
447 print('removing symlink at %s' % dest)
438 os.unlink(dest)
448 os.unlink(dest)
439 else:
449 else:
440 print('No symlink exists at %s' % dest)
450 print('No symlink exists at %s' % dest)
441
451
442 class install_symlinked(install):
452 class install_symlinked(install):
443 def run(self):
453 def run(self):
444 if sys.platform == 'win32':
454 if sys.platform == 'win32':
445 raise Exception("This doesn't work on Windows.")
455 raise Exception("This doesn't work on Windows.")
446
456
447 # Run all sub-commands (at least those that need to be run)
457 # Run all sub-commands (at least those that need to be run)
448 for cmd_name in self.get_sub_commands():
458 for cmd_name in self.get_sub_commands():
449 self.run_command(cmd_name)
459 self.run_command(cmd_name)
450
460
451 # 'sub_commands': a list of commands this command might have to run to
461 # 'sub_commands': a list of commands this command might have to run to
452 # get its work done. See cmd.py for more info.
462 # get its work done. See cmd.py for more info.
453 sub_commands = [('install_lib_symlink', lambda self:True),
463 sub_commands = [('install_lib_symlink', lambda self:True),
454 ('install_scripts_sym', lambda self:True),
464 ('install_scripts_sym', lambda self:True),
455 ]
465 ]
456
466
457 class install_scripts_for_symlink(install_scripts):
467 class install_scripts_for_symlink(install_scripts):
458 """Redefined to get options from 'symlink' instead of 'install'.
468 """Redefined to get options from 'symlink' instead of 'install'.
459
469
460 I love distutils almost as much as I love setuptools.
470 I love distutils almost as much as I love setuptools.
461 """
471 """
462 def finalize_options(self):
472 def finalize_options(self):
463 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
473 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
464 self.set_undefined_options('symlink',
474 self.set_undefined_options('symlink',
465 ('install_scripts', 'install_dir'),
475 ('install_scripts', 'install_dir'),
466 ('force', 'force'),
476 ('force', 'force'),
467 ('skip_build', 'skip_build'),
477 ('skip_build', 'skip_build'),
468 )
478 )
469
479
470 #---------------------------------------------------------------------------
480 #---------------------------------------------------------------------------
471 # Verify all dependencies
481 # Verify all dependencies
472 #---------------------------------------------------------------------------
482 #---------------------------------------------------------------------------
473
483
474 def check_for_dependencies():
484 def check_for_dependencies():
475 """Check for IPython's dependencies.
485 """Check for IPython's dependencies.
476
486
477 This function should NOT be called if running under setuptools!
487 This function should NOT be called if running under setuptools!
478 """
488 """
479 from setupext.setupext import (
489 from setupext.setupext import (
480 print_line, print_raw, print_status,
490 print_line, print_raw, print_status,
481 check_for_sphinx, check_for_pygments,
491 check_for_sphinx, check_for_pygments,
482 check_for_nose, check_for_pexpect,
492 check_for_nose, check_for_pexpect,
483 check_for_pyzmq, check_for_readline,
493 check_for_pyzmq, check_for_readline,
484 check_for_jinja2, check_for_tornado
494 check_for_jinja2, check_for_tornado
485 )
495 )
486 print_line()
496 print_line()
487 print_raw("BUILDING IPYTHON")
497 print_raw("BUILDING IPYTHON")
488 print_status('python', sys.version)
498 print_status('python', sys.version)
489 print_status('platform', sys.platform)
499 print_status('platform', sys.platform)
490 if sys.platform == 'win32':
500 if sys.platform == 'win32':
491 print_status('Windows version', sys.getwindowsversion())
501 print_status('Windows version', sys.getwindowsversion())
492
502
493 print_raw("")
503 print_raw("")
494 print_raw("OPTIONAL DEPENDENCIES")
504 print_raw("OPTIONAL DEPENDENCIES")
495
505
496 check_for_sphinx()
506 check_for_sphinx()
497 check_for_pygments()
507 check_for_pygments()
498 check_for_nose()
508 check_for_nose()
499 if os.name == 'posix':
509 if os.name == 'posix':
500 check_for_pexpect()
510 check_for_pexpect()
501 check_for_pyzmq()
511 check_for_pyzmq()
502 check_for_tornado()
512 check_for_tornado()
503 check_for_readline()
513 check_for_readline()
504 check_for_jinja2()
514 check_for_jinja2()
505
515
506 #---------------------------------------------------------------------------
516 #---------------------------------------------------------------------------
507 # VCS related
517 # VCS related
508 #---------------------------------------------------------------------------
518 #---------------------------------------------------------------------------
509
519
510 # utils.submodule has checks for submodule status
520 # utils.submodule has checks for submodule status
511 execfile(pjoin('IPython','utils','submodule.py'), globals())
521 execfile(pjoin('IPython','utils','submodule.py'), globals())
512
522
513 class UpdateSubmodules(Command):
523 class UpdateSubmodules(Command):
514 """Update git submodules
524 """Update git submodules
515
525
516 IPython's external javascript dependencies live in a separate repo.
526 IPython's external javascript dependencies live in a separate repo.
517 """
527 """
518 description = "Update git submodules"
528 description = "Update git submodules"
519 user_options = []
529 user_options = []
520
530
521 def initialize_options(self):
531 def initialize_options(self):
522 pass
532 pass
523
533
524 def finalize_options(self):
534 def finalize_options(self):
525 pass
535 pass
526
536
527 def run(self):
537 def run(self):
528 failure = False
538 failure = False
529 try:
539 try:
530 self.spawn('git submodule init'.split())
540 self.spawn('git submodule init'.split())
531 self.spawn('git submodule update --recursive'.split())
541 self.spawn('git submodule update --recursive'.split())
532 except Exception as e:
542 except Exception as e:
533 failure = e
543 failure = e
534 print(e)
544 print(e)
535
545
536 if not check_submodule_status(repo_root) == 'clean':
546 if not check_submodule_status(repo_root) == 'clean':
537 print("submodules could not be checked out")
547 print("submodules could not be checked out")
538 sys.exit(1)
548 sys.exit(1)
539
549
540
550
541 def git_prebuild(pkg_dir, build_cmd=build_py):
551 def git_prebuild(pkg_dir, build_cmd=build_py):
542 """Return extended build or sdist command class for recording commit
552 """Return extended build or sdist command class for recording commit
543
553
544 records git commit in IPython.utils._sysinfo.commit
554 records git commit in IPython.utils._sysinfo.commit
545
555
546 for use in IPython.utils.sysinfo.sys_info() calls after installation.
556 for use in IPython.utils.sysinfo.sys_info() calls after installation.
547
557
548 Also ensures that submodules exist prior to running
558 Also ensures that submodules exist prior to running
549 """
559 """
550
560
551 class MyBuildPy(build_cmd):
561 class MyBuildPy(build_cmd):
552 ''' Subclass to write commit data into installation tree '''
562 ''' Subclass to write commit data into installation tree '''
553 def run(self):
563 def run(self):
554 build_cmd.run(self)
564 build_cmd.run(self)
555 # this one will only fire for build commands
565 # this one will only fire for build commands
556 if hasattr(self, 'build_lib'):
566 if hasattr(self, 'build_lib'):
557 self._record_commit(self.build_lib)
567 self._record_commit(self.build_lib)
558
568
559 def make_release_tree(self, base_dir, files):
569 def make_release_tree(self, base_dir, files):
560 # this one will fire for sdist
570 # this one will fire for sdist
561 build_cmd.make_release_tree(self, base_dir, files)
571 build_cmd.make_release_tree(self, base_dir, files)
562 self._record_commit(base_dir)
572 self._record_commit(base_dir)
563
573
564 def _record_commit(self, base_dir):
574 def _record_commit(self, base_dir):
565 import subprocess
575 import subprocess
566 proc = subprocess.Popen('git rev-parse --short HEAD',
576 proc = subprocess.Popen('git rev-parse --short HEAD',
567 stdout=subprocess.PIPE,
577 stdout=subprocess.PIPE,
568 stderr=subprocess.PIPE,
578 stderr=subprocess.PIPE,
569 shell=True)
579 shell=True)
570 repo_commit, _ = proc.communicate()
580 repo_commit, _ = proc.communicate()
571 repo_commit = repo_commit.strip().decode("ascii")
581 repo_commit = repo_commit.strip().decode("ascii")
572
582
573 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
583 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
574 if os.path.isfile(out_pth) and not repo_commit:
584 if os.path.isfile(out_pth) and not repo_commit:
575 # nothing to write, don't clobber
585 # nothing to write, don't clobber
576 return
586 return
577
587
578 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
588 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
579
589
580 # remove to avoid overwriting original via hard link
590 # remove to avoid overwriting original via hard link
581 try:
591 try:
582 os.remove(out_pth)
592 os.remove(out_pth)
583 except (IOError, OSError):
593 except (IOError, OSError):
584 pass
594 pass
585 with open(out_pth, 'w') as out_file:
595 with open(out_pth, 'w') as out_file:
586 out_file.writelines([
596 out_file.writelines([
587 '# GENERATED BY setup.py\n',
597 '# GENERATED BY setup.py\n',
588 'commit = u"%s"\n' % repo_commit,
598 'commit = u"%s"\n' % repo_commit,
589 ])
599 ])
590 return require_submodules(MyBuildPy)
600 return require_submodules(MyBuildPy)
591
601
592
602
593 def require_submodules(command):
603 def require_submodules(command):
594 """decorator for instructing a command to check for submodules before running"""
604 """decorator for instructing a command to check for submodules before running"""
595 class DecoratedCommand(command):
605 class DecoratedCommand(command):
596 def run(self):
606 def run(self):
597 if not check_submodule_status(repo_root) == 'clean':
607 if not check_submodule_status(repo_root) == 'clean':
598 print("submodules missing! Run `setup.py submodule` and try again")
608 print("submodules missing! Run `setup.py submodule` and try again")
599 sys.exit(1)
609 sys.exit(1)
600 command.run(self)
610 command.run(self)
601 return DecoratedCommand
611 return DecoratedCommand
602
612
603 #---------------------------------------------------------------------------
613 #---------------------------------------------------------------------------
604 # bdist related
614 # bdist related
605 #---------------------------------------------------------------------------
615 #---------------------------------------------------------------------------
606
616
607 def get_bdist_wheel():
617 def get_bdist_wheel():
608 """Construct bdist_wheel command for building wheels
618 """Construct bdist_wheel command for building wheels
609
619
610 Constructs py2-none-any tag, instead of py2.7-none-any
620 Constructs py2-none-any tag, instead of py2.7-none-any
611 """
621 """
612 class RequiresWheel(Command):
622 class RequiresWheel(Command):
613 description = "Dummy command for missing bdist_wheel"
623 description = "Dummy command for missing bdist_wheel"
614 user_options = []
624 user_options = []
615
625
616 def initialize_options(self):
626 def initialize_options(self):
617 pass
627 pass
618
628
619 def finalize_options(self):
629 def finalize_options(self):
620 pass
630 pass
621
631
622 def run(self):
632 def run(self):
623 print("bdist_wheel requires the wheel package")
633 print("bdist_wheel requires the wheel package")
624 sys.exit(1)
634 sys.exit(1)
625
635
626 if 'setuptools' not in sys.modules:
636 if 'setuptools' not in sys.modules:
627 return RequiresWheel
637 return RequiresWheel
628 else:
638 else:
629 try:
639 try:
630 from wheel.bdist_wheel import bdist_wheel, read_pkg_info, write_pkg_info
640 from wheel.bdist_wheel import bdist_wheel, read_pkg_info, write_pkg_info
631 except ImportError:
641 except ImportError:
632 return RequiresWheel
642 return RequiresWheel
633
643
634 class bdist_wheel_tag(bdist_wheel):
644 class bdist_wheel_tag(bdist_wheel):
635
645
636 def add_requirements(self, metadata_path):
646 def add_requirements(self, metadata_path):
637 """transform platform-dependent requirements"""
647 """transform platform-dependent requirements"""
638 pkg_info = read_pkg_info(metadata_path)
648 pkg_info = read_pkg_info(metadata_path)
639 # pkg_info is an email.Message object (?!)
649 # pkg_info is an email.Message object (?!)
640 # we have to remove the unconditional 'readline' and/or 'pyreadline' entries
650 # we have to remove the unconditional 'readline' and/or 'pyreadline' entries
641 # and transform them to conditionals
651 # and transform them to conditionals
642 requires = pkg_info.get_all('Requires-Dist')
652 requires = pkg_info.get_all('Requires-Dist')
643 del pkg_info['Requires-Dist']
653 del pkg_info['Requires-Dist']
644 def _remove_startswith(lis, prefix):
654 def _remove_startswith(lis, prefix):
645 """like list.remove, but with startswith instead of =="""
655 """like list.remove, but with startswith instead of =="""
646 found = False
656 found = False
647 for idx, item in enumerate(lis):
657 for idx, item in enumerate(lis):
648 if item.startswith(prefix):
658 if item.startswith(prefix):
649 found = True
659 found = True
650 break
660 break
651 if found:
661 if found:
652 lis.pop(idx)
662 lis.pop(idx)
653
663
654 for pkg in ("gnureadline", "pyreadline", "mock"):
664 for pkg in ("gnureadline", "pyreadline", "mock"):
655 _remove_startswith(requires, pkg)
665 _remove_startswith(requires, pkg)
656 requires.append("gnureadline; sys.platform == 'darwin' and platform.python_implementation == 'CPython'")
666 requires.append("gnureadline; sys.platform == 'darwin' and platform.python_implementation == 'CPython'")
657 requires.append("pyreadline (>=2.0); extra == 'terminal' and sys.platform == 'win32' and platform.python_implementation == 'CPython'")
667 requires.append("pyreadline (>=2.0); extra == 'terminal' and sys.platform == 'win32' and platform.python_implementation == 'CPython'")
658 requires.append("pyreadline (>=2.0); extra == 'all' and sys.platform == 'win32' and platform.python_implementation == 'CPython'")
668 requires.append("pyreadline (>=2.0); extra == 'all' and sys.platform == 'win32' and platform.python_implementation == 'CPython'")
659 requires.append("mock; extra == 'test' and python_version < '3.3'")
669 requires.append("mock; extra == 'test' and python_version < '3.3'")
660 for r in requires:
670 for r in requires:
661 pkg_info['Requires-Dist'] = r
671 pkg_info['Requires-Dist'] = r
662 write_pkg_info(metadata_path, pkg_info)
672 write_pkg_info(metadata_path, pkg_info)
663
673
664 return bdist_wheel_tag
674 return bdist_wheel_tag
665
675
666 #---------------------------------------------------------------------------
676 #---------------------------------------------------------------------------
667 # Notebook related
677 # Notebook related
668 #---------------------------------------------------------------------------
678 #---------------------------------------------------------------------------
669
679
670 class CompileCSS(Command):
680 class CompileCSS(Command):
671 """Recompile Notebook CSS
681 """Recompile Notebook CSS
672
682
673 Regenerate the compiled CSS from LESS sources.
683 Regenerate the compiled CSS from LESS sources.
674
684
675 Requires various dev dependencies, such as invoke and lessc.
685 Requires various dev dependencies, such as invoke and lessc.
676 """
686 """
677 description = "Recompile Notebook CSS"
687 description = "Recompile Notebook CSS"
678 user_options = [
688 user_options = [
679 ('minify', 'x', "minify CSS"),
689 ('minify', 'x', "minify CSS"),
680 ('force', 'f', "force recompilation of CSS"),
690 ('force', 'f', "force recompilation of CSS"),
681 ]
691 ]
682
692
683 def initialize_options(self):
693 def initialize_options(self):
684 self.minify = False
694 self.minify = False
685 self.force = False
695 self.force = False
686
696
687 def finalize_options(self):
697 def finalize_options(self):
688 self.minify = bool(self.minify)
698 self.minify = bool(self.minify)
689 self.force = bool(self.force)
699 self.force = bool(self.force)
690
700
691 def run(self):
701 def run(self):
692 cmd = ['invoke', 'css']
702 cmd = ['invoke', 'css']
693 if self.minify:
703 if self.minify:
694 cmd.append('--minify')
704 cmd.append('--minify')
695 if self.force:
705 if self.force:
696 cmd.append('--force')
706 cmd.append('--force')
697 check_call(cmd, cwd=pjoin(repo_root, "IPython", "html"))
707 check_call(cmd, cwd=pjoin(repo_root, "IPython", "html"))
698
708
699
709
700 class JavascriptVersion(Command):
710 class JavascriptVersion(Command):
701 """write the javascript version to notebook javascript"""
711 """write the javascript version to notebook javascript"""
702 description = "Write IPython version to javascript"
712 description = "Write IPython version to javascript"
703 user_options = []
713 user_options = []
704
714
705 def initialize_options(self):
715 def initialize_options(self):
706 pass
716 pass
707
717
708 def finalize_options(self):
718 def finalize_options(self):
709 pass
719 pass
710
720
711 def run(self):
721 def run(self):
712 nsfile = pjoin(repo_root, "IPython", "html", "static", "base", "js", "namespace.js")
722 nsfile = pjoin(repo_root, "IPython", "html", "static", "base", "js", "namespace.js")
713 with open(nsfile) as f:
723 with open(nsfile) as f:
714 lines = f.readlines()
724 lines = f.readlines()
715 with open(nsfile, 'w') as f:
725 with open(nsfile, 'w') as f:
716 for line in lines:
726 for line in lines:
717 if line.startswith("IPython.version"):
727 if line.startswith("IPython.version"):
718 line = 'IPython.version = "{0}";\n'.format(version)
728 line = 'IPython.version = "{0}";\n'.format(version)
719 f.write(line)
729 f.write(line)
720
730
721
731
722 def css_js_prerelease(command, strict=True):
732 def css_js_prerelease(command, strict=True):
723 """decorator for building js/minified css prior to a release"""
733 """decorator for building js/minified css prior to a release"""
724 class DecoratedCommand(command):
734 class DecoratedCommand(command):
725 def run(self):
735 def run(self):
726 self.distribution.run_command('jsversion')
736 self.distribution.run_command('jsversion')
727 css = self.distribution.get_command_obj('css')
737 css = self.distribution.get_command_obj('css')
728 css.minify = True
738 css.minify = True
729 try:
739 try:
730 self.distribution.run_command('css')
740 self.distribution.run_command('css')
731 except Exception as e:
741 except Exception as e:
732 if strict:
742 if strict:
733 raise
743 raise
734 else:
744 else:
735 log.warn("Failed to build css sourcemaps: %s" % e)
745 log.warn("Failed to build css sourcemaps: %s" % e)
736 command.run(self)
746 command.run(self)
737 return DecoratedCommand
747 return DecoratedCommand
General Comments 0
You need to be logged in to leave comments. Login now