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