##// END OF EJS Templates
Rework setup to allow installing on Python 2 and 3....
Thomas Kluyver -
Show More
@@ -1,335 +1,340 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.txt, distributed with this software.
17 # The full license is in the file COPYING.txt, 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 if sys.version_info[:2] < (2,7):
29 if sys.version_info[:2] < (2,7):
30 error = "ERROR: IPython requires Python Version 2.7 or above."
30 error = "ERROR: IPython requires Python Version 2.7 or above."
31 print(error, file=sys.stderr)
31 print(error, file=sys.stderr)
32 sys.exit(1)
32 sys.exit(1)
33
33
34 PY3 = (sys.version_info[0] >= 3)
34 PY3 = (sys.version_info[0] >= 3)
35
35
36 # At least we're on the python version we need, move on.
36 # At least we're on the python version we need, move on.
37
37
38 #-------------------------------------------------------------------------------
38 #-------------------------------------------------------------------------------
39 # Imports
39 # Imports
40 #-------------------------------------------------------------------------------
40 #-------------------------------------------------------------------------------
41
41
42 # Stdlib imports
42 # Stdlib imports
43 import os
43 import os
44 import shutil
44 import shutil
45
45
46 from glob import glob
46 from glob import glob
47
47
48 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
48 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
49 # update it when the contents of directories change.
49 # update it when the contents of directories change.
50 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
50 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
51
51
52 from distutils.core import setup
52 from distutils.core import setup
53
53
54 # Our own imports
54 # Our own imports
55 from setupbase import target_update
55 from setupbase import target_update
56
56
57 from setupbase import (
57 from setupbase import (
58 setup_args,
58 setup_args,
59 find_packages,
59 find_packages,
60 find_package_data,
60 find_package_data,
61 find_scripts,
61 find_entry_points,
62 build_scripts_rename,
62 build_scripts_entrypt,
63 find_data_files,
63 find_data_files,
64 check_for_dependencies,
64 check_for_dependencies,
65 git_prebuild,
65 git_prebuild,
66 check_submodule_status,
66 check_submodule_status,
67 update_submodules,
67 update_submodules,
68 require_submodules,
68 require_submodules,
69 UpdateSubmodules,
69 UpdateSubmodules,
70 CompileCSS,
70 CompileCSS,
71 install_symlinked,
72 install_lib_symlink,
73 install_scripts_for_symlink,
71 )
74 )
72 from setupext import setupext
75 from setupext import setupext
73
76
74 isfile = os.path.isfile
77 isfile = os.path.isfile
75 pjoin = os.path.join
78 pjoin = os.path.join
76
79
77 #-----------------------------------------------------------------------------
80 #-----------------------------------------------------------------------------
78 # Function definitions
81 # Function definitions
79 #-----------------------------------------------------------------------------
82 #-----------------------------------------------------------------------------
80
83
81 def cleanup():
84 def cleanup():
82 """Clean up the junk left around by the build process"""
85 """Clean up the junk left around by the build process"""
83 if "develop" not in sys.argv and "egg_info" not in sys.argv:
86 if "develop" not in sys.argv and "egg_info" not in sys.argv:
84 try:
87 try:
85 shutil.rmtree('ipython.egg-info')
88 shutil.rmtree('ipython.egg-info')
86 except:
89 except:
87 try:
90 try:
88 os.unlink('ipython.egg-info')
91 os.unlink('ipython.egg-info')
89 except:
92 except:
90 pass
93 pass
91
94
92 #-------------------------------------------------------------------------------
95 #-------------------------------------------------------------------------------
93 # Handle OS specific things
96 # Handle OS specific things
94 #-------------------------------------------------------------------------------
97 #-------------------------------------------------------------------------------
95
98
96 if os.name in ('nt','dos'):
99 if os.name in ('nt','dos'):
97 os_name = 'windows'
100 os_name = 'windows'
98 else:
101 else:
99 os_name = os.name
102 os_name = os.name
100
103
101 # Under Windows, 'sdist' has not been supported. Now that the docs build with
104 # Under Windows, 'sdist' has not been supported. Now that the docs build with
102 # Sphinx it might work, but let's not turn it on until someone confirms that it
105 # Sphinx it might work, but let's not turn it on until someone confirms that it
103 # actually works.
106 # actually works.
104 if os_name == 'windows' and 'sdist' in sys.argv:
107 if os_name == 'windows' and 'sdist' in sys.argv:
105 print('The sdist command is not available under Windows. Exiting.')
108 print('The sdist command is not available under Windows. Exiting.')
106 sys.exit(1)
109 sys.exit(1)
107
110
108 #-------------------------------------------------------------------------------
111 #-------------------------------------------------------------------------------
109 # Make sure we aren't trying to run without submodules
112 # Make sure we aren't trying to run without submodules
110 #-------------------------------------------------------------------------------
113 #-------------------------------------------------------------------------------
111 here = os.path.abspath(os.path.dirname(__file__))
114 here = os.path.abspath(os.path.dirname(__file__))
112
115
113 def require_clean_submodules():
116 def require_clean_submodules():
114 """Check on git submodules before distutils can do anything
117 """Check on git submodules before distutils can do anything
115
118
116 Since distutils cannot be trusted to update the tree
119 Since distutils cannot be trusted to update the tree
117 after everything has been set in motion,
120 after everything has been set in motion,
118 this is not a distutils command.
121 this is not a distutils command.
119 """
122 """
120 # PACKAGERS: Add a return here to skip checks for git submodules
123 # PACKAGERS: Add a return here to skip checks for git submodules
121
124
122 # don't do anything if nothing is actually supposed to happen
125 # don't do anything if nothing is actually supposed to happen
123 for do_nothing in ('-h', '--help', '--help-commands', 'clean', 'submodule'):
126 for do_nothing in ('-h', '--help', '--help-commands', 'clean', 'submodule'):
124 if do_nothing in sys.argv:
127 if do_nothing in sys.argv:
125 return
128 return
126
129
127 status = check_submodule_status(here)
130 status = check_submodule_status(here)
128
131
129 if status == "missing":
132 if status == "missing":
130 print("checking out submodules for the first time")
133 print("checking out submodules for the first time")
131 update_submodules(here)
134 update_submodules(here)
132 elif status == "unclean":
135 elif status == "unclean":
133 print('\n'.join([
136 print('\n'.join([
134 "Cannot build / install IPython with unclean submodules",
137 "Cannot build / install IPython with unclean submodules",
135 "Please update submodules with",
138 "Please update submodules with",
136 " python setup.py submodule",
139 " python setup.py submodule",
137 "or",
140 "or",
138 " git submodule update",
141 " git submodule update",
139 "or commit any submodule changes you have made."
142 "or commit any submodule changes you have made."
140 ]))
143 ]))
141 sys.exit(1)
144 sys.exit(1)
142
145
143 require_clean_submodules()
146 require_clean_submodules()
144
147
145 #-------------------------------------------------------------------------------
148 #-------------------------------------------------------------------------------
146 # Things related to the IPython documentation
149 # Things related to the IPython documentation
147 #-------------------------------------------------------------------------------
150 #-------------------------------------------------------------------------------
148
151
149 # update the manuals when building a source dist
152 # update the manuals when building a source dist
150 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
153 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
151 import textwrap
152
154
153 # List of things to be updated. Each entry is a triplet of args for
155 # List of things to be updated. Each entry is a triplet of args for
154 # target_update()
156 # target_update()
155 to_update = [
157 to_update = [
156 # FIXME - Disabled for now: we need to redo an automatic way
158 # FIXME - Disabled for now: we need to redo an automatic way
157 # of generating the magic info inside the rst.
159 # of generating the magic info inside the rst.
158 #('docs/magic.tex',
160 #('docs/magic.tex',
159 #['IPython/Magic.py'],
161 #['IPython/Magic.py'],
160 #"cd doc && ./update_magic.sh" ),
162 #"cd doc && ./update_magic.sh" ),
161
163
162 ('docs/man/ipcluster.1.gz',
164 ('docs/man/ipcluster.1.gz',
163 ['docs/man/ipcluster.1'],
165 ['docs/man/ipcluster.1'],
164 'cd docs/man && gzip -9c ipcluster.1 > ipcluster.1.gz'),
166 'cd docs/man && gzip -9c ipcluster.1 > ipcluster.1.gz'),
165
167
166 ('docs/man/ipcontroller.1.gz',
168 ('docs/man/ipcontroller.1.gz',
167 ['docs/man/ipcontroller.1'],
169 ['docs/man/ipcontroller.1'],
168 'cd docs/man && gzip -9c ipcontroller.1 > ipcontroller.1.gz'),
170 'cd docs/man && gzip -9c ipcontroller.1 > ipcontroller.1.gz'),
169
171
170 ('docs/man/ipengine.1.gz',
172 ('docs/man/ipengine.1.gz',
171 ['docs/man/ipengine.1'],
173 ['docs/man/ipengine.1'],
172 'cd docs/man && gzip -9c ipengine.1 > ipengine.1.gz'),
174 'cd docs/man && gzip -9c ipengine.1 > ipengine.1.gz'),
173
175
174 ('docs/man/iplogger.1.gz',
176 ('docs/man/iplogger.1.gz',
175 ['docs/man/iplogger.1'],
177 ['docs/man/iplogger.1'],
176 'cd docs/man && gzip -9c iplogger.1 > iplogger.1.gz'),
178 'cd docs/man && gzip -9c iplogger.1 > iplogger.1.gz'),
177
179
178 ('docs/man/ipython.1.gz',
180 ('docs/man/ipython.1.gz',
179 ['docs/man/ipython.1'],
181 ['docs/man/ipython.1'],
180 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
182 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
181
183
182 ('docs/man/irunner.1.gz',
184 ('docs/man/irunner.1.gz',
183 ['docs/man/irunner.1'],
185 ['docs/man/irunner.1'],
184 'cd docs/man && gzip -9c irunner.1 > irunner.1.gz'),
186 'cd docs/man && gzip -9c irunner.1 > irunner.1.gz'),
185 ]
187 ]
186
188
187
189
188 [ target_update(*t) for t in to_update ]
190 [ target_update(*t) for t in to_update ]
189
191
190 #---------------------------------------------------------------------------
192 #---------------------------------------------------------------------------
191 # Find all the packages, package data, and data_files
193 # Find all the packages, package data, and data_files
192 #---------------------------------------------------------------------------
194 #---------------------------------------------------------------------------
193
195
194 packages = find_packages()
196 packages = find_packages()
195 package_data = find_package_data()
197 package_data = find_package_data()
196 data_files = find_data_files()
198 data_files = find_data_files()
197
199
198 setup_args['packages'] = packages
200 setup_args['packages'] = packages
199 setup_args['package_data'] = package_data
201 setup_args['package_data'] = package_data
200 setup_args['data_files'] = data_files
202 setup_args['data_files'] = data_files
201
203
202 #---------------------------------------------------------------------------
204 #---------------------------------------------------------------------------
203 # custom distutils commands
205 # custom distutils commands
204 #---------------------------------------------------------------------------
206 #---------------------------------------------------------------------------
205 # 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
206 from distutils.command.sdist import sdist
208 from distutils.command.sdist import sdist
207 from distutils.command.upload import upload
209 from distutils.command.upload import upload
208
210
209 class UploadWindowsInstallers(upload):
211 class UploadWindowsInstallers(upload):
210
212
211 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)"
212 user_options = upload.user_options + [
214 user_options = upload.user_options + [
213 ('files=', 'f', 'exe file (or glob) to upload')
215 ('files=', 'f', 'exe file (or glob) to upload')
214 ]
216 ]
215 def initialize_options(self):
217 def initialize_options(self):
216 upload.initialize_options(self)
218 upload.initialize_options(self)
217 meta = self.distribution.metadata
219 meta = self.distribution.metadata
218 base = '{name}-{version}'.format(
220 base = '{name}-{version}'.format(
219 name=meta.get_name(),
221 name=meta.get_name(),
220 version=meta.get_version()
222 version=meta.get_version()
221 )
223 )
222 self.files = os.path.join('dist', '%s.*.exe' % base)
224 self.files = os.path.join('dist', '%s.*.exe' % base)
223
225
224 def run(self):
226 def run(self):
225 for dist_file in glob(self.files):
227 for dist_file in glob(self.files):
226 self.upload_file('bdist_wininst', 'any', dist_file)
228 self.upload_file('bdist_wininst', 'any', dist_file)
227
229
228 setup_args['cmdclass'] = {
230 setup_args['cmdclass'] = {
229 'build_py': git_prebuild('IPython'),
231 'build_py': git_prebuild('IPython'),
230 'sdist' : git_prebuild('IPython', sdist),
232 'sdist' : git_prebuild('IPython', sdist),
231 'upload_wininst' : UploadWindowsInstallers,
233 'upload_wininst' : UploadWindowsInstallers,
232 'submodule' : UpdateSubmodules,
234 'submodule' : UpdateSubmodules,
233 'css' : CompileCSS,
235 'css' : CompileCSS,
236 'symlink': install_symlinked,
237 'install_lib_symlink': install_lib_symlink,
238 'install_scripts_sym': install_scripts_for_symlink,
234 }
239 }
235
240
236 #---------------------------------------------------------------------------
241 #---------------------------------------------------------------------------
237 # Handle scripts, dependencies, and setuptools specific things
242 # Handle scripts, dependencies, and setuptools specific things
238 #---------------------------------------------------------------------------
243 #---------------------------------------------------------------------------
239
244
240 # For some commands, use setuptools. Note that we do NOT list install here!
245 # For some commands, use setuptools. Note that we do NOT list install here!
241 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
246 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
242 needs_setuptools = set(('develop', 'release', 'bdist_egg', 'bdist_rpm',
247 needs_setuptools = set(('develop', 'release', 'bdist_egg', 'bdist_rpm',
243 'bdist', 'bdist_dumb', 'bdist_wininst', 'install_egg_info',
248 'bdist', 'bdist_dumb', 'bdist_wininst', 'install_egg_info',
244 'egg_info', 'easy_install', 'upload',
249 'egg_info', 'easy_install', 'upload',
245 ))
250 ))
246 if sys.platform == 'win32':
251 if sys.platform == 'win32':
247 # Depend on setuptools for install on *Windows only*
252 # Depend on setuptools for install on *Windows only*
248 # If we get script-installation working without setuptools,
253 # If we get script-installation working without setuptools,
249 # then we can back off, but until then use it.
254 # then we can back off, but until then use it.
250 # See Issue #369 on GitHub for more
255 # See Issue #369 on GitHub for more
251 needs_setuptools.add('install')
256 needs_setuptools.add('install')
252
257
253 if len(needs_setuptools.intersection(sys.argv)) > 0:
258 if len(needs_setuptools.intersection(sys.argv)) > 0:
254 import setuptools
259 import setuptools
255
260
256 # This dict is used for passing extra arguments that are setuptools
261 # This dict is used for passing extra arguments that are setuptools
257 # specific to setup
262 # specific to setup
258 setuptools_extra_args = {}
263 setuptools_extra_args = {}
259
264
260 if 'setuptools' in sys.modules:
265 if 'setuptools' in sys.modules:
261 # setup.py develop should check for submodules
266 # setup.py develop should check for submodules
262 from setuptools.command.develop import develop
267 from setuptools.command.develop import develop
263 setup_args['cmdclass']['develop'] = require_submodules(develop)
268 setup_args['cmdclass']['develop'] = require_submodules(develop)
264
269
265 setuptools_extra_args['zip_safe'] = False
270 setuptools_extra_args['zip_safe'] = False
266 setuptools_extra_args['entry_points'] = find_scripts(True, suffix = '3' if PY3 else '')
271 setuptools_extra_args['entry_points'] = {'console_scripts':find_entry_points()}
267 setup_args['extras_require'] = dict(
272 setup_args['extras_require'] = dict(
268 parallel = 'pyzmq>=2.1.11',
273 parallel = 'pyzmq>=2.1.11',
269 qtconsole = ['pyzmq>=2.1.11', 'pygments'],
274 qtconsole = ['pyzmq>=2.1.11', 'pygments'],
270 zmq = 'pyzmq>=2.1.11',
275 zmq = 'pyzmq>=2.1.11',
271 doc = 'Sphinx>=0.3',
276 doc = 'Sphinx>=0.3',
272 test = 'nose>=0.10.1',
277 test = 'nose>=0.10.1',
273 notebook = ['tornado>=3.1', 'pyzmq>=2.1.11', 'jinja2'],
278 notebook = ['tornado>=3.1', 'pyzmq>=2.1.11', 'jinja2'],
274 nbconvert = ['pygments', 'jinja2', 'Sphinx>=0.3']
279 nbconvert = ['pygments', 'jinja2', 'Sphinx>=0.3']
275 )
280 )
276 everything = set()
281 everything = set()
277 for deps in setup_args['extras_require'].values():
282 for deps in setup_args['extras_require'].values():
278 if not isinstance(deps, list):
283 if not isinstance(deps, list):
279 deps = [deps]
284 deps = [deps]
280 for dep in deps:
285 for dep in deps:
281 everything.add(dep)
286 everything.add(dep)
282 setup_args['extras_require']['all'] = everything
287 setup_args['extras_require']['all'] = everything
283
288
284 requires = setup_args.setdefault('install_requires', [])
289 requires = setup_args.setdefault('install_requires', [])
285 setupext.display_status = False
290 setupext.display_status = False
286 if not setupext.check_for_readline():
291 if not setupext.check_for_readline():
287 if sys.platform == 'darwin':
292 if sys.platform == 'darwin':
288 requires.append('readline')
293 requires.append('readline')
289 elif sys.platform.startswith('win'):
294 elif sys.platform.startswith('win'):
290 # Pyreadline 64 bit windows issue solved in versions >=1.7.1
295 # Pyreadline 64 bit windows issue solved in versions >=1.7.1
291 # Also solves issues with some older versions of pyreadline that
296 # Also solves issues with some older versions of pyreadline that
292 # satisfy the unconstrained depdendency.
297 # satisfy the unconstrained depdendency.
293 requires.append('pyreadline>=1.7.1')
298 requires.append('pyreadline>=1.7.1')
294 else:
299 else:
295 pass
300 pass
296 # do we want to install readline here?
301 # do we want to install readline here?
297
302
298 # Script to be run by the windows binary installer after the default setup
303 # Script to be run by the windows binary installer after the default setup
299 # routine, to add shortcuts and similar windows-only things. Windows
304 # routine, to add shortcuts and similar windows-only things. Windows
300 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
305 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
301 # doesn't find them.
306 # doesn't find them.
302 if 'bdist_wininst' in sys.argv:
307 if 'bdist_wininst' in sys.argv:
303 if len(sys.argv) > 2 and \
308 if len(sys.argv) > 2 and \
304 ('sdist' in sys.argv or 'bdist_rpm' in sys.argv):
309 ('sdist' in sys.argv or 'bdist_rpm' in sys.argv):
305 print >> sys.stderr, "ERROR: bdist_wininst must be run alone. Exiting."
310 print >> sys.stderr, "ERROR: bdist_wininst must be run alone. Exiting."
306 sys.exit(1)
311 sys.exit(1)
307 setup_args['data_files'].append(
312 setup_args['data_files'].append(
308 ['Scripts', ('scripts/ipython.ico', 'scripts/ipython_nb.ico')])
313 ['Scripts', ('scripts/ipython.ico', 'scripts/ipython_nb.ico')])
309 setup_args['scripts'] = [pjoin('scripts','ipython_win_post_install.py')]
314 setup_args['scripts'] = [pjoin('scripts','ipython_win_post_install.py')]
310 setup_args['options'] = {"bdist_wininst":
315 setup_args['options'] = {"bdist_wininst":
311 {"install_script":
316 {"install_script":
312 "ipython_win_post_install.py"}}
317 "ipython_win_post_install.py"}}
313
318
314 else:
319 else:
315 # If we are running without setuptools, call this function which will
320 # If we are running without setuptools, call this function which will
316 # check for dependencies an inform the user what is needed. This is
321 # check for dependencies an inform the user what is needed. This is
317 # just to make life easy for users.
322 # just to make life easy for users.
318 check_for_dependencies()
323 check_for_dependencies()
319 setup_args['scripts'] = find_scripts(False)
324 # scripts has to be a non-empty list, or install_scripts isn't called
320 if PY3:
325 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
321 # Rename scripts with '3' suffix
326
322 setup_args['cmdclass']['build_scripts'] = build_scripts_rename
327 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
323
328
324 #---------------------------------------------------------------------------
329 #---------------------------------------------------------------------------
325 # Do the actual setup now
330 # Do the actual setup now
326 #---------------------------------------------------------------------------
331 #---------------------------------------------------------------------------
327
332
328 setup_args.update(setuptools_extra_args)
333 setup_args.update(setuptools_extra_args)
329
334
330 def main():
335 def main():
331 setup(**setup_args)
336 setup(**setup_args)
332 cleanup()
337 cleanup()
333
338
334 if __name__ == '__main__':
339 if __name__ == '__main__':
335 main()
340 main()
@@ -1,519 +1,565 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 from __future__ import print_function
11 from __future__ import print_function
12
12
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14 # Copyright (C) 2008 The IPython Development Team
14 # Copyright (C) 2008 The IPython Development Team
15 #
15 #
16 # Distributed under the terms of the BSD License. The full license is in
16 # Distributed under the terms of the BSD License. The full license is in
17 # the file COPYING, distributed as part of this software.
17 # the file COPYING, distributed as part of this software.
18 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
19
19
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21 # Imports
21 # Imports
22 #-------------------------------------------------------------------------------
22 #-------------------------------------------------------------------------------
23 import errno
23 import os
24 import os
24 import sys
25 import sys
25
26
26 try:
27 from configparser import ConfigParser
28 except:
29 from ConfigParser import ConfigParser
30 from distutils.command.build_py import build_py
27 from distutils.command.build_py import build_py
31 from distutils.command.build_scripts import build_scripts
28 from distutils.command.build_scripts import build_scripts
29 from distutils.command.install import install
30 from distutils.command.install_scripts import install_scripts
32 from distutils.cmd import Command
31 from distutils.cmd import Command
33 from glob import glob
32 from glob import glob
34 from subprocess import call
33 from subprocess import call
35
34
36 from setupext import install_data_ext
35 from setupext import install_data_ext
37
36
38 #-------------------------------------------------------------------------------
37 #-------------------------------------------------------------------------------
39 # Useful globals and utility functions
38 # Useful globals and utility functions
40 #-------------------------------------------------------------------------------
39 #-------------------------------------------------------------------------------
41
40
42 # A few handy globals
41 # A few handy globals
43 isfile = os.path.isfile
42 isfile = os.path.isfile
44 pjoin = os.path.join
43 pjoin = os.path.join
45 repo_root = os.path.dirname(os.path.abspath(__file__))
44 repo_root = os.path.dirname(os.path.abspath(__file__))
46
45
47 def oscmd(s):
46 def oscmd(s):
48 print(">", s)
47 print(">", s)
49 os.system(s)
48 os.system(s)
50
49
51 # Py3 compatibility hacks, without assuming IPython itself is installed with
50 # Py3 compatibility hacks, without assuming IPython itself is installed with
52 # the full py3compat machinery.
51 # the full py3compat machinery.
53
52
54 try:
53 try:
55 execfile
54 execfile
56 except NameError:
55 except NameError:
57 def execfile(fname, globs, locs=None):
56 def execfile(fname, globs, locs=None):
58 locs = locs or globs
57 locs = locs or globs
59 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
58 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
60
59
61 # A little utility we'll need below, since glob() does NOT allow you to do
60 # A little utility we'll need below, since glob() does NOT allow you to do
62 # exclusion on multiple endings!
61 # exclusion on multiple endings!
63 def file_doesnt_endwith(test,endings):
62 def file_doesnt_endwith(test,endings):
64 """Return true if test is a file and its name does NOT end with any
63 """Return true if test is a file and its name does NOT end with any
65 of the strings listed in endings."""
64 of the strings listed in endings."""
66 if not isfile(test):
65 if not isfile(test):
67 return False
66 return False
68 for e in endings:
67 for e in endings:
69 if test.endswith(e):
68 if test.endswith(e):
70 return False
69 return False
71 return True
70 return True
72
71
73 #---------------------------------------------------------------------------
72 #---------------------------------------------------------------------------
74 # Basic project information
73 # Basic project information
75 #---------------------------------------------------------------------------
74 #---------------------------------------------------------------------------
76
75
77 # release.py contains version, authors, license, url, keywords, etc.
76 # release.py contains version, authors, license, url, keywords, etc.
78 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
77 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
79
78
80 # Create a dict with the basic information
79 # Create a dict with the basic information
81 # This dict is eventually passed to setup after additional keys are added.
80 # This dict is eventually passed to setup after additional keys are added.
82 setup_args = dict(
81 setup_args = dict(
83 name = name,
82 name = name,
84 version = version,
83 version = version,
85 description = description,
84 description = description,
86 long_description = long_description,
85 long_description = long_description,
87 author = author,
86 author = author,
88 author_email = author_email,
87 author_email = author_email,
89 url = url,
88 url = url,
90 download_url = download_url,
89 download_url = download_url,
91 license = license,
90 license = license,
92 platforms = platforms,
91 platforms = platforms,
93 keywords = keywords,
92 keywords = keywords,
94 classifiers = classifiers,
93 classifiers = classifiers,
95 cmdclass = {'install_data': install_data_ext},
94 cmdclass = {'install_data': install_data_ext},
96 )
95 )
97
96
98
97
99 #---------------------------------------------------------------------------
98 #---------------------------------------------------------------------------
100 # Find packages
99 # Find packages
101 #---------------------------------------------------------------------------
100 #---------------------------------------------------------------------------
102
101
103 def find_packages():
102 def find_packages():
104 """
103 """
105 Find all of IPython's packages.
104 Find all of IPython's packages.
106 """
105 """
107 excludes = ['deathrow', 'quarantine']
106 excludes = ['deathrow', 'quarantine']
108 packages = []
107 packages = []
109 for dir,subdirs,files in os.walk('IPython'):
108 for dir,subdirs,files in os.walk('IPython'):
110 package = dir.replace(os.path.sep, '.')
109 package = dir.replace(os.path.sep, '.')
111 if any(package.startswith('IPython.'+exc) for exc in excludes):
110 if any(package.startswith('IPython.'+exc) for exc in excludes):
112 # package is to be excluded (e.g. deathrow)
111 # package is to be excluded (e.g. deathrow)
113 continue
112 continue
114 if '__init__.py' not in files:
113 if '__init__.py' not in files:
115 # not a package
114 # not a package
116 continue
115 continue
117 packages.append(package)
116 packages.append(package)
118 return packages
117 return packages
119
118
120 #---------------------------------------------------------------------------
119 #---------------------------------------------------------------------------
121 # Find package data
120 # Find package data
122 #---------------------------------------------------------------------------
121 #---------------------------------------------------------------------------
123
122
124 def find_package_data():
123 def find_package_data():
125 """
124 """
126 Find IPython's package_data.
125 Find IPython's package_data.
127 """
126 """
128 # This is not enough for these things to appear in an sdist.
127 # This is not enough for these things to appear in an sdist.
129 # We need to muck with the MANIFEST to get this to work
128 # We need to muck with the MANIFEST to get this to work
130
129
131 # exclude static things that we don't ship (e.g. mathjax)
130 # exclude static things that we don't ship (e.g. mathjax)
132 excludes = ['mathjax']
131 excludes = ['mathjax']
133
132
134 # add 'static/' prefix to exclusions, and tuplify for use in startswith
133 # add 'static/' prefix to exclusions, and tuplify for use in startswith
135 excludes = tuple([os.path.join('static', ex) for ex in excludes])
134 excludes = tuple([os.path.join('static', ex) for ex in excludes])
136
135
137 # walk notebook resources:
136 # walk notebook resources:
138 cwd = os.getcwd()
137 cwd = os.getcwd()
139 os.chdir(os.path.join('IPython', 'html'))
138 os.chdir(os.path.join('IPython', 'html'))
140 static_walk = list(os.walk('static'))
139 static_walk = list(os.walk('static'))
141 static_data = []
140 static_data = []
142 for parent, dirs, files in static_walk:
141 for parent, dirs, files in static_walk:
143 if parent.startswith(excludes):
142 if parent.startswith(excludes):
144 continue
143 continue
145 for f in files:
144 for f in files:
146 static_data.append(os.path.join(parent, f))
145 static_data.append(os.path.join(parent, f))
147
146
148 os.chdir(os.path.join('tests',))
147 os.chdir(os.path.join('tests',))
149 js_tests = glob('casperjs/*.*') + glob('casperjs/*/*')
148 js_tests = glob('casperjs/*.*') + glob('casperjs/*/*')
150 os.chdir(cwd)
149 os.chdir(cwd)
151
150
152 package_data = {
151 package_data = {
153 'IPython.config.profile' : ['README*', '*/*.py'],
152 'IPython.config.profile' : ['README*', '*/*.py'],
154 'IPython.core.tests' : ['*.png', '*.jpg'],
153 'IPython.core.tests' : ['*.png', '*.jpg'],
155 'IPython.testing' : ['*.txt'],
154 'IPython.testing' : ['*.txt'],
156 'IPython.testing.plugin' : ['*.txt'],
155 'IPython.testing.plugin' : ['*.txt'],
157 'IPython.html' : ['templates/*'] + static_data,
156 'IPython.html' : ['templates/*'] + static_data,
158 'IPython.html.tests' : js_tests,
157 'IPython.html.tests' : js_tests,
159 'IPython.qt.console' : ['resources/icon/*.svg'],
158 'IPython.qt.console' : ['resources/icon/*.svg'],
160 'IPython.nbconvert' : ['templates/*.tpl', 'templates/latex/*.tplx',
159 'IPython.nbconvert' : ['templates/*.tpl', 'templates/latex/*.tplx',
161 'templates/latex/skeleton/*.tplx', 'templates/skeleton/*',
160 'templates/latex/skeleton/*.tplx', 'templates/skeleton/*',
162 'templates/reveal_internals/*.tpl', 'tests/files/*.*',
161 'templates/reveal_internals/*.tpl', 'tests/files/*.*',
163 'exporters/tests/files/*.*'],
162 'exporters/tests/files/*.*'],
164 'IPython.nbformat' : ['tests/*.ipynb']
163 'IPython.nbformat' : ['tests/*.ipynb']
165 }
164 }
166 return package_data
165 return package_data
167
166
168
167
169 #---------------------------------------------------------------------------
168 #---------------------------------------------------------------------------
170 # Find data files
169 # Find data files
171 #---------------------------------------------------------------------------
170 #---------------------------------------------------------------------------
172
171
173 def make_dir_struct(tag,base,out_base):
172 def make_dir_struct(tag,base,out_base):
174 """Make the directory structure of all files below a starting dir.
173 """Make the directory structure of all files below a starting dir.
175
174
176 This is just a convenience routine to help build a nested directory
175 This is just a convenience routine to help build a nested directory
177 hierarchy because distutils is too stupid to do this by itself.
176 hierarchy because distutils is too stupid to do this by itself.
178
177
179 XXX - this needs a proper docstring!
178 XXX - this needs a proper docstring!
180 """
179 """
181
180
182 # we'll use these a lot below
181 # we'll use these a lot below
183 lbase = len(base)
182 lbase = len(base)
184 pathsep = os.path.sep
183 pathsep = os.path.sep
185 lpathsep = len(pathsep)
184 lpathsep = len(pathsep)
186
185
187 out = []
186 out = []
188 for (dirpath,dirnames,filenames) in os.walk(base):
187 for (dirpath,dirnames,filenames) in os.walk(base):
189 # we need to strip out the dirpath from the base to map it to the
188 # we need to strip out the dirpath from the base to map it to the
190 # output (installation) path. This requires possibly stripping the
189 # output (installation) path. This requires possibly stripping the
191 # path separator, because otherwise pjoin will not work correctly
190 # path separator, because otherwise pjoin will not work correctly
192 # (pjoin('foo/','/bar') returns '/bar').
191 # (pjoin('foo/','/bar') returns '/bar').
193
192
194 dp_eff = dirpath[lbase:]
193 dp_eff = dirpath[lbase:]
195 if dp_eff.startswith(pathsep):
194 if dp_eff.startswith(pathsep):
196 dp_eff = dp_eff[lpathsep:]
195 dp_eff = dp_eff[lpathsep:]
197 # The output path must be anchored at the out_base marker
196 # The output path must be anchored at the out_base marker
198 out_path = pjoin(out_base,dp_eff)
197 out_path = pjoin(out_base,dp_eff)
199 # Now we can generate the final filenames. Since os.walk only produces
198 # Now we can generate the final filenames. Since os.walk only produces
200 # filenames, we must join back with the dirpath to get full valid file
199 # filenames, we must join back with the dirpath to get full valid file
201 # paths:
200 # paths:
202 pfiles = [pjoin(dirpath,f) for f in filenames]
201 pfiles = [pjoin(dirpath,f) for f in filenames]
203 # Finally, generate the entry we need, which is a pari of (output
202 # Finally, generate the entry we need, which is a pari of (output
204 # path, files) for use as a data_files parameter in install_data.
203 # path, files) for use as a data_files parameter in install_data.
205 out.append((out_path, pfiles))
204 out.append((out_path, pfiles))
206
205
207 return out
206 return out
208
207
209
208
210 def find_data_files():
209 def find_data_files():
211 """
210 """
212 Find IPython's data_files.
211 Find IPython's data_files.
213
212
214 Most of these are docs.
213 Most of these are docs.
215 """
214 """
216
215
217 docdirbase = pjoin('share', 'doc', 'ipython')
216 docdirbase = pjoin('share', 'doc', 'ipython')
218 manpagebase = pjoin('share', 'man', 'man1')
217 manpagebase = pjoin('share', 'man', 'man1')
219
218
220 # Simple file lists can be made by hand
219 # Simple file lists can be made by hand
221 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
220 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
222 if not manpages:
221 if not manpages:
223 # When running from a source tree, the manpages aren't gzipped
222 # When running from a source tree, the manpages aren't gzipped
224 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
223 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
225
224
226 igridhelpfiles = [f for f in glob(pjoin('IPython','extensions','igrid_help.*')) if isfile(f)]
225 igridhelpfiles = [f for f in glob(pjoin('IPython','extensions','igrid_help.*')) if isfile(f)]
227
226
228 # For nested structures, use the utility above
227 # For nested structures, use the utility above
229 example_files = make_dir_struct(
228 example_files = make_dir_struct(
230 'data',
229 'data',
231 pjoin('docs','examples'),
230 pjoin('docs','examples'),
232 pjoin(docdirbase,'examples')
231 pjoin(docdirbase,'examples')
233 )
232 )
234 manual_files = make_dir_struct(
233 manual_files = make_dir_struct(
235 'data',
234 'data',
236 pjoin('docs','html'),
235 pjoin('docs','html'),
237 pjoin(docdirbase,'manual')
236 pjoin(docdirbase,'manual')
238 )
237 )
239
238
240 # And assemble the entire output list
239 # And assemble the entire output list
241 data_files = [ (manpagebase, manpages),
240 data_files = [ (manpagebase, manpages),
242 (pjoin(docdirbase, 'extensions'), igridhelpfiles),
241 (pjoin(docdirbase, 'extensions'), igridhelpfiles),
243 ] + manual_files + example_files
242 ] + manual_files + example_files
244
243
245 return data_files
244 return data_files
246
245
247
246
248 def make_man_update_target(manpage):
247 def make_man_update_target(manpage):
249 """Return a target_update-compliant tuple for the given manpage.
248 """Return a target_update-compliant tuple for the given manpage.
250
249
251 Parameters
250 Parameters
252 ----------
251 ----------
253 manpage : string
252 manpage : string
254 Name of the manpage, must include the section number (trailing number).
253 Name of the manpage, must include the section number (trailing number).
255
254
256 Example
255 Example
257 -------
256 -------
258
257
259 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
258 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
260 ('docs/man/ipython.1.gz',
259 ('docs/man/ipython.1.gz',
261 ['docs/man/ipython.1'],
260 ['docs/man/ipython.1'],
262 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
261 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
263 """
262 """
264 man_dir = pjoin('docs', 'man')
263 man_dir = pjoin('docs', 'man')
265 manpage_gz = manpage + '.gz'
264 manpage_gz = manpage + '.gz'
266 manpath = pjoin(man_dir, manpage)
265 manpath = pjoin(man_dir, manpage)
267 manpath_gz = pjoin(man_dir, manpage_gz)
266 manpath_gz = pjoin(man_dir, manpage_gz)
268 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
267 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
269 locals() )
268 locals() )
270 return (manpath_gz, [manpath], gz_cmd)
269 return (manpath_gz, [manpath], gz_cmd)
271
270
272 # The two functions below are copied from IPython.utils.path, so we don't need
271 # The two functions below are copied from IPython.utils.path, so we don't need
273 # to import IPython during setup, which fails on Python 3.
272 # to import IPython during setup, which fails on Python 3.
274
273
275 def target_outdated(target,deps):
274 def target_outdated(target,deps):
276 """Determine whether a target is out of date.
275 """Determine whether a target is out of date.
277
276
278 target_outdated(target,deps) -> 1/0
277 target_outdated(target,deps) -> 1/0
279
278
280 deps: list of filenames which MUST exist.
279 deps: list of filenames which MUST exist.
281 target: single filename which may or may not exist.
280 target: single filename which may or may not exist.
282
281
283 If target doesn't exist or is older than any file listed in deps, return
282 If target doesn't exist or is older than any file listed in deps, return
284 true, otherwise return false.
283 true, otherwise return false.
285 """
284 """
286 try:
285 try:
287 target_time = os.path.getmtime(target)
286 target_time = os.path.getmtime(target)
288 except os.error:
287 except os.error:
289 return 1
288 return 1
290 for dep in deps:
289 for dep in deps:
291 dep_time = os.path.getmtime(dep)
290 dep_time = os.path.getmtime(dep)
292 if dep_time > target_time:
291 if dep_time > target_time:
293 #print "For target",target,"Dep failed:",dep # dbg
292 #print "For target",target,"Dep failed:",dep # dbg
294 #print "times (dep,tar):",dep_time,target_time # dbg
293 #print "times (dep,tar):",dep_time,target_time # dbg
295 return 1
294 return 1
296 return 0
295 return 0
297
296
298
297
299 def target_update(target,deps,cmd):
298 def target_update(target,deps,cmd):
300 """Update a target with a given command given a list of dependencies.
299 """Update a target with a given command given a list of dependencies.
301
300
302 target_update(target,deps,cmd) -> runs cmd if target is outdated.
301 target_update(target,deps,cmd) -> runs cmd if target is outdated.
303
302
304 This is just a wrapper around target_outdated() which calls the given
303 This is just a wrapper around target_outdated() which calls the given
305 command if target is outdated."""
304 command if target is outdated."""
306
305
307 if target_outdated(target,deps):
306 if target_outdated(target,deps):
308 os.system(cmd)
307 os.system(cmd)
309
308
310 #---------------------------------------------------------------------------
309 #---------------------------------------------------------------------------
311 # Find scripts
310 # Find scripts
312 #---------------------------------------------------------------------------
311 #---------------------------------------------------------------------------
313
312
314 def find_scripts(entry_points=False, suffix=''):
313 def find_entry_points():
315 """Find IPython's scripts.
314 """Find IPython's scripts.
316
315
317 if entry_points is True:
316 if entry_points is True:
318 return setuptools entry_point-style definitions
317 return setuptools entry_point-style definitions
319 else:
318 else:
320 return file paths of plain scripts [default]
319 return file paths of plain scripts [default]
321
320
322 suffix is appended to script names if entry_points is True, so that the
321 suffix is appended to script names if entry_points is True, so that the
323 Python 3 scripts get named "ipython3" etc.
322 Python 3 scripts get named "ipython3" etc.
324 """
323 """
325 if entry_points:
324 ep = [
326 console_scripts = [s % suffix for s in [
327 'ipython%s = IPython:start_ipython',
325 'ipython%s = IPython:start_ipython',
328 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
326 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
329 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
327 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
330 'iplogger%s = IPython.parallel.apps.iploggerapp:launch_new_instance',
328 'iplogger%s = IPython.parallel.apps.iploggerapp:launch_new_instance',
331 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
329 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
332 'iptest%s = IPython.testing.iptestcontroller:main',
330 'iptest%s = IPython.testing.iptestcontroller:main',
333 'irunner%s = IPython.lib.irunner:main',
331 'irunner%s = IPython.lib.irunner:main',
334 ]]
335 gui_scripts = []
336 scripts = dict(console_scripts=console_scripts, gui_scripts=gui_scripts)
337 else:
338 parallel_scripts = pjoin('IPython','parallel','scripts')
339 main_scripts = pjoin('IPython','scripts')
340 scripts = [
341 pjoin(parallel_scripts, 'ipengine'),
342 pjoin(parallel_scripts, 'ipcontroller'),
343 pjoin(parallel_scripts, 'ipcluster'),
344 pjoin(parallel_scripts, 'iplogger'),
345 pjoin(main_scripts, 'ipython'),
346 pjoin(main_scripts, 'irunner'),
347 pjoin(main_scripts, 'iptest')
348 ]
332 ]
349 return scripts
333 suffix = str(sys.version_info[0])
334 return [e % '' for e in ep] + [e % suffix for e in ep]
335
336 script_src = """#!{executable}
337 from {mod} import {func}
338 {func}()
339 """
340
341 class build_scripts_entrypt(build_scripts):
342 def run(self):
343 self.mkpath(self.build_dir)
344 outfiles = []
345 for script in find_entry_points():
346 name, entrypt = script.split('=')
347 name = name.strip()
348 entrypt = entrypt.strip()
349 outfile = os.path.join(self.build_dir, name)
350 outfiles.append(outfile)
351 print('Writing script to', outfile)
352
353 mod, func = entrypt.split(':')
354 with open(outfile, 'w') as f:
355 f.write(script_src.format(executable=sys.executable,
356 mod=mod, func=func))
357
358 return outfiles, outfiles
359
360 class install_lib_symlink(Command):
361 user_options = [
362 ('install-dir=', 'd', "directory to install to"),
363 ]
364
365 def initialize_options(self):
366 self.install_dir = None
350
367
351 class build_scripts_rename(build_scripts):
368 def finalize_options(self):
352 """Use this on Python 3 to rename scripts to ipython3 etc."""
369 self.set_undefined_options('symlink',
353 _suffix = '3'
370 ('install_lib', 'install_dir'),
371 )
372
373 def run(self):
374 if sys.platform == 'win32':
375 raise Exception("This doesn't work on Windows.")
376 pkg = os.path.join(os.getcwd(), 'IPython')
377 dest = os.path.join(self.install_dir, 'IPython')
378 print('symlinking %s -> %s' % (pkg, dest))
379 try:
380 os.symlink(pkg, dest)
381 except OSError as e:
382 if e.errno == errno.EEXIST:
383 print('ALREADY EXISTS')
384 else:
385 raise
386
387 class install_symlinked(install):
388 def run(self):
389 if sys.platform == 'win32':
390 raise Exception("This doesn't work on Windows.")
391 install.run(self)
354
392
355 def copy_scripts(self):
393 # 'sub_commands': a list of commands this command might have to run to
356 outfiles, updated_files = super(build_scripts_rename, self).copy_scripts()
394 # get its work done. See cmd.py for more info.
357 new_outfiles = [p + self._suffix for p in outfiles]
395 sub_commands = [('install_lib_symlink', lambda self:True),
358 updated_files = [p + self._suffix for p in updated_files]
396 ('install_scripts_sym', lambda self:True),
359 for old, new in zip(outfiles, new_outfiles):
397 ]
360 if os.path.exists(new):
398
361 os.unlink(new)
399 class install_scripts_for_symlink(install_scripts):
362 self.move_file(old, new)
400 """Redefined to get options from 'symlink' instead of 'install'.
363 return new_outfiles, updated_files
401
364
402 I love distutils almost as much as I love setuptools.
403 """
404 def finalize_options(self):
405 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
406 self.set_undefined_options('symlink',
407 ('install_scripts', 'install_dir'),
408 ('force', 'force'),
409 ('skip_build', 'skip_build'),
410 )
365
411
366 #---------------------------------------------------------------------------
412 #---------------------------------------------------------------------------
367 # Verify all dependencies
413 # Verify all dependencies
368 #---------------------------------------------------------------------------
414 #---------------------------------------------------------------------------
369
415
370 def check_for_dependencies():
416 def check_for_dependencies():
371 """Check for IPython's dependencies.
417 """Check for IPython's dependencies.
372
418
373 This function should NOT be called if running under setuptools!
419 This function should NOT be called if running under setuptools!
374 """
420 """
375 from setupext.setupext import (
421 from setupext.setupext import (
376 print_line, print_raw, print_status,
422 print_line, print_raw, print_status,
377 check_for_sphinx, check_for_pygments,
423 check_for_sphinx, check_for_pygments,
378 check_for_nose, check_for_pexpect,
424 check_for_nose, check_for_pexpect,
379 check_for_pyzmq, check_for_readline,
425 check_for_pyzmq, check_for_readline,
380 check_for_jinja2, check_for_tornado
426 check_for_jinja2, check_for_tornado
381 )
427 )
382 print_line()
428 print_line()
383 print_raw("BUILDING IPYTHON")
429 print_raw("BUILDING IPYTHON")
384 print_status('python', sys.version)
430 print_status('python', sys.version)
385 print_status('platform', sys.platform)
431 print_status('platform', sys.platform)
386 if sys.platform == 'win32':
432 if sys.platform == 'win32':
387 print_status('Windows version', sys.getwindowsversion())
433 print_status('Windows version', sys.getwindowsversion())
388
434
389 print_raw("")
435 print_raw("")
390 print_raw("OPTIONAL DEPENDENCIES")
436 print_raw("OPTIONAL DEPENDENCIES")
391
437
392 check_for_sphinx()
438 check_for_sphinx()
393 check_for_pygments()
439 check_for_pygments()
394 check_for_nose()
440 check_for_nose()
395 check_for_pexpect()
441 check_for_pexpect()
396 check_for_pyzmq()
442 check_for_pyzmq()
397 check_for_tornado()
443 check_for_tornado()
398 check_for_readline()
444 check_for_readline()
399 check_for_jinja2()
445 check_for_jinja2()
400
446
401 #---------------------------------------------------------------------------
447 #---------------------------------------------------------------------------
402 # VCS related
448 # VCS related
403 #---------------------------------------------------------------------------
449 #---------------------------------------------------------------------------
404
450
405 # utils.submodule has checks for submodule status
451 # utils.submodule has checks for submodule status
406 execfile(pjoin('IPython','utils','submodule.py'), globals())
452 execfile(pjoin('IPython','utils','submodule.py'), globals())
407
453
408 class UpdateSubmodules(Command):
454 class UpdateSubmodules(Command):
409 """Update git submodules
455 """Update git submodules
410
456
411 IPython's external javascript dependencies live in a separate repo.
457 IPython's external javascript dependencies live in a separate repo.
412 """
458 """
413 description = "Update git submodules"
459 description = "Update git submodules"
414 user_options = []
460 user_options = []
415
461
416 def initialize_options(self):
462 def initialize_options(self):
417 pass
463 pass
418
464
419 def finalize_options(self):
465 def finalize_options(self):
420 pass
466 pass
421
467
422 def run(self):
468 def run(self):
423 failure = False
469 failure = False
424 try:
470 try:
425 self.spawn('git submodule init'.split())
471 self.spawn('git submodule init'.split())
426 self.spawn('git submodule update --recursive'.split())
472 self.spawn('git submodule update --recursive'.split())
427 except Exception as e:
473 except Exception as e:
428 failure = e
474 failure = e
429 print(e)
475 print(e)
430
476
431 if not check_submodule_status(repo_root) == 'clean':
477 if not check_submodule_status(repo_root) == 'clean':
432 print("submodules could not be checked out")
478 print("submodules could not be checked out")
433 sys.exit(1)
479 sys.exit(1)
434
480
435
481
436 def git_prebuild(pkg_dir, build_cmd=build_py):
482 def git_prebuild(pkg_dir, build_cmd=build_py):
437 """Return extended build or sdist command class for recording commit
483 """Return extended build or sdist command class for recording commit
438
484
439 records git commit in IPython.utils._sysinfo.commit
485 records git commit in IPython.utils._sysinfo.commit
440
486
441 for use in IPython.utils.sysinfo.sys_info() calls after installation.
487 for use in IPython.utils.sysinfo.sys_info() calls after installation.
442
488
443 Also ensures that submodules exist prior to running
489 Also ensures that submodules exist prior to running
444 """
490 """
445
491
446 class MyBuildPy(build_cmd):
492 class MyBuildPy(build_cmd):
447 ''' Subclass to write commit data into installation tree '''
493 ''' Subclass to write commit data into installation tree '''
448 def run(self):
494 def run(self):
449 build_cmd.run(self)
495 build_cmd.run(self)
450 # this one will only fire for build commands
496 # this one will only fire for build commands
451 if hasattr(self, 'build_lib'):
497 if hasattr(self, 'build_lib'):
452 self._record_commit(self.build_lib)
498 self._record_commit(self.build_lib)
453
499
454 def make_release_tree(self, base_dir, files):
500 def make_release_tree(self, base_dir, files):
455 # this one will fire for sdist
501 # this one will fire for sdist
456 build_cmd.make_release_tree(self, base_dir, files)
502 build_cmd.make_release_tree(self, base_dir, files)
457 self._record_commit(base_dir)
503 self._record_commit(base_dir)
458
504
459 def _record_commit(self, base_dir):
505 def _record_commit(self, base_dir):
460 import subprocess
506 import subprocess
461 proc = subprocess.Popen('git rev-parse --short HEAD',
507 proc = subprocess.Popen('git rev-parse --short HEAD',
462 stdout=subprocess.PIPE,
508 stdout=subprocess.PIPE,
463 stderr=subprocess.PIPE,
509 stderr=subprocess.PIPE,
464 shell=True)
510 shell=True)
465 repo_commit, _ = proc.communicate()
511 repo_commit, _ = proc.communicate()
466 repo_commit = repo_commit.strip().decode("ascii")
512 repo_commit = repo_commit.strip().decode("ascii")
467
513
468 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
514 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
469 if os.path.isfile(out_pth) and not repo_commit:
515 if os.path.isfile(out_pth) and not repo_commit:
470 # nothing to write, don't clobber
516 # nothing to write, don't clobber
471 return
517 return
472
518
473 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
519 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
474
520
475 # remove to avoid overwriting original via hard link
521 # remove to avoid overwriting original via hard link
476 try:
522 try:
477 os.remove(out_pth)
523 os.remove(out_pth)
478 except (IOError, OSError):
524 except (IOError, OSError):
479 pass
525 pass
480 with open(out_pth, 'w') as out_file:
526 with open(out_pth, 'w') as out_file:
481 out_file.writelines([
527 out_file.writelines([
482 '# GENERATED BY setup.py\n',
528 '# GENERATED BY setup.py\n',
483 'commit = "%s"\n' % repo_commit,
529 'commit = "%s"\n' % repo_commit,
484 ])
530 ])
485 return require_submodules(MyBuildPy)
531 return require_submodules(MyBuildPy)
486
532
487
533
488 def require_submodules(command):
534 def require_submodules(command):
489 """decorator for instructing a command to check for submodules before running"""
535 """decorator for instructing a command to check for submodules before running"""
490 class DecoratedCommand(command):
536 class DecoratedCommand(command):
491 def run(self):
537 def run(self):
492 if not check_submodule_status(repo_root) == 'clean':
538 if not check_submodule_status(repo_root) == 'clean':
493 print("submodules missing! Run `setup.py submodule` and try again")
539 print("submodules missing! Run `setup.py submodule` and try again")
494 sys.exit(1)
540 sys.exit(1)
495 command.run(self)
541 command.run(self)
496 return DecoratedCommand
542 return DecoratedCommand
497
543
498 #---------------------------------------------------------------------------
544 #---------------------------------------------------------------------------
499 # Notebook related
545 # Notebook related
500 #---------------------------------------------------------------------------
546 #---------------------------------------------------------------------------
501
547
502 class CompileCSS(Command):
548 class CompileCSS(Command):
503 """Recompile Notebook CSS
549 """Recompile Notebook CSS
504
550
505 Regenerate the compiled CSS from LESS sources.
551 Regenerate the compiled CSS from LESS sources.
506
552
507 Requires various dev dependencies, such as fabric and lessc.
553 Requires various dev dependencies, such as fabric and lessc.
508 """
554 """
509 description = "Recompile Notebook CSS"
555 description = "Recompile Notebook CSS"
510 user_options = []
556 user_options = []
511
557
512 def initialize_options(self):
558 def initialize_options(self):
513 pass
559 pass
514
560
515 def finalize_options(self):
561 def finalize_options(self):
516 pass
562 pass
517
563
518 def run(self):
564 def run(self):
519 call("fab css", shell=True, cwd=pjoin(repo_root, "IPython", "html"))
565 call("fab css", shell=True, cwd=pjoin(repo_root, "IPython", "html"))
General Comments 0
You need to be logged in to leave comments. Login now