##// END OF EJS Templates
Install on Windows without using setuptools...
Thomas Kluyver -
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,747 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 363 """Find IPython's scripts.
364 364
365 365 if entry_points is True:
366 366 return setuptools entry_point-style definitions
367 367 else:
368 368 return file paths of plain scripts [default]
369 369
370 370 suffix is appended to script names if entry_points is True, so that the
371 371 Python 3 scripts get named "ipython3" etc.
372 372 """
373 373 ep = [
374 374 'ipython%s = IPython:start_ipython',
375 375 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
376 376 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
377 377 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
378 378 'iptest%s = IPython.testing.iptestcontroller:main',
379 379 ]
380 380 suffix = str(sys.version_info[0])
381 381 return [e % '' for e in ep] + [e % suffix for e in ep]
382 382
383 383 script_src = """#!{executable}
384 384 # This script was automatically generated by setup.py
385 385 if __name__ == '__main__':
386 386 from {mod} import {func}
387 387 {func}()
388 388 """
389 389
390 390 class build_scripts_entrypt(build_scripts):
391 391 def run(self):
392 392 self.mkpath(self.build_dir)
393 393 outfiles = []
394 394 for script in find_entry_points():
395 395 name, entrypt = script.split('=')
396 396 name = name.strip()
397 397 entrypt = entrypt.strip()
398 398 outfile = os.path.join(self.build_dir, name)
399 399 outfiles.append(outfile)
400 400 print('Writing script to', outfile)
401 401
402 402 mod, func = entrypt.split(':')
403 403 with open(outfile, 'w') as f:
404 404 f.write(script_src.format(executable=sys.executable,
405 405 mod=mod, func=func))
406
407 if sys.platform == 'win32':
408 # Write .cmd wrappers for Windows so 'ipython' etc. work at the
409 # command line
410 cmd_file = os.path.join(self.build_dir, name + '.cmd')
411 cmd = '@"{python}" "%~dp0\{script}" %*\r\n'.format(
412 python=sys.executable, script=name)
413 log.info("Writing %s wrapper script" % cmd_file)
414 with open(cmd_file, 'w') as f:
415 f.write(cmd)
406 416
407 417 return outfiles, outfiles
408 418
409 419 class install_lib_symlink(Command):
410 420 user_options = [
411 421 ('install-dir=', 'd', "directory to install to"),
412 422 ]
413 423
414 424 def initialize_options(self):
415 425 self.install_dir = None
416 426
417 427 def finalize_options(self):
418 428 self.set_undefined_options('symlink',
419 429 ('install_lib', 'install_dir'),
420 430 )
421 431
422 432 def run(self):
423 433 if sys.platform == 'win32':
424 434 raise Exception("This doesn't work on Windows.")
425 435 pkg = os.path.join(os.getcwd(), 'IPython')
426 436 dest = os.path.join(self.install_dir, 'IPython')
427 437 if os.path.islink(dest):
428 438 print('removing existing symlink at %s' % dest)
429 439 os.unlink(dest)
430 440 print('symlinking %s -> %s' % (pkg, dest))
431 441 os.symlink(pkg, dest)
432 442
433 443 class unsymlink(install):
434 444 def run(self):
435 445 dest = os.path.join(self.install_lib, 'IPython')
436 446 if os.path.islink(dest):
437 447 print('removing symlink at %s' % dest)
438 448 os.unlink(dest)
439 449 else:
440 450 print('No symlink exists at %s' % dest)
441 451
442 452 class install_symlinked(install):
443 453 def run(self):
444 454 if sys.platform == 'win32':
445 455 raise Exception("This doesn't work on Windows.")
446 456
447 457 # Run all sub-commands (at least those that need to be run)
448 458 for cmd_name in self.get_sub_commands():
449 459 self.run_command(cmd_name)
450 460
451 461 # 'sub_commands': a list of commands this command might have to run to
452 462 # get its work done. See cmd.py for more info.
453 463 sub_commands = [('install_lib_symlink', lambda self:True),
454 464 ('install_scripts_sym', lambda self:True),
455 465 ]
456 466
457 467 class install_scripts_for_symlink(install_scripts):
458 468 """Redefined to get options from 'symlink' instead of 'install'.
459 469
460 470 I love distutils almost as much as I love setuptools.
461 471 """
462 472 def finalize_options(self):
463 473 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
464 474 self.set_undefined_options('symlink',
465 475 ('install_scripts', 'install_dir'),
466 476 ('force', 'force'),
467 477 ('skip_build', 'skip_build'),
468 478 )
469 479
470 480 #---------------------------------------------------------------------------
471 481 # Verify all dependencies
472 482 #---------------------------------------------------------------------------
473 483
474 484 def check_for_dependencies():
475 485 """Check for IPython's dependencies.
476 486
477 487 This function should NOT be called if running under setuptools!
478 488 """
479 489 from setupext.setupext import (
480 490 print_line, print_raw, print_status,
481 491 check_for_sphinx, check_for_pygments,
482 492 check_for_nose, check_for_pexpect,
483 493 check_for_pyzmq, check_for_readline,
484 494 check_for_jinja2, check_for_tornado
485 495 )
486 496 print_line()
487 497 print_raw("BUILDING IPYTHON")
488 498 print_status('python', sys.version)
489 499 print_status('platform', sys.platform)
490 500 if sys.platform == 'win32':
491 501 print_status('Windows version', sys.getwindowsversion())
492 502
493 503 print_raw("")
494 504 print_raw("OPTIONAL DEPENDENCIES")
495 505
496 506 check_for_sphinx()
497 507 check_for_pygments()
498 508 check_for_nose()
499 509 if os.name == 'posix':
500 510 check_for_pexpect()
501 511 check_for_pyzmq()
502 512 check_for_tornado()
503 513 check_for_readline()
504 514 check_for_jinja2()
505 515
506 516 #---------------------------------------------------------------------------
507 517 # VCS related
508 518 #---------------------------------------------------------------------------
509 519
510 520 # utils.submodule has checks for submodule status
511 521 execfile(pjoin('IPython','utils','submodule.py'), globals())
512 522
513 523 class UpdateSubmodules(Command):
514 524 """Update git submodules
515 525
516 526 IPython's external javascript dependencies live in a separate repo.
517 527 """
518 528 description = "Update git submodules"
519 529 user_options = []
520 530
521 531 def initialize_options(self):
522 532 pass
523 533
524 534 def finalize_options(self):
525 535 pass
526 536
527 537 def run(self):
528 538 failure = False
529 539 try:
530 540 self.spawn('git submodule init'.split())
531 541 self.spawn('git submodule update --recursive'.split())
532 542 except Exception as e:
533 543 failure = e
534 544 print(e)
535 545
536 546 if not check_submodule_status(repo_root) == 'clean':
537 547 print("submodules could not be checked out")
538 548 sys.exit(1)
539 549
540 550
541 551 def git_prebuild(pkg_dir, build_cmd=build_py):
542 552 """Return extended build or sdist command class for recording commit
543 553
544 554 records git commit in IPython.utils._sysinfo.commit
545 555
546 556 for use in IPython.utils.sysinfo.sys_info() calls after installation.
547 557
548 558 Also ensures that submodules exist prior to running
549 559 """
550 560
551 561 class MyBuildPy(build_cmd):
552 562 ''' Subclass to write commit data into installation tree '''
553 563 def run(self):
554 564 build_cmd.run(self)
555 565 # this one will only fire for build commands
556 566 if hasattr(self, 'build_lib'):
557 567 self._record_commit(self.build_lib)
558 568
559 569 def make_release_tree(self, base_dir, files):
560 570 # this one will fire for sdist
561 571 build_cmd.make_release_tree(self, base_dir, files)
562 572 self._record_commit(base_dir)
563 573
564 574 def _record_commit(self, base_dir):
565 575 import subprocess
566 576 proc = subprocess.Popen('git rev-parse --short HEAD',
567 577 stdout=subprocess.PIPE,
568 578 stderr=subprocess.PIPE,
569 579 shell=True)
570 580 repo_commit, _ = proc.communicate()
571 581 repo_commit = repo_commit.strip().decode("ascii")
572 582
573 583 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
574 584 if os.path.isfile(out_pth) and not repo_commit:
575 585 # nothing to write, don't clobber
576 586 return
577 587
578 588 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
579 589
580 590 # remove to avoid overwriting original via hard link
581 591 try:
582 592 os.remove(out_pth)
583 593 except (IOError, OSError):
584 594 pass
585 595 with open(out_pth, 'w') as out_file:
586 596 out_file.writelines([
587 597 '# GENERATED BY setup.py\n',
588 598 'commit = u"%s"\n' % repo_commit,
589 599 ])
590 600 return require_submodules(MyBuildPy)
591 601
592 602
593 603 def require_submodules(command):
594 604 """decorator for instructing a command to check for submodules before running"""
595 605 class DecoratedCommand(command):
596 606 def run(self):
597 607 if not check_submodule_status(repo_root) == 'clean':
598 608 print("submodules missing! Run `setup.py submodule` and try again")
599 609 sys.exit(1)
600 610 command.run(self)
601 611 return DecoratedCommand
602 612
603 613 #---------------------------------------------------------------------------
604 614 # bdist related
605 615 #---------------------------------------------------------------------------
606 616
607 617 def get_bdist_wheel():
608 618 """Construct bdist_wheel command for building wheels
609 619
610 620 Constructs py2-none-any tag, instead of py2.7-none-any
611 621 """
612 622 class RequiresWheel(Command):
613 623 description = "Dummy command for missing bdist_wheel"
614 624 user_options = []
615 625
616 626 def initialize_options(self):
617 627 pass
618 628
619 629 def finalize_options(self):
620 630 pass
621 631
622 632 def run(self):
623 633 print("bdist_wheel requires the wheel package")
624 634 sys.exit(1)
625 635
626 636 if 'setuptools' not in sys.modules:
627 637 return RequiresWheel
628 638 else:
629 639 try:
630 640 from wheel.bdist_wheel import bdist_wheel, read_pkg_info, write_pkg_info
631 641 except ImportError:
632 642 return RequiresWheel
633 643
634 644 class bdist_wheel_tag(bdist_wheel):
635 645
636 646 def add_requirements(self, metadata_path):
637 647 """transform platform-dependent requirements"""
638 648 pkg_info = read_pkg_info(metadata_path)
639 649 # pkg_info is an email.Message object (?!)
640 650 # we have to remove the unconditional 'readline' and/or 'pyreadline' entries
641 651 # and transform them to conditionals
642 652 requires = pkg_info.get_all('Requires-Dist')
643 653 del pkg_info['Requires-Dist']
644 654 def _remove_startswith(lis, prefix):
645 655 """like list.remove, but with startswith instead of =="""
646 656 found = False
647 657 for idx, item in enumerate(lis):
648 658 if item.startswith(prefix):
649 659 found = True
650 660 break
651 661 if found:
652 662 lis.pop(idx)
653 663
654 664 for pkg in ("gnureadline", "pyreadline", "mock"):
655 665 _remove_startswith(requires, pkg)
656 666 requires.append("gnureadline; sys.platform == 'darwin' and platform.python_implementation == 'CPython'")
657 667 requires.append("pyreadline (>=2.0); extra == 'terminal' and sys.platform == 'win32' and platform.python_implementation == 'CPython'")
658 668 requires.append("pyreadline (>=2.0); extra == 'all' and sys.platform == 'win32' and platform.python_implementation == 'CPython'")
659 669 requires.append("mock; extra == 'test' and python_version < '3.3'")
660 670 for r in requires:
661 671 pkg_info['Requires-Dist'] = r
662 672 write_pkg_info(metadata_path, pkg_info)
663 673
664 674 return bdist_wheel_tag
665 675
666 676 #---------------------------------------------------------------------------
667 677 # Notebook related
668 678 #---------------------------------------------------------------------------
669 679
670 680 class CompileCSS(Command):
671 681 """Recompile Notebook CSS
672 682
673 683 Regenerate the compiled CSS from LESS sources.
674 684
675 685 Requires various dev dependencies, such as invoke and lessc.
676 686 """
677 687 description = "Recompile Notebook CSS"
678 688 user_options = [
679 689 ('minify', 'x', "minify CSS"),
680 690 ('force', 'f', "force recompilation of CSS"),
681 691 ]
682 692
683 693 def initialize_options(self):
684 694 self.minify = False
685 695 self.force = False
686 696
687 697 def finalize_options(self):
688 698 self.minify = bool(self.minify)
689 699 self.force = bool(self.force)
690 700
691 701 def run(self):
692 702 cmd = ['invoke', 'css']
693 703 if self.minify:
694 704 cmd.append('--minify')
695 705 if self.force:
696 706 cmd.append('--force')
697 707 check_call(cmd, cwd=pjoin(repo_root, "IPython", "html"))
698 708
699 709
700 710 class JavascriptVersion(Command):
701 711 """write the javascript version to notebook javascript"""
702 712 description = "Write IPython version to javascript"
703 713 user_options = []
704 714
705 715 def initialize_options(self):
706 716 pass
707 717
708 718 def finalize_options(self):
709 719 pass
710 720
711 721 def run(self):
712 722 nsfile = pjoin(repo_root, "IPython", "html", "static", "base", "js", "namespace.js")
713 723 with open(nsfile) as f:
714 724 lines = f.readlines()
715 725 with open(nsfile, 'w') as f:
716 726 for line in lines:
717 727 if line.startswith("IPython.version"):
718 728 line = 'IPython.version = "{0}";\n'.format(version)
719 729 f.write(line)
720 730
721 731
722 732 def css_js_prerelease(command, strict=True):
723 733 """decorator for building js/minified css prior to a release"""
724 734 class DecoratedCommand(command):
725 735 def run(self):
726 736 self.distribution.run_command('jsversion')
727 737 css = self.distribution.get_command_obj('css')
728 738 css.minify = True
729 739 try:
730 740 self.distribution.run_command('css')
731 741 except Exception as e:
732 742 if strict:
733 743 raise
734 744 else:
735 745 log.warn("Failed to build css sourcemaps: %s" % e)
736 746 command.run(self)
737 747 return DecoratedCommand
General Comments 0
You need to be logged in to leave comments. Login now