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