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