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