##// END OF EJS Templates
make sure test files are installed
MinRK -
Show More
@@ -1,468 +1,469 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', 'frontend', 'html', 'notebook'))
137 os.chdir(os.path.join('IPython', 'frontend', 'html', 'notebook'))
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.testing' : ['*.txt'],
150 'IPython.testing' : ['*.txt'],
150 'IPython.testing.plugin' : ['*.txt'],
151 'IPython.testing.plugin' : ['*.txt'],
151 'IPython.frontend.html.notebook' : ['templates/*'] + static_data,
152 'IPython.frontend.html.notebook' : ['templates/*'] + static_data,
152 'IPython.frontend.qt.console' : ['resources/icon/*.svg'],
153 'IPython.frontend.qt.console' : ['resources/icon/*.svg'],
153 }
154 }
154 return package_data
155 return package_data
155
156
156
157
157 #---------------------------------------------------------------------------
158 #---------------------------------------------------------------------------
158 # Find data files
159 # Find data files
159 #---------------------------------------------------------------------------
160 #---------------------------------------------------------------------------
160
161
161 def make_dir_struct(tag,base,out_base):
162 def make_dir_struct(tag,base,out_base):
162 """Make the directory structure of all files below a starting dir.
163 """Make the directory structure of all files below a starting dir.
163
164
164 This is just a convenience routine to help build a nested directory
165 This is just a convenience routine to help build a nested directory
165 hierarchy because distutils is too stupid to do this by itself.
166 hierarchy because distutils is too stupid to do this by itself.
166
167
167 XXX - this needs a proper docstring!
168 XXX - this needs a proper docstring!
168 """
169 """
169
170
170 # we'll use these a lot below
171 # we'll use these a lot below
171 lbase = len(base)
172 lbase = len(base)
172 pathsep = os.path.sep
173 pathsep = os.path.sep
173 lpathsep = len(pathsep)
174 lpathsep = len(pathsep)
174
175
175 out = []
176 out = []
176 for (dirpath,dirnames,filenames) in os.walk(base):
177 for (dirpath,dirnames,filenames) in os.walk(base):
177 # we need to strip out the dirpath from the base to map it to the
178 # we need to strip out the dirpath from the base to map it to the
178 # output (installation) path. This requires possibly stripping the
179 # output (installation) path. This requires possibly stripping the
179 # path separator, because otherwise pjoin will not work correctly
180 # path separator, because otherwise pjoin will not work correctly
180 # (pjoin('foo/','/bar') returns '/bar').
181 # (pjoin('foo/','/bar') returns '/bar').
181
182
182 dp_eff = dirpath[lbase:]
183 dp_eff = dirpath[lbase:]
183 if dp_eff.startswith(pathsep):
184 if dp_eff.startswith(pathsep):
184 dp_eff = dp_eff[lpathsep:]
185 dp_eff = dp_eff[lpathsep:]
185 # The output path must be anchored at the out_base marker
186 # The output path must be anchored at the out_base marker
186 out_path = pjoin(out_base,dp_eff)
187 out_path = pjoin(out_base,dp_eff)
187 # Now we can generate the final filenames. Since os.walk only produces
188 # Now we can generate the final filenames. Since os.walk only produces
188 # filenames, we must join back with the dirpath to get full valid file
189 # filenames, we must join back with the dirpath to get full valid file
189 # paths:
190 # paths:
190 pfiles = [pjoin(dirpath,f) for f in filenames]
191 pfiles = [pjoin(dirpath,f) for f in filenames]
191 # Finally, generate the entry we need, which is a pari of (output
192 # Finally, generate the entry we need, which is a pari of (output
192 # path, files) for use as a data_files parameter in install_data.
193 # path, files) for use as a data_files parameter in install_data.
193 out.append((out_path, pfiles))
194 out.append((out_path, pfiles))
194
195
195 return out
196 return out
196
197
197
198
198 def find_data_files():
199 def find_data_files():
199 """
200 """
200 Find IPython's data_files.
201 Find IPython's data_files.
201
202
202 Most of these are docs.
203 Most of these are docs.
203 """
204 """
204
205
205 docdirbase = pjoin('share', 'doc', 'ipython')
206 docdirbase = pjoin('share', 'doc', 'ipython')
206 manpagebase = pjoin('share', 'man', 'man1')
207 manpagebase = pjoin('share', 'man', 'man1')
207
208
208 # Simple file lists can be made by hand
209 # Simple file lists can be made by hand
209 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
210 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
210 if not manpages:
211 if not manpages:
211 # When running from a source tree, the manpages aren't gzipped
212 # When running from a source tree, the manpages aren't gzipped
212 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
213 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
213
214
214 igridhelpfiles = [f for f in glob(pjoin('IPython','extensions','igrid_help.*')) if isfile(f)]
215 igridhelpfiles = [f for f in glob(pjoin('IPython','extensions','igrid_help.*')) if isfile(f)]
215
216
216 # For nested structures, use the utility above
217 # For nested structures, use the utility above
217 example_files = make_dir_struct(
218 example_files = make_dir_struct(
218 'data',
219 'data',
219 pjoin('docs','examples'),
220 pjoin('docs','examples'),
220 pjoin(docdirbase,'examples')
221 pjoin(docdirbase,'examples')
221 )
222 )
222 manual_files = make_dir_struct(
223 manual_files = make_dir_struct(
223 'data',
224 'data',
224 pjoin('docs','html'),
225 pjoin('docs','html'),
225 pjoin(docdirbase,'manual')
226 pjoin(docdirbase,'manual')
226 )
227 )
227
228
228 # And assemble the entire output list
229 # And assemble the entire output list
229 data_files = [ (manpagebase, manpages),
230 data_files = [ (manpagebase, manpages),
230 (pjoin(docdirbase, 'extensions'), igridhelpfiles),
231 (pjoin(docdirbase, 'extensions'), igridhelpfiles),
231 ] + manual_files + example_files
232 ] + manual_files + example_files
232
233
233 return data_files
234 return data_files
234
235
235
236
236 def make_man_update_target(manpage):
237 def make_man_update_target(manpage):
237 """Return a target_update-compliant tuple for the given manpage.
238 """Return a target_update-compliant tuple for the given manpage.
238
239
239 Parameters
240 Parameters
240 ----------
241 ----------
241 manpage : string
242 manpage : string
242 Name of the manpage, must include the section number (trailing number).
243 Name of the manpage, must include the section number (trailing number).
243
244
244 Example
245 Example
245 -------
246 -------
246
247
247 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
248 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
248 ('docs/man/ipython.1.gz',
249 ('docs/man/ipython.1.gz',
249 ['docs/man/ipython.1'],
250 ['docs/man/ipython.1'],
250 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
251 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
251 """
252 """
252 man_dir = pjoin('docs', 'man')
253 man_dir = pjoin('docs', 'man')
253 manpage_gz = manpage + '.gz'
254 manpage_gz = manpage + '.gz'
254 manpath = pjoin(man_dir, manpage)
255 manpath = pjoin(man_dir, manpage)
255 manpath_gz = pjoin(man_dir, manpage_gz)
256 manpath_gz = pjoin(man_dir, manpage_gz)
256 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
257 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
257 locals() )
258 locals() )
258 return (manpath_gz, [manpath], gz_cmd)
259 return (manpath_gz, [manpath], gz_cmd)
259
260
260 # The two functions below are copied from IPython.utils.path, so we don't need
261 # The two functions below are copied from IPython.utils.path, so we don't need
261 # to import IPython during setup, which fails on Python 3.
262 # to import IPython during setup, which fails on Python 3.
262
263
263 def target_outdated(target,deps):
264 def target_outdated(target,deps):
264 """Determine whether a target is out of date.
265 """Determine whether a target is out of date.
265
266
266 target_outdated(target,deps) -> 1/0
267 target_outdated(target,deps) -> 1/0
267
268
268 deps: list of filenames which MUST exist.
269 deps: list of filenames which MUST exist.
269 target: single filename which may or may not exist.
270 target: single filename which may or may not exist.
270
271
271 If target doesn't exist or is older than any file listed in deps, return
272 If target doesn't exist or is older than any file listed in deps, return
272 true, otherwise return false.
273 true, otherwise return false.
273 """
274 """
274 try:
275 try:
275 target_time = os.path.getmtime(target)
276 target_time = os.path.getmtime(target)
276 except os.error:
277 except os.error:
277 return 1
278 return 1
278 for dep in deps:
279 for dep in deps:
279 dep_time = os.path.getmtime(dep)
280 dep_time = os.path.getmtime(dep)
280 if dep_time > target_time:
281 if dep_time > target_time:
281 #print "For target",target,"Dep failed:",dep # dbg
282 #print "For target",target,"Dep failed:",dep # dbg
282 #print "times (dep,tar):",dep_time,target_time # dbg
283 #print "times (dep,tar):",dep_time,target_time # dbg
283 return 1
284 return 1
284 return 0
285 return 0
285
286
286
287
287 def target_update(target,deps,cmd):
288 def target_update(target,deps,cmd):
288 """Update a target with a given command given a list of dependencies.
289 """Update a target with a given command given a list of dependencies.
289
290
290 target_update(target,deps,cmd) -> runs cmd if target is outdated.
291 target_update(target,deps,cmd) -> runs cmd if target is outdated.
291
292
292 This is just a wrapper around target_outdated() which calls the given
293 This is just a wrapper around target_outdated() which calls the given
293 command if target is outdated."""
294 command if target is outdated."""
294
295
295 if target_outdated(target,deps):
296 if target_outdated(target,deps):
296 os.system(cmd)
297 os.system(cmd)
297
298
298 #---------------------------------------------------------------------------
299 #---------------------------------------------------------------------------
299 # Find scripts
300 # Find scripts
300 #---------------------------------------------------------------------------
301 #---------------------------------------------------------------------------
301
302
302 def find_scripts(entry_points=False, suffix=''):
303 def find_scripts(entry_points=False, suffix=''):
303 """Find IPython's scripts.
304 """Find IPython's scripts.
304
305
305 if entry_points is True:
306 if entry_points is True:
306 return setuptools entry_point-style definitions
307 return setuptools entry_point-style definitions
307 else:
308 else:
308 return file paths of plain scripts [default]
309 return file paths of plain scripts [default]
309
310
310 suffix is appended to script names if entry_points is True, so that the
311 suffix is appended to script names if entry_points is True, so that the
311 Python 3 scripts get named "ipython3" etc.
312 Python 3 scripts get named "ipython3" etc.
312 """
313 """
313 if entry_points:
314 if entry_points:
314 console_scripts = [s % suffix for s in [
315 console_scripts = [s % suffix for s in [
315 'ipython%s = IPython.frontend.terminal.ipapp:launch_new_instance',
316 'ipython%s = IPython.frontend.terminal.ipapp:launch_new_instance',
316 'pycolor%s = IPython.utils.PyColorize:main',
317 'pycolor%s = IPython.utils.PyColorize:main',
317 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
318 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
318 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
319 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
319 'iplogger%s = IPython.parallel.apps.iploggerapp:launch_new_instance',
320 'iplogger%s = IPython.parallel.apps.iploggerapp:launch_new_instance',
320 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
321 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
321 'iptest%s = IPython.testing.iptest:main',
322 'iptest%s = IPython.testing.iptest:main',
322 'irunner%s = IPython.lib.irunner:main'
323 'irunner%s = IPython.lib.irunner:main'
323 ]]
324 ]]
324 gui_scripts = []
325 gui_scripts = []
325 scripts = dict(console_scripts=console_scripts, gui_scripts=gui_scripts)
326 scripts = dict(console_scripts=console_scripts, gui_scripts=gui_scripts)
326 else:
327 else:
327 parallel_scripts = pjoin('IPython','parallel','scripts')
328 parallel_scripts = pjoin('IPython','parallel','scripts')
328 main_scripts = pjoin('IPython','scripts')
329 main_scripts = pjoin('IPython','scripts')
329 scripts = [
330 scripts = [
330 pjoin(parallel_scripts, 'ipengine'),
331 pjoin(parallel_scripts, 'ipengine'),
331 pjoin(parallel_scripts, 'ipcontroller'),
332 pjoin(parallel_scripts, 'ipcontroller'),
332 pjoin(parallel_scripts, 'ipcluster'),
333 pjoin(parallel_scripts, 'ipcluster'),
333 pjoin(parallel_scripts, 'iplogger'),
334 pjoin(parallel_scripts, 'iplogger'),
334 pjoin(main_scripts, 'ipython'),
335 pjoin(main_scripts, 'ipython'),
335 pjoin(main_scripts, 'pycolor'),
336 pjoin(main_scripts, 'pycolor'),
336 pjoin(main_scripts, 'irunner'),
337 pjoin(main_scripts, 'irunner'),
337 pjoin(main_scripts, 'iptest')
338 pjoin(main_scripts, 'iptest')
338 ]
339 ]
339 return scripts
340 return scripts
340
341
341 #---------------------------------------------------------------------------
342 #---------------------------------------------------------------------------
342 # Verify all dependencies
343 # Verify all dependencies
343 #---------------------------------------------------------------------------
344 #---------------------------------------------------------------------------
344
345
345 def check_for_dependencies():
346 def check_for_dependencies():
346 """Check for IPython's dependencies.
347 """Check for IPython's dependencies.
347
348
348 This function should NOT be called if running under setuptools!
349 This function should NOT be called if running under setuptools!
349 """
350 """
350 from setupext.setupext import (
351 from setupext.setupext import (
351 print_line, print_raw, print_status,
352 print_line, print_raw, print_status,
352 check_for_sphinx, check_for_pygments,
353 check_for_sphinx, check_for_pygments,
353 check_for_nose, check_for_pexpect,
354 check_for_nose, check_for_pexpect,
354 check_for_pyzmq, check_for_readline
355 check_for_pyzmq, check_for_readline
355 )
356 )
356 print_line()
357 print_line()
357 print_raw("BUILDING IPYTHON")
358 print_raw("BUILDING IPYTHON")
358 print_status('python', sys.version)
359 print_status('python', sys.version)
359 print_status('platform', sys.platform)
360 print_status('platform', sys.platform)
360 if sys.platform == 'win32':
361 if sys.platform == 'win32':
361 print_status('Windows version', sys.getwindowsversion())
362 print_status('Windows version', sys.getwindowsversion())
362
363
363 print_raw("")
364 print_raw("")
364 print_raw("OPTIONAL DEPENDENCIES")
365 print_raw("OPTIONAL DEPENDENCIES")
365
366
366 check_for_sphinx()
367 check_for_sphinx()
367 check_for_pygments()
368 check_for_pygments()
368 check_for_nose()
369 check_for_nose()
369 check_for_pexpect()
370 check_for_pexpect()
370 check_for_pyzmq()
371 check_for_pyzmq()
371 check_for_readline()
372 check_for_readline()
372
373
373 #---------------------------------------------------------------------------
374 #---------------------------------------------------------------------------
374 # VCS related
375 # VCS related
375 #---------------------------------------------------------------------------
376 #---------------------------------------------------------------------------
376
377
377 # utils.submodule has checks for submodule status
378 # utils.submodule has checks for submodule status
378 execfile(pjoin('IPython','utils','submodule.py'), globals())
379 execfile(pjoin('IPython','utils','submodule.py'), globals())
379
380
380 class UpdateSubmodules(Command):
381 class UpdateSubmodules(Command):
381 """Update git submodules
382 """Update git submodules
382
383
383 IPython's external javascript dependencies live in a separate repo.
384 IPython's external javascript dependencies live in a separate repo.
384 """
385 """
385 description = "Update git submodules"
386 description = "Update git submodules"
386 user_options = []
387 user_options = []
387
388
388 def initialize_options(self):
389 def initialize_options(self):
389 pass
390 pass
390
391
391 def finalize_options(self):
392 def finalize_options(self):
392 pass
393 pass
393
394
394 def run(self):
395 def run(self):
395 failure = False
396 failure = False
396 try:
397 try:
397 self.spawn('git submodule init'.split())
398 self.spawn('git submodule init'.split())
398 self.spawn('git submodule update --recursive'.split())
399 self.spawn('git submodule update --recursive'.split())
399 except Exception as e:
400 except Exception as e:
400 failure = e
401 failure = e
401 print(e)
402 print(e)
402
403
403 if not check_submodule_status(repo_root) == 'clean':
404 if not check_submodule_status(repo_root) == 'clean':
404 print("submodules could not be checked out")
405 print("submodules could not be checked out")
405 sys.exit(1)
406 sys.exit(1)
406
407
407
408
408 def git_prebuild(pkg_dir, build_cmd=build_py):
409 def git_prebuild(pkg_dir, build_cmd=build_py):
409 """Return extended build or sdist command class for recording commit
410 """Return extended build or sdist command class for recording commit
410
411
411 records git commit in IPython.utils._sysinfo.commit
412 records git commit in IPython.utils._sysinfo.commit
412
413
413 for use in IPython.utils.sysinfo.sys_info() calls after installation.
414 for use in IPython.utils.sysinfo.sys_info() calls after installation.
414
415
415 Also ensures that submodules exist prior to running
416 Also ensures that submodules exist prior to running
416 """
417 """
417
418
418 class MyBuildPy(build_cmd):
419 class MyBuildPy(build_cmd):
419 ''' Subclass to write commit data into installation tree '''
420 ''' Subclass to write commit data into installation tree '''
420 def run(self):
421 def run(self):
421 build_cmd.run(self)
422 build_cmd.run(self)
422 # this one will only fire for build commands
423 # this one will only fire for build commands
423 if hasattr(self, 'build_lib'):
424 if hasattr(self, 'build_lib'):
424 self._record_commit(self.build_lib)
425 self._record_commit(self.build_lib)
425
426
426 def make_release_tree(self, base_dir, files):
427 def make_release_tree(self, base_dir, files):
427 # this one will fire for sdist
428 # this one will fire for sdist
428 build_cmd.make_release_tree(self, base_dir, files)
429 build_cmd.make_release_tree(self, base_dir, files)
429 self._record_commit(base_dir)
430 self._record_commit(base_dir)
430
431
431 def _record_commit(self, base_dir):
432 def _record_commit(self, base_dir):
432 import subprocess
433 import subprocess
433 proc = subprocess.Popen('git rev-parse --short HEAD',
434 proc = subprocess.Popen('git rev-parse --short HEAD',
434 stdout=subprocess.PIPE,
435 stdout=subprocess.PIPE,
435 stderr=subprocess.PIPE,
436 stderr=subprocess.PIPE,
436 shell=True)
437 shell=True)
437 repo_commit, _ = proc.communicate()
438 repo_commit, _ = proc.communicate()
438 repo_commit = repo_commit.strip().decode("ascii")
439 repo_commit = repo_commit.strip().decode("ascii")
439
440
440 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
441 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
441 if os.path.isfile(out_pth) and not repo_commit:
442 if os.path.isfile(out_pth) and not repo_commit:
442 # nothing to write, don't clobber
443 # nothing to write, don't clobber
443 return
444 return
444
445
445 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
446 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
446
447
447 # remove to avoid overwriting original via hard link
448 # remove to avoid overwriting original via hard link
448 try:
449 try:
449 os.remove(out_pth)
450 os.remove(out_pth)
450 except (IOError, OSError):
451 except (IOError, OSError):
451 pass
452 pass
452 with open(out_pth, 'w') as out_file:
453 with open(out_pth, 'w') as out_file:
453 out_file.writelines([
454 out_file.writelines([
454 '# GENERATED BY setup.py\n',
455 '# GENERATED BY setup.py\n',
455 'commit = "%s"\n' % repo_commit,
456 'commit = "%s"\n' % repo_commit,
456 ])
457 ])
457 return require_submodules(MyBuildPy)
458 return require_submodules(MyBuildPy)
458
459
459
460
460 def require_submodules(command):
461 def require_submodules(command):
461 """decorator for instructing a command to check for submodules before running"""
462 """decorator for instructing a command to check for submodules before running"""
462 class DecoratedCommand(command):
463 class DecoratedCommand(command):
463 def run(self):
464 def run(self):
464 if not check_submodule_status(repo_root) == 'clean':
465 if not check_submodule_status(repo_root) == 'clean':
465 print("submodules missing! Run `setup.py submodule` and try again")
466 print("submodules missing! Run `setup.py submodule` and try again")
466 sys.exit(1)
467 sys.exit(1)
467 command.run(self)
468 command.run(self)
468 return DecoratedCommand
469 return DecoratedCommand
General Comments 0
You need to be logged in to leave comments. Login now