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