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