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