##// END OF EJS Templates
Fix iptest setuptools entry point
Thomas Kluyver -
Show More
@@ -1,476 +1,476 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 This module defines the things that are used in setup.py for building IPython
3 This module defines the things that are used in setup.py for building IPython
4
4
5 This includes:
5 This includes:
6
6
7 * The basic arguments to setup
7 * The basic arguments to setup
8 * Functions for finding things like packages, package data, etc.
8 * Functions for finding things like packages, package data, etc.
9 * A function for checking dependencies.
9 * A function for checking dependencies.
10 """
10 """
11 from __future__ import print_function
11 from __future__ import print_function
12
12
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14 # Copyright (C) 2008 The IPython Development Team
14 # Copyright (C) 2008 The IPython Development Team
15 #
15 #
16 # Distributed under the terms of the BSD License. The full license is in
16 # Distributed under the terms of the BSD License. The full license is in
17 # the file COPYING, distributed as part of this software.
17 # the file COPYING, distributed as part of this software.
18 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
19
19
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21 # Imports
21 # Imports
22 #-------------------------------------------------------------------------------
22 #-------------------------------------------------------------------------------
23 import os
23 import os
24 import sys
24 import sys
25
25
26 try:
26 try:
27 from configparser import ConfigParser
27 from configparser import ConfigParser
28 except:
28 except:
29 from ConfigParser import ConfigParser
29 from ConfigParser import ConfigParser
30 from distutils.command.build_py import build_py
30 from distutils.command.build_py import build_py
31 from distutils.cmd import Command
31 from distutils.cmd import Command
32 from glob import glob
32 from glob import glob
33
33
34 from setupext import install_data_ext
34 from setupext import install_data_ext
35
35
36 #-------------------------------------------------------------------------------
36 #-------------------------------------------------------------------------------
37 # Useful globals and utility functions
37 # Useful globals and utility functions
38 #-------------------------------------------------------------------------------
38 #-------------------------------------------------------------------------------
39
39
40 # A few handy globals
40 # A few handy globals
41 isfile = os.path.isfile
41 isfile = os.path.isfile
42 pjoin = os.path.join
42 pjoin = os.path.join
43 repo_root = os.path.dirname(os.path.abspath(__file__))
43 repo_root = os.path.dirname(os.path.abspath(__file__))
44
44
45 def oscmd(s):
45 def oscmd(s):
46 print(">", s)
46 print(">", s)
47 os.system(s)
47 os.system(s)
48
48
49 # Py3 compatibility hacks, without assuming IPython itself is installed with
49 # Py3 compatibility hacks, without assuming IPython itself is installed with
50 # the full py3compat machinery.
50 # the full py3compat machinery.
51
51
52 try:
52 try:
53 execfile
53 execfile
54 except NameError:
54 except NameError:
55 def execfile(fname, globs, locs=None):
55 def execfile(fname, globs, locs=None):
56 locs = locs or globs
56 locs = locs or globs
57 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
57 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
58
58
59 # A little utility we'll need below, since glob() does NOT allow you to do
59 # A little utility we'll need below, since glob() does NOT allow you to do
60 # exclusion on multiple endings!
60 # exclusion on multiple endings!
61 def file_doesnt_endwith(test,endings):
61 def file_doesnt_endwith(test,endings):
62 """Return true if test is a file and its name does NOT end with any
62 """Return true if test is a file and its name does NOT end with any
63 of the strings listed in endings."""
63 of the strings listed in endings."""
64 if not isfile(test):
64 if not isfile(test):
65 return False
65 return False
66 for e in endings:
66 for e in endings:
67 if test.endswith(e):
67 if test.endswith(e):
68 return False
68 return False
69 return True
69 return True
70
70
71 #---------------------------------------------------------------------------
71 #---------------------------------------------------------------------------
72 # Basic project information
72 # Basic project information
73 #---------------------------------------------------------------------------
73 #---------------------------------------------------------------------------
74
74
75 # release.py contains version, authors, license, url, keywords, etc.
75 # release.py contains version, authors, license, url, keywords, etc.
76 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
76 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
77
77
78 # Create a dict with the basic information
78 # Create a dict with the basic information
79 # This dict is eventually passed to setup after additional keys are added.
79 # This dict is eventually passed to setup after additional keys are added.
80 setup_args = dict(
80 setup_args = dict(
81 name = name,
81 name = name,
82 version = version,
82 version = version,
83 description = description,
83 description = description,
84 long_description = long_description,
84 long_description = long_description,
85 author = author,
85 author = author,
86 author_email = author_email,
86 author_email = author_email,
87 url = url,
87 url = url,
88 download_url = download_url,
88 download_url = download_url,
89 license = license,
89 license = license,
90 platforms = platforms,
90 platforms = platforms,
91 keywords = keywords,
91 keywords = keywords,
92 classifiers = classifiers,
92 classifiers = classifiers,
93 cmdclass = {'install_data': install_data_ext},
93 cmdclass = {'install_data': install_data_ext},
94 )
94 )
95
95
96
96
97 #---------------------------------------------------------------------------
97 #---------------------------------------------------------------------------
98 # Find packages
98 # Find packages
99 #---------------------------------------------------------------------------
99 #---------------------------------------------------------------------------
100
100
101 def find_packages():
101 def find_packages():
102 """
102 """
103 Find all of IPython's packages.
103 Find all of IPython's packages.
104 """
104 """
105 excludes = ['deathrow', 'quarantine']
105 excludes = ['deathrow', 'quarantine']
106 packages = []
106 packages = []
107 for dir,subdirs,files in os.walk('IPython'):
107 for dir,subdirs,files in os.walk('IPython'):
108 package = dir.replace(os.path.sep, '.')
108 package = dir.replace(os.path.sep, '.')
109 if any(package.startswith('IPython.'+exc) for exc in excludes):
109 if any(package.startswith('IPython.'+exc) for exc in excludes):
110 # package is to be excluded (e.g. deathrow)
110 # package is to be excluded (e.g. deathrow)
111 continue
111 continue
112 if '__init__.py' not in files:
112 if '__init__.py' not in files:
113 # not a package
113 # not a package
114 continue
114 continue
115 packages.append(package)
115 packages.append(package)
116 return packages
116 return packages
117
117
118 #---------------------------------------------------------------------------
118 #---------------------------------------------------------------------------
119 # Find package data
119 # Find package data
120 #---------------------------------------------------------------------------
120 #---------------------------------------------------------------------------
121
121
122 def find_package_data():
122 def find_package_data():
123 """
123 """
124 Find IPython's package_data.
124 Find IPython's package_data.
125 """
125 """
126 # This is not enough for these things to appear in an sdist.
126 # This is not enough for these things to appear in an sdist.
127 # We need to muck with the MANIFEST to get this to work
127 # We need to muck with the MANIFEST to get this to work
128
128
129 # exclude static things that we don't ship (e.g. mathjax)
129 # exclude static things that we don't ship (e.g. mathjax)
130 excludes = ['mathjax']
130 excludes = ['mathjax']
131
131
132 # add 'static/' prefix to exclusions, and tuplify for use in startswith
132 # add 'static/' prefix to exclusions, and tuplify for use in startswith
133 excludes = tuple([os.path.join('static', ex) for ex in excludes])
133 excludes = tuple([os.path.join('static', ex) for ex in excludes])
134
134
135 # walk notebook resources:
135 # walk notebook resources:
136 cwd = os.getcwd()
136 cwd = os.getcwd()
137 os.chdir(os.path.join('IPython', 'html'))
137 os.chdir(os.path.join('IPython', 'html'))
138 static_walk = list(os.walk('static'))
138 static_walk = list(os.walk('static'))
139 os.chdir(cwd)
139 os.chdir(cwd)
140 static_data = []
140 static_data = []
141 for parent, dirs, files in static_walk:
141 for parent, dirs, files in static_walk:
142 if parent.startswith(excludes):
142 if parent.startswith(excludes):
143 continue
143 continue
144 for f in files:
144 for f in files:
145 static_data.append(os.path.join(parent, f))
145 static_data.append(os.path.join(parent, f))
146
146
147 package_data = {
147 package_data = {
148 'IPython.config.profile' : ['README*', '*/*.py'],
148 'IPython.config.profile' : ['README*', '*/*.py'],
149 'IPython.core.tests' : ['*.png', '*.jpg'],
149 'IPython.core.tests' : ['*.png', '*.jpg'],
150 'IPython.testing' : ['*.txt'],
150 'IPython.testing' : ['*.txt'],
151 'IPython.testing.plugin' : ['*.txt'],
151 'IPython.testing.plugin' : ['*.txt'],
152 'IPython.html' : ['templates/*'] + static_data,
152 'IPython.html' : ['templates/*'] + static_data,
153 'IPython.qt.console' : ['resources/icon/*.svg'],
153 'IPython.qt.console' : ['resources/icon/*.svg'],
154 'IPython.nbconvert' : ['templates/*.tpl', 'templates/latex/*.tplx',
154 'IPython.nbconvert' : ['templates/*.tpl', 'templates/latex/*.tplx',
155 'templates/latex/skeleton/*.tplx', 'templates/skeleton/*',
155 'templates/latex/skeleton/*.tplx', 'templates/skeleton/*',
156 'templates/reveal_internals/*.tpl', 'tests/files/*.*',
156 'templates/reveal_internals/*.tpl', 'tests/files/*.*',
157 'exporters/tests/files/*.*']
157 'exporters/tests/files/*.*']
158 }
158 }
159 return package_data
159 return package_data
160
160
161
161
162 #---------------------------------------------------------------------------
162 #---------------------------------------------------------------------------
163 # Find data files
163 # Find data files
164 #---------------------------------------------------------------------------
164 #---------------------------------------------------------------------------
165
165
166 def make_dir_struct(tag,base,out_base):
166 def make_dir_struct(tag,base,out_base):
167 """Make the directory structure of all files below a starting dir.
167 """Make the directory structure of all files below a starting dir.
168
168
169 This is just a convenience routine to help build a nested directory
169 This is just a convenience routine to help build a nested directory
170 hierarchy because distutils is too stupid to do this by itself.
170 hierarchy because distutils is too stupid to do this by itself.
171
171
172 XXX - this needs a proper docstring!
172 XXX - this needs a proper docstring!
173 """
173 """
174
174
175 # we'll use these a lot below
175 # we'll use these a lot below
176 lbase = len(base)
176 lbase = len(base)
177 pathsep = os.path.sep
177 pathsep = os.path.sep
178 lpathsep = len(pathsep)
178 lpathsep = len(pathsep)
179
179
180 out = []
180 out = []
181 for (dirpath,dirnames,filenames) in os.walk(base):
181 for (dirpath,dirnames,filenames) in os.walk(base):
182 # we need to strip out the dirpath from the base to map it to the
182 # we need to strip out the dirpath from the base to map it to the
183 # output (installation) path. This requires possibly stripping the
183 # output (installation) path. This requires possibly stripping the
184 # path separator, because otherwise pjoin will not work correctly
184 # path separator, because otherwise pjoin will not work correctly
185 # (pjoin('foo/','/bar') returns '/bar').
185 # (pjoin('foo/','/bar') returns '/bar').
186
186
187 dp_eff = dirpath[lbase:]
187 dp_eff = dirpath[lbase:]
188 if dp_eff.startswith(pathsep):
188 if dp_eff.startswith(pathsep):
189 dp_eff = dp_eff[lpathsep:]
189 dp_eff = dp_eff[lpathsep:]
190 # The output path must be anchored at the out_base marker
190 # The output path must be anchored at the out_base marker
191 out_path = pjoin(out_base,dp_eff)
191 out_path = pjoin(out_base,dp_eff)
192 # Now we can generate the final filenames. Since os.walk only produces
192 # Now we can generate the final filenames. Since os.walk only produces
193 # filenames, we must join back with the dirpath to get full valid file
193 # filenames, we must join back with the dirpath to get full valid file
194 # paths:
194 # paths:
195 pfiles = [pjoin(dirpath,f) for f in filenames]
195 pfiles = [pjoin(dirpath,f) for f in filenames]
196 # Finally, generate the entry we need, which is a pari of (output
196 # Finally, generate the entry we need, which is a pari of (output
197 # path, files) for use as a data_files parameter in install_data.
197 # path, files) for use as a data_files parameter in install_data.
198 out.append((out_path, pfiles))
198 out.append((out_path, pfiles))
199
199
200 return out
200 return out
201
201
202
202
203 def find_data_files():
203 def find_data_files():
204 """
204 """
205 Find IPython's data_files.
205 Find IPython's data_files.
206
206
207 Most of these are docs.
207 Most of these are docs.
208 """
208 """
209
209
210 docdirbase = pjoin('share', 'doc', 'ipython')
210 docdirbase = pjoin('share', 'doc', 'ipython')
211 manpagebase = pjoin('share', 'man', 'man1')
211 manpagebase = pjoin('share', 'man', 'man1')
212
212
213 # Simple file lists can be made by hand
213 # Simple file lists can be made by hand
214 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
214 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
215 if not manpages:
215 if not manpages:
216 # When running from a source tree, the manpages aren't gzipped
216 # When running from a source tree, the manpages aren't gzipped
217 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
217 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
218
218
219 igridhelpfiles = [f for f in glob(pjoin('IPython','extensions','igrid_help.*')) if isfile(f)]
219 igridhelpfiles = [f for f in glob(pjoin('IPython','extensions','igrid_help.*')) if isfile(f)]
220
220
221 # For nested structures, use the utility above
221 # For nested structures, use the utility above
222 example_files = make_dir_struct(
222 example_files = make_dir_struct(
223 'data',
223 'data',
224 pjoin('docs','examples'),
224 pjoin('docs','examples'),
225 pjoin(docdirbase,'examples')
225 pjoin(docdirbase,'examples')
226 )
226 )
227 manual_files = make_dir_struct(
227 manual_files = make_dir_struct(
228 'data',
228 'data',
229 pjoin('docs','html'),
229 pjoin('docs','html'),
230 pjoin(docdirbase,'manual')
230 pjoin(docdirbase,'manual')
231 )
231 )
232
232
233 # And assemble the entire output list
233 # And assemble the entire output list
234 data_files = [ (manpagebase, manpages),
234 data_files = [ (manpagebase, manpages),
235 (pjoin(docdirbase, 'extensions'), igridhelpfiles),
235 (pjoin(docdirbase, 'extensions'), igridhelpfiles),
236 ] + manual_files + example_files
236 ] + manual_files + example_files
237
237
238 return data_files
238 return data_files
239
239
240
240
241 def make_man_update_target(manpage):
241 def make_man_update_target(manpage):
242 """Return a target_update-compliant tuple for the given manpage.
242 """Return a target_update-compliant tuple for the given manpage.
243
243
244 Parameters
244 Parameters
245 ----------
245 ----------
246 manpage : string
246 manpage : string
247 Name of the manpage, must include the section number (trailing number).
247 Name of the manpage, must include the section number (trailing number).
248
248
249 Example
249 Example
250 -------
250 -------
251
251
252 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
252 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
253 ('docs/man/ipython.1.gz',
253 ('docs/man/ipython.1.gz',
254 ['docs/man/ipython.1'],
254 ['docs/man/ipython.1'],
255 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
255 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
256 """
256 """
257 man_dir = pjoin('docs', 'man')
257 man_dir = pjoin('docs', 'man')
258 manpage_gz = manpage + '.gz'
258 manpage_gz = manpage + '.gz'
259 manpath = pjoin(man_dir, manpage)
259 manpath = pjoin(man_dir, manpage)
260 manpath_gz = pjoin(man_dir, manpage_gz)
260 manpath_gz = pjoin(man_dir, manpage_gz)
261 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
261 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
262 locals() )
262 locals() )
263 return (manpath_gz, [manpath], gz_cmd)
263 return (manpath_gz, [manpath], gz_cmd)
264
264
265 # The two functions below are copied from IPython.utils.path, so we don't need
265 # The two functions below are copied from IPython.utils.path, so we don't need
266 # to import IPython during setup, which fails on Python 3.
266 # to import IPython during setup, which fails on Python 3.
267
267
268 def target_outdated(target,deps):
268 def target_outdated(target,deps):
269 """Determine whether a target is out of date.
269 """Determine whether a target is out of date.
270
270
271 target_outdated(target,deps) -> 1/0
271 target_outdated(target,deps) -> 1/0
272
272
273 deps: list of filenames which MUST exist.
273 deps: list of filenames which MUST exist.
274 target: single filename which may or may not exist.
274 target: single filename which may or may not exist.
275
275
276 If target doesn't exist or is older than any file listed in deps, return
276 If target doesn't exist or is older than any file listed in deps, return
277 true, otherwise return false.
277 true, otherwise return false.
278 """
278 """
279 try:
279 try:
280 target_time = os.path.getmtime(target)
280 target_time = os.path.getmtime(target)
281 except os.error:
281 except os.error:
282 return 1
282 return 1
283 for dep in deps:
283 for dep in deps:
284 dep_time = os.path.getmtime(dep)
284 dep_time = os.path.getmtime(dep)
285 if dep_time > target_time:
285 if dep_time > target_time:
286 #print "For target",target,"Dep failed:",dep # dbg
286 #print "For target",target,"Dep failed:",dep # dbg
287 #print "times (dep,tar):",dep_time,target_time # dbg
287 #print "times (dep,tar):",dep_time,target_time # dbg
288 return 1
288 return 1
289 return 0
289 return 0
290
290
291
291
292 def target_update(target,deps,cmd):
292 def target_update(target,deps,cmd):
293 """Update a target with a given command given a list of dependencies.
293 """Update a target with a given command given a list of dependencies.
294
294
295 target_update(target,deps,cmd) -> runs cmd if target is outdated.
295 target_update(target,deps,cmd) -> runs cmd if target is outdated.
296
296
297 This is just a wrapper around target_outdated() which calls the given
297 This is just a wrapper around target_outdated() which calls the given
298 command if target is outdated."""
298 command if target is outdated."""
299
299
300 if target_outdated(target,deps):
300 if target_outdated(target,deps):
301 os.system(cmd)
301 os.system(cmd)
302
302
303 #---------------------------------------------------------------------------
303 #---------------------------------------------------------------------------
304 # Find scripts
304 # Find scripts
305 #---------------------------------------------------------------------------
305 #---------------------------------------------------------------------------
306
306
307 def find_scripts(entry_points=False, suffix=''):
307 def find_scripts(entry_points=False, suffix=''):
308 """Find IPython's scripts.
308 """Find IPython's scripts.
309
309
310 if entry_points is True:
310 if entry_points is True:
311 return setuptools entry_point-style definitions
311 return setuptools entry_point-style definitions
312 else:
312 else:
313 return file paths of plain scripts [default]
313 return file paths of plain scripts [default]
314
314
315 suffix is appended to script names if entry_points is True, so that the
315 suffix is appended to script names if entry_points is True, so that the
316 Python 3 scripts get named "ipython3" etc.
316 Python 3 scripts get named "ipython3" etc.
317 """
317 """
318 if entry_points:
318 if entry_points:
319 console_scripts = [s % suffix for s in [
319 console_scripts = [s % suffix for s in [
320 'ipython%s = IPython:start_ipython',
320 'ipython%s = IPython:start_ipython',
321 'pycolor%s = IPython.utils.PyColorize:main',
321 'pycolor%s = IPython.utils.PyColorize:main',
322 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
322 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
323 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
323 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
324 'iplogger%s = IPython.parallel.apps.iploggerapp:launch_new_instance',
324 'iplogger%s = IPython.parallel.apps.iploggerapp:launch_new_instance',
325 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
325 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
326 'iptest%s = IPython.testing.iptest:main',
326 'iptest%s = IPython.testing.iptestcontroller:main',
327 'irunner%s = IPython.lib.irunner:main',
327 'irunner%s = IPython.lib.irunner:main',
328 ]]
328 ]]
329 gui_scripts = []
329 gui_scripts = []
330 scripts = dict(console_scripts=console_scripts, gui_scripts=gui_scripts)
330 scripts = dict(console_scripts=console_scripts, gui_scripts=gui_scripts)
331 else:
331 else:
332 parallel_scripts = pjoin('IPython','parallel','scripts')
332 parallel_scripts = pjoin('IPython','parallel','scripts')
333 main_scripts = pjoin('IPython','scripts')
333 main_scripts = pjoin('IPython','scripts')
334 scripts = [
334 scripts = [
335 pjoin(parallel_scripts, 'ipengine'),
335 pjoin(parallel_scripts, 'ipengine'),
336 pjoin(parallel_scripts, 'ipcontroller'),
336 pjoin(parallel_scripts, 'ipcontroller'),
337 pjoin(parallel_scripts, 'ipcluster'),
337 pjoin(parallel_scripts, 'ipcluster'),
338 pjoin(parallel_scripts, 'iplogger'),
338 pjoin(parallel_scripts, 'iplogger'),
339 pjoin(main_scripts, 'ipython'),
339 pjoin(main_scripts, 'ipython'),
340 pjoin(main_scripts, 'pycolor'),
340 pjoin(main_scripts, 'pycolor'),
341 pjoin(main_scripts, 'irunner'),
341 pjoin(main_scripts, 'irunner'),
342 pjoin(main_scripts, 'iptest')
342 pjoin(main_scripts, 'iptest')
343 ]
343 ]
344 return scripts
344 return scripts
345
345
346 #---------------------------------------------------------------------------
346 #---------------------------------------------------------------------------
347 # Verify all dependencies
347 # Verify all dependencies
348 #---------------------------------------------------------------------------
348 #---------------------------------------------------------------------------
349
349
350 def check_for_dependencies():
350 def check_for_dependencies():
351 """Check for IPython's dependencies.
351 """Check for IPython's dependencies.
352
352
353 This function should NOT be called if running under setuptools!
353 This function should NOT be called if running under setuptools!
354 """
354 """
355 from setupext.setupext import (
355 from setupext.setupext import (
356 print_line, print_raw, print_status,
356 print_line, print_raw, print_status,
357 check_for_sphinx, check_for_pygments,
357 check_for_sphinx, check_for_pygments,
358 check_for_nose, check_for_pexpect,
358 check_for_nose, check_for_pexpect,
359 check_for_pyzmq, check_for_readline,
359 check_for_pyzmq, check_for_readline,
360 check_for_jinja2, check_for_tornado
360 check_for_jinja2, check_for_tornado
361 )
361 )
362 print_line()
362 print_line()
363 print_raw("BUILDING IPYTHON")
363 print_raw("BUILDING IPYTHON")
364 print_status('python', sys.version)
364 print_status('python', sys.version)
365 print_status('platform', sys.platform)
365 print_status('platform', sys.platform)
366 if sys.platform == 'win32':
366 if sys.platform == 'win32':
367 print_status('Windows version', sys.getwindowsversion())
367 print_status('Windows version', sys.getwindowsversion())
368
368
369 print_raw("")
369 print_raw("")
370 print_raw("OPTIONAL DEPENDENCIES")
370 print_raw("OPTIONAL DEPENDENCIES")
371
371
372 check_for_sphinx()
372 check_for_sphinx()
373 check_for_pygments()
373 check_for_pygments()
374 check_for_nose()
374 check_for_nose()
375 check_for_pexpect()
375 check_for_pexpect()
376 check_for_pyzmq()
376 check_for_pyzmq()
377 check_for_tornado()
377 check_for_tornado()
378 check_for_readline()
378 check_for_readline()
379 check_for_jinja2()
379 check_for_jinja2()
380
380
381 #---------------------------------------------------------------------------
381 #---------------------------------------------------------------------------
382 # VCS related
382 # VCS related
383 #---------------------------------------------------------------------------
383 #---------------------------------------------------------------------------
384
384
385 # utils.submodule has checks for submodule status
385 # utils.submodule has checks for submodule status
386 execfile(pjoin('IPython','utils','submodule.py'), globals())
386 execfile(pjoin('IPython','utils','submodule.py'), globals())
387
387
388 class UpdateSubmodules(Command):
388 class UpdateSubmodules(Command):
389 """Update git submodules
389 """Update git submodules
390
390
391 IPython's external javascript dependencies live in a separate repo.
391 IPython's external javascript dependencies live in a separate repo.
392 """
392 """
393 description = "Update git submodules"
393 description = "Update git submodules"
394 user_options = []
394 user_options = []
395
395
396 def initialize_options(self):
396 def initialize_options(self):
397 pass
397 pass
398
398
399 def finalize_options(self):
399 def finalize_options(self):
400 pass
400 pass
401
401
402 def run(self):
402 def run(self):
403 failure = False
403 failure = False
404 try:
404 try:
405 self.spawn('git submodule init'.split())
405 self.spawn('git submodule init'.split())
406 self.spawn('git submodule update --recursive'.split())
406 self.spawn('git submodule update --recursive'.split())
407 except Exception as e:
407 except Exception as e:
408 failure = e
408 failure = e
409 print(e)
409 print(e)
410
410
411 if not check_submodule_status(repo_root) == 'clean':
411 if not check_submodule_status(repo_root) == 'clean':
412 print("submodules could not be checked out")
412 print("submodules could not be checked out")
413 sys.exit(1)
413 sys.exit(1)
414
414
415
415
416 def git_prebuild(pkg_dir, build_cmd=build_py):
416 def git_prebuild(pkg_dir, build_cmd=build_py):
417 """Return extended build or sdist command class for recording commit
417 """Return extended build or sdist command class for recording commit
418
418
419 records git commit in IPython.utils._sysinfo.commit
419 records git commit in IPython.utils._sysinfo.commit
420
420
421 for use in IPython.utils.sysinfo.sys_info() calls after installation.
421 for use in IPython.utils.sysinfo.sys_info() calls after installation.
422
422
423 Also ensures that submodules exist prior to running
423 Also ensures that submodules exist prior to running
424 """
424 """
425
425
426 class MyBuildPy(build_cmd):
426 class MyBuildPy(build_cmd):
427 ''' Subclass to write commit data into installation tree '''
427 ''' Subclass to write commit data into installation tree '''
428 def run(self):
428 def run(self):
429 build_cmd.run(self)
429 build_cmd.run(self)
430 # this one will only fire for build commands
430 # this one will only fire for build commands
431 if hasattr(self, 'build_lib'):
431 if hasattr(self, 'build_lib'):
432 self._record_commit(self.build_lib)
432 self._record_commit(self.build_lib)
433
433
434 def make_release_tree(self, base_dir, files):
434 def make_release_tree(self, base_dir, files):
435 # this one will fire for sdist
435 # this one will fire for sdist
436 build_cmd.make_release_tree(self, base_dir, files)
436 build_cmd.make_release_tree(self, base_dir, files)
437 self._record_commit(base_dir)
437 self._record_commit(base_dir)
438
438
439 def _record_commit(self, base_dir):
439 def _record_commit(self, base_dir):
440 import subprocess
440 import subprocess
441 proc = subprocess.Popen('git rev-parse --short HEAD',
441 proc = subprocess.Popen('git rev-parse --short HEAD',
442 stdout=subprocess.PIPE,
442 stdout=subprocess.PIPE,
443 stderr=subprocess.PIPE,
443 stderr=subprocess.PIPE,
444 shell=True)
444 shell=True)
445 repo_commit, _ = proc.communicate()
445 repo_commit, _ = proc.communicate()
446 repo_commit = repo_commit.strip().decode("ascii")
446 repo_commit = repo_commit.strip().decode("ascii")
447
447
448 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
448 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
449 if os.path.isfile(out_pth) and not repo_commit:
449 if os.path.isfile(out_pth) and not repo_commit:
450 # nothing to write, don't clobber
450 # nothing to write, don't clobber
451 return
451 return
452
452
453 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
453 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
454
454
455 # remove to avoid overwriting original via hard link
455 # remove to avoid overwriting original via hard link
456 try:
456 try:
457 os.remove(out_pth)
457 os.remove(out_pth)
458 except (IOError, OSError):
458 except (IOError, OSError):
459 pass
459 pass
460 with open(out_pth, 'w') as out_file:
460 with open(out_pth, 'w') as out_file:
461 out_file.writelines([
461 out_file.writelines([
462 '# GENERATED BY setup.py\n',
462 '# GENERATED BY setup.py\n',
463 'commit = "%s"\n' % repo_commit,
463 'commit = "%s"\n' % repo_commit,
464 ])
464 ])
465 return require_submodules(MyBuildPy)
465 return require_submodules(MyBuildPy)
466
466
467
467
468 def require_submodules(command):
468 def require_submodules(command):
469 """decorator for instructing a command to check for submodules before running"""
469 """decorator for instructing a command to check for submodules before running"""
470 class DecoratedCommand(command):
470 class DecoratedCommand(command):
471 def run(self):
471 def run(self):
472 if not check_submodule_status(repo_root) == 'clean':
472 if not check_submodule_status(repo_root) == 'clean':
473 print("submodules missing! Run `setup.py submodule` and try again")
473 print("submodules missing! Run `setup.py submodule` and try again")
474 sys.exit(1)
474 sys.exit(1)
475 command.run(self)
475 command.run(self)
476 return DecoratedCommand
476 return DecoratedCommand
General Comments 0
You need to be logged in to leave comments. Login now