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