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