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