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