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