##// END OF EJS Templates
reformat previous commit
Matthias Bussonnier -
r26943:69dbfc1c
parent child
Show More
@@ -1,408 +1,408
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
11
12 # Copyright (c) IPython Development Team.
12 # Copyright (c) IPython Development Team.
13 # Distributed under the terms of the Modified BSD License.
13 # Distributed under the terms of the Modified BSD License.
14
14
15 import os
15 import os
16 import re
16 import re
17 import sys
17 import sys
18 from glob import glob
18 from glob import glob
19 from logging import log
19 from logging import log
20
20
21 from setuptools import Command
21 from setuptools import Command
22 from setuptools.command.build_py import build_py
22 from setuptools.command.build_py import build_py
23
23
24 # TODO: Replacement for this?
24 # TODO: Replacement for this?
25 from distutils.command.build_scripts import build_scripts
25 from distutils.command.build_scripts import build_scripts
26 from setuptools.command.install import install
26 from setuptools.command.install import install
27 from setuptools.command.install_scripts import install_scripts
27 from setuptools.command.install_scripts import install_scripts
28
28
29 from setupext import install_data_ext
29 from setupext import install_data_ext
30
30
31 #-------------------------------------------------------------------------------
31 #-------------------------------------------------------------------------------
32 # Useful globals and utility functions
32 # Useful globals and utility functions
33 #-------------------------------------------------------------------------------
33 #-------------------------------------------------------------------------------
34
34
35 # A few handy globals
35 # A few handy globals
36 isfile = os.path.isfile
36 isfile = os.path.isfile
37 pjoin = os.path.join
37 pjoin = os.path.join
38 repo_root = os.path.dirname(os.path.abspath(__file__))
38 repo_root = os.path.dirname(os.path.abspath(__file__))
39
39
40 def execfile(fname, globs, locs=None):
40 def execfile(fname, globs, locs=None):
41 locs = locs or globs
41 locs = locs or globs
42 with open(fname) as f:
42 with open(fname) as f:
43 exec(compile(f.read(), fname, "exec"), globs, locs)
43 exec(compile(f.read(), fname, "exec"), globs, locs)
44
44
45 # A little utility we'll need below, since glob() does NOT allow you to do
45 # A little utility we'll need below, since glob() does NOT allow you to do
46 # exclusion on multiple endings!
46 # exclusion on multiple endings!
47 def file_doesnt_endwith(test,endings):
47 def file_doesnt_endwith(test,endings):
48 """Return true if test is a file and its name does NOT end with any
48 """Return true if test is a file and its name does NOT end with any
49 of the strings listed in endings."""
49 of the strings listed in endings."""
50 if not isfile(test):
50 if not isfile(test):
51 return False
51 return False
52 for e in endings:
52 for e in endings:
53 if test.endswith(e):
53 if test.endswith(e):
54 return False
54 return False
55 return True
55 return True
56
56
57 #---------------------------------------------------------------------------
57 #---------------------------------------------------------------------------
58 # Basic project information
58 # Basic project information
59 #---------------------------------------------------------------------------
59 #---------------------------------------------------------------------------
60
60
61 # release.py contains version, authors, license, url, keywords, etc.
61 # release.py contains version, authors, license, url, keywords, etc.
62 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
62 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
63
63
64 # Create a dict with the basic information
64 # Create a dict with the basic information
65 # This dict is eventually passed to setup after additional keys are added.
65 # This dict is eventually passed to setup after additional keys are added.
66 setup_args = dict(
66 setup_args = dict(
67 name = name,
67 name = name,
68 version = version,
68 version = version,
69 description = description,
69 description = description,
70 long_description = long_description,
70 long_description = long_description,
71 author = author,
71 author = author,
72 author_email = author_email,
72 author_email = author_email,
73 url = url,
73 url = url,
74 license = license,
74 license = license,
75 platforms = platforms,
75 platforms = platforms,
76 keywords = keywords,
76 keywords = keywords,
77 classifiers = classifiers,
77 classifiers = classifiers,
78 cmdclass = {'install_data': install_data_ext},
78 cmdclass = {'install_data': install_data_ext},
79 project_urls={
79 project_urls={
80 'Documentation': 'https://ipython.readthedocs.io/',
80 'Documentation': 'https://ipython.readthedocs.io/',
81 'Funding' : 'https://numfocus.org/',
81 'Funding' : 'https://numfocus.org/',
82 'Source' : 'https://github.com/ipython/ipython',
82 'Source' : 'https://github.com/ipython/ipython',
83 'Tracker' : 'https://github.com/ipython/ipython/issues',
83 'Tracker' : 'https://github.com/ipython/ipython/issues',
84 }
84 }
85 )
85 )
86
86
87
87
88 #---------------------------------------------------------------------------
88 #---------------------------------------------------------------------------
89 # Find packages
89 # Find packages
90 #---------------------------------------------------------------------------
90 #---------------------------------------------------------------------------
91
91
92 def find_packages():
92 def find_packages():
93 """
93 """
94 Find all of IPython's packages.
94 Find all of IPython's packages.
95 """
95 """
96 excludes = ['deathrow', 'quarantine']
96 excludes = ['deathrow', 'quarantine']
97 packages = []
97 packages = []
98 for directory, subdirs,files in os.walk('IPython'):
98 for directory, subdirs, files in os.walk("IPython"):
99 package = directory.replace(os.path.sep, '.')
99 package = directory.replace(os.path.sep, ".")
100 if any(package.startswith('IPython.'+exc) for exc in excludes):
100 if any(package.startswith("IPython." + exc) for exc in excludes):
101 # package is to be excluded (e.g. deathrow)
101 # package is to be excluded (e.g. deathrow)
102 continue
102 continue
103 if '__init__.py' not in files:
103 if '__init__.py' not in files:
104 # not a package
104 # not a package
105 continue
105 continue
106 packages.append(package)
106 packages.append(package)
107 return packages
107 return packages
108
108
109 #---------------------------------------------------------------------------
109 #---------------------------------------------------------------------------
110 # Find package data
110 # Find package data
111 #---------------------------------------------------------------------------
111 #---------------------------------------------------------------------------
112
112
113 def find_package_data():
113 def find_package_data():
114 """
114 """
115 Find IPython's package_data.
115 Find IPython's package_data.
116 """
116 """
117 # This is not enough for these things to appear in an sdist.
117 # This is not enough for these things to appear in an sdist.
118 # We need to muck with the MANIFEST to get this to work
118 # We need to muck with the MANIFEST to get this to work
119
119
120 package_data = {
120 package_data = {
121 'IPython.core' : ['profile/README*'],
121 'IPython.core' : ['profile/README*'],
122 'IPython.core.tests' : ['*.png', '*.jpg', 'daft_extension/*.py'],
122 'IPython.core.tests' : ['*.png', '*.jpg', 'daft_extension/*.py'],
123 'IPython.lib.tests' : ['*.wav'],
123 'IPython.lib.tests' : ['*.wav'],
124 'IPython.testing.plugin' : ['*.txt'],
124 'IPython.testing.plugin' : ['*.txt'],
125 }
125 }
126
126
127 return package_data
127 return package_data
128
128
129
129
130 def check_package_data(package_data):
130 def check_package_data(package_data):
131 """verify that package_data globs make sense"""
131 """verify that package_data globs make sense"""
132 print("checking package data")
132 print("checking package data")
133 for pkg, data in package_data.items():
133 for pkg, data in package_data.items():
134 pkg_root = pjoin(*pkg.split('.'))
134 pkg_root = pjoin(*pkg.split('.'))
135 for d in data:
135 for d in data:
136 path = pjoin(pkg_root, d)
136 path = pjoin(pkg_root, d)
137 if '*' in path:
137 if '*' in path:
138 assert len(glob(path)) > 0, "No files match pattern %s" % path
138 assert len(glob(path)) > 0, "No files match pattern %s" % path
139 else:
139 else:
140 assert os.path.exists(path), "Missing package data: %s" % path
140 assert os.path.exists(path), "Missing package data: %s" % path
141
141
142
142
143 def check_package_data_first(command):
143 def check_package_data_first(command):
144 """decorator for checking package_data before running a given command
144 """decorator for checking package_data before running a given command
145
145
146 Probably only needs to wrap build_py
146 Probably only needs to wrap build_py
147 """
147 """
148 class DecoratedCommand(command):
148 class DecoratedCommand(command):
149 def run(self):
149 def run(self):
150 check_package_data(self.package_data)
150 check_package_data(self.package_data)
151 command.run(self)
151 command.run(self)
152 return DecoratedCommand
152 return DecoratedCommand
153
153
154
154
155 #---------------------------------------------------------------------------
155 #---------------------------------------------------------------------------
156 # Find data files
156 # Find data files
157 #---------------------------------------------------------------------------
157 #---------------------------------------------------------------------------
158
158
159 def find_data_files():
159 def find_data_files():
160 """
160 """
161 Find IPython's data_files.
161 Find IPython's data_files.
162
162
163 Just man pages at this point.
163 Just man pages at this point.
164 """
164 """
165
165
166 if "freebsd" in sys.platform:
166 if "freebsd" in sys.platform:
167 manpagebase = pjoin('man', 'man1')
167 manpagebase = pjoin('man', 'man1')
168 else:
168 else:
169 manpagebase = pjoin('share', 'man', 'man1')
169 manpagebase = pjoin('share', 'man', 'man1')
170
170
171 # Simple file lists can be made by hand
171 # Simple file lists can be made by hand
172 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
172 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
173 if not manpages:
173 if not manpages:
174 # When running from a source tree, the manpages aren't gzipped
174 # When running from a source tree, the manpages aren't gzipped
175 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
175 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
176
176
177 # And assemble the entire output list
177 # And assemble the entire output list
178 data_files = [ (manpagebase, manpages) ]
178 data_files = [ (manpagebase, manpages) ]
179
179
180 return data_files
180 return data_files
181
181
182
182
183 # The two functions below are copied from IPython.utils.path, so we don't need
183 # The two functions below are copied from IPython.utils.path, so we don't need
184 # to import IPython during setup, which fails on Python 3.
184 # to import IPython during setup, which fails on Python 3.
185
185
186 def target_outdated(target,deps):
186 def target_outdated(target,deps):
187 """Determine whether a target is out of date.
187 """Determine whether a target is out of date.
188
188
189 target_outdated(target,deps) -> 1/0
189 target_outdated(target,deps) -> 1/0
190
190
191 deps: list of filenames which MUST exist.
191 deps: list of filenames which MUST exist.
192 target: single filename which may or may not exist.
192 target: single filename which may or may not exist.
193
193
194 If target doesn't exist or is older than any file listed in deps, return
194 If target doesn't exist or is older than any file listed in deps, return
195 true, otherwise return false.
195 true, otherwise return false.
196 """
196 """
197 try:
197 try:
198 target_time = os.path.getmtime(target)
198 target_time = os.path.getmtime(target)
199 except os.error:
199 except os.error:
200 return 1
200 return 1
201 for dep in deps:
201 for dep in deps:
202 dep_time = os.path.getmtime(dep)
202 dep_time = os.path.getmtime(dep)
203 if dep_time > target_time:
203 if dep_time > target_time:
204 #print "For target",target,"Dep failed:",dep # dbg
204 #print "For target",target,"Dep failed:",dep # dbg
205 #print "times (dep,tar):",dep_time,target_time # dbg
205 #print "times (dep,tar):",dep_time,target_time # dbg
206 return 1
206 return 1
207 return 0
207 return 0
208
208
209
209
210 def target_update(target,deps,cmd):
210 def target_update(target,deps,cmd):
211 """Update a target with a given command given a list of dependencies.
211 """Update a target with a given command given a list of dependencies.
212
212
213 target_update(target,deps,cmd) -> runs cmd if target is outdated.
213 target_update(target,deps,cmd) -> runs cmd if target is outdated.
214
214
215 This is just a wrapper around target_outdated() which calls the given
215 This is just a wrapper around target_outdated() which calls the given
216 command if target is outdated."""
216 command if target is outdated."""
217
217
218 if target_outdated(target,deps):
218 if target_outdated(target,deps):
219 os.system(cmd)
219 os.system(cmd)
220
220
221 #---------------------------------------------------------------------------
221 #---------------------------------------------------------------------------
222 # Find scripts
222 # Find scripts
223 #---------------------------------------------------------------------------
223 #---------------------------------------------------------------------------
224
224
225 def find_entry_points():
225 def find_entry_points():
226 """Defines the command line entry points for IPython
226 """Defines the command line entry points for IPython
227
227
228 This always uses setuptools-style entry points. When setuptools is not in
228 This always uses setuptools-style entry points. When setuptools is not in
229 use, our own build_scripts_entrypt class below parses these and builds
229 use, our own build_scripts_entrypt class below parses these and builds
230 command line scripts.
230 command line scripts.
231
231
232 Each of our entry points gets both a plain name, e.g. ipython, and one
232 Each of our entry points gets both a plain name, e.g. ipython, and one
233 suffixed with the Python major version number, e.g. ipython3.
233 suffixed with the Python major version number, e.g. ipython3.
234 """
234 """
235 ep = [
235 ep = [
236 'ipython%s = IPython:start_ipython',
236 'ipython%s = IPython:start_ipython',
237 'iptest%s = IPython.testing.iptestcontroller:main',
237 'iptest%s = IPython.testing.iptestcontroller:main',
238 ]
238 ]
239 suffix = str(sys.version_info[0])
239 suffix = str(sys.version_info[0])
240 return [e % '' for e in ep] + [e % suffix for e in ep]
240 return [e % '' for e in ep] + [e % suffix for e in ep]
241
241
242 script_src = """#!{executable}
242 script_src = """#!{executable}
243 # This script was automatically generated by setup.py
243 # This script was automatically generated by setup.py
244 if __name__ == '__main__':
244 if __name__ == '__main__':
245 from {mod} import {func}
245 from {mod} import {func}
246 {func}()
246 {func}()
247 """
247 """
248
248
249 class build_scripts_entrypt(build_scripts):
249 class build_scripts_entrypt(build_scripts):
250 """Build the command line scripts
250 """Build the command line scripts
251
251
252 Parse setuptools style entry points and write simple scripts to run the
252 Parse setuptools style entry points and write simple scripts to run the
253 target functions.
253 target functions.
254
254
255 On Windows, this also creates .cmd wrappers for the scripts so that you can
255 On Windows, this also creates .cmd wrappers for the scripts so that you can
256 easily launch them from a command line.
256 easily launch them from a command line.
257 """
257 """
258 def run(self):
258 def run(self):
259 self.mkpath(self.build_dir)
259 self.mkpath(self.build_dir)
260 outfiles = []
260 outfiles = []
261 for script in find_entry_points():
261 for script in find_entry_points():
262 name, entrypt = script.split('=')
262 name, entrypt = script.split('=')
263 name = name.strip()
263 name = name.strip()
264 entrypt = entrypt.strip()
264 entrypt = entrypt.strip()
265 outfile = os.path.join(self.build_dir, name)
265 outfile = os.path.join(self.build_dir, name)
266 outfiles.append(outfile)
266 outfiles.append(outfile)
267 print('Writing script to', outfile)
267 print('Writing script to', outfile)
268
268
269 mod, func = entrypt.split(':')
269 mod, func = entrypt.split(':')
270 with open(outfile, 'w') as f:
270 with open(outfile, 'w') as f:
271 f.write(script_src.format(executable=sys.executable,
271 f.write(script_src.format(executable=sys.executable,
272 mod=mod, func=func))
272 mod=mod, func=func))
273
273
274 if sys.platform == 'win32':
274 if sys.platform == 'win32':
275 # Write .cmd wrappers for Windows so 'ipython' etc. work at the
275 # Write .cmd wrappers for Windows so 'ipython' etc. work at the
276 # command line
276 # command line
277 cmd_file = os.path.join(self.build_dir, name + '.cmd')
277 cmd_file = os.path.join(self.build_dir, name + '.cmd')
278 cmd = r'@"{python}" "%~dp0\{script}" %*\r\n'.format(
278 cmd = r'@"{python}" "%~dp0\{script}" %*\r\n'.format(
279 python=sys.executable, script=name)
279 python=sys.executable, script=name)
280 log.info("Writing %s wrapper script" % cmd_file)
280 log.info("Writing %s wrapper script" % cmd_file)
281 with open(cmd_file, 'w') as f:
281 with open(cmd_file, 'w') as f:
282 f.write(cmd)
282 f.write(cmd)
283
283
284 return outfiles, outfiles
284 return outfiles, outfiles
285
285
286 class install_lib_symlink(Command):
286 class install_lib_symlink(Command):
287 user_options = [
287 user_options = [
288 ('install-dir=', 'd', "directory to install to"),
288 ('install-dir=', 'd', "directory to install to"),
289 ]
289 ]
290
290
291 def initialize_options(self):
291 def initialize_options(self):
292 self.install_dir = None
292 self.install_dir = None
293
293
294 def finalize_options(self):
294 def finalize_options(self):
295 self.set_undefined_options('symlink',
295 self.set_undefined_options('symlink',
296 ('install_lib', 'install_dir'),
296 ('install_lib', 'install_dir'),
297 )
297 )
298
298
299 def run(self):
299 def run(self):
300 if sys.platform == 'win32':
300 if sys.platform == 'win32':
301 raise Exception("This doesn't work on Windows.")
301 raise Exception("This doesn't work on Windows.")
302 pkg = os.path.join(os.getcwd(), 'IPython')
302 pkg = os.path.join(os.getcwd(), 'IPython')
303 dest = os.path.join(self.install_dir, 'IPython')
303 dest = os.path.join(self.install_dir, 'IPython')
304 if os.path.islink(dest):
304 if os.path.islink(dest):
305 print('removing existing symlink at %s' % dest)
305 print('removing existing symlink at %s' % dest)
306 os.unlink(dest)
306 os.unlink(dest)
307 print('symlinking %s -> %s' % (pkg, dest))
307 print('symlinking %s -> %s' % (pkg, dest))
308 os.symlink(pkg, dest)
308 os.symlink(pkg, dest)
309
309
310 class unsymlink(install):
310 class unsymlink(install):
311 def run(self):
311 def run(self):
312 dest = os.path.join(self.install_lib, 'IPython')
312 dest = os.path.join(self.install_lib, 'IPython')
313 if os.path.islink(dest):
313 if os.path.islink(dest):
314 print('removing symlink at %s' % dest)
314 print('removing symlink at %s' % dest)
315 os.unlink(dest)
315 os.unlink(dest)
316 else:
316 else:
317 print('No symlink exists at %s' % dest)
317 print('No symlink exists at %s' % dest)
318
318
319 class install_symlinked(install):
319 class install_symlinked(install):
320 def run(self):
320 def run(self):
321 if sys.platform == 'win32':
321 if sys.platform == 'win32':
322 raise Exception("This doesn't work on Windows.")
322 raise Exception("This doesn't work on Windows.")
323
323
324 # Run all sub-commands (at least those that need to be run)
324 # Run all sub-commands (at least those that need to be run)
325 for cmd_name in self.get_sub_commands():
325 for cmd_name in self.get_sub_commands():
326 self.run_command(cmd_name)
326 self.run_command(cmd_name)
327
327
328 # 'sub_commands': a list of commands this command might have to run to
328 # 'sub_commands': a list of commands this command might have to run to
329 # get its work done. See cmd.py for more info.
329 # get its work done. See cmd.py for more info.
330 sub_commands = [('install_lib_symlink', lambda self:True),
330 sub_commands = [('install_lib_symlink', lambda self:True),
331 ('install_scripts_sym', lambda self:True),
331 ('install_scripts_sym', lambda self:True),
332 ]
332 ]
333
333
334 class install_scripts_for_symlink(install_scripts):
334 class install_scripts_for_symlink(install_scripts):
335 """Redefined to get options from 'symlink' instead of 'install'.
335 """Redefined to get options from 'symlink' instead of 'install'.
336
336
337 I love distutils almost as much as I love setuptools.
337 I love distutils almost as much as I love setuptools.
338 """
338 """
339 def finalize_options(self):
339 def finalize_options(self):
340 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
340 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
341 self.set_undefined_options('symlink',
341 self.set_undefined_options('symlink',
342 ('install_scripts', 'install_dir'),
342 ('install_scripts', 'install_dir'),
343 ('force', 'force'),
343 ('force', 'force'),
344 ('skip_build', 'skip_build'),
344 ('skip_build', 'skip_build'),
345 )
345 )
346
346
347
347
348 #---------------------------------------------------------------------------
348 #---------------------------------------------------------------------------
349 # VCS related
349 # VCS related
350 #---------------------------------------------------------------------------
350 #---------------------------------------------------------------------------
351
351
352
352
353 def git_prebuild(pkg_dir, build_cmd=build_py):
353 def git_prebuild(pkg_dir, build_cmd=build_py):
354 """Return extended build or sdist command class for recording commit
354 """Return extended build or sdist command class for recording commit
355
355
356 records git commit in IPython.utils._sysinfo.commit
356 records git commit in IPython.utils._sysinfo.commit
357
357
358 for use in IPython.utils.sysinfo.sys_info() calls after installation.
358 for use in IPython.utils.sysinfo.sys_info() calls after installation.
359 """
359 """
360
360
361 class MyBuildPy(build_cmd):
361 class MyBuildPy(build_cmd):
362 ''' Subclass to write commit data into installation tree '''
362 ''' Subclass to write commit data into installation tree '''
363 def run(self):
363 def run(self):
364 # loose as `.dev` is suppose to be invalid
364 # loose as `.dev` is suppose to be invalid
365 print("check version number")
365 print("check version number")
366 loose_pep440re = re.compile(r'^(\d+)\.(\d+)\.(\d+((a|b|rc)\d+)?)(\.post\d+)?(\.dev\d*)?$')
366 loose_pep440re = re.compile(r'^(\d+)\.(\d+)\.(\d+((a|b|rc)\d+)?)(\.post\d+)?(\.dev\d*)?$')
367 if not loose_pep440re.match(version):
367 if not loose_pep440re.match(version):
368 raise ValueError("Version number '%s' is not valid (should match [N!]N(.N)*[{a|b|rc}N][.postN][.devN])" % version)
368 raise ValueError("Version number '%s' is not valid (should match [N!]N(.N)*[{a|b|rc}N][.postN][.devN])" % version)
369
369
370
370
371 build_cmd.run(self)
371 build_cmd.run(self)
372 # this one will only fire for build commands
372 # this one will only fire for build commands
373 if hasattr(self, 'build_lib'):
373 if hasattr(self, 'build_lib'):
374 self._record_commit(self.build_lib)
374 self._record_commit(self.build_lib)
375
375
376 def make_release_tree(self, base_dir, files):
376 def make_release_tree(self, base_dir, files):
377 # this one will fire for sdist
377 # this one will fire for sdist
378 build_cmd.make_release_tree(self, base_dir, files)
378 build_cmd.make_release_tree(self, base_dir, files)
379 self._record_commit(base_dir)
379 self._record_commit(base_dir)
380
380
381 def _record_commit(self, base_dir):
381 def _record_commit(self, base_dir):
382 import subprocess
382 import subprocess
383 proc = subprocess.Popen('git rev-parse --short HEAD',
383 proc = subprocess.Popen('git rev-parse --short HEAD',
384 stdout=subprocess.PIPE,
384 stdout=subprocess.PIPE,
385 stderr=subprocess.PIPE,
385 stderr=subprocess.PIPE,
386 shell=True)
386 shell=True)
387 repo_commit, _ = proc.communicate()
387 repo_commit, _ = proc.communicate()
388 repo_commit = repo_commit.strip().decode("ascii")
388 repo_commit = repo_commit.strip().decode("ascii")
389
389
390 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
390 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
391 if os.path.isfile(out_pth) and not repo_commit:
391 if os.path.isfile(out_pth) and not repo_commit:
392 # nothing to write, don't clobber
392 # nothing to write, don't clobber
393 return
393 return
394
394
395 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
395 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
396
396
397 # remove to avoid overwriting original via hard link
397 # remove to avoid overwriting original via hard link
398 try:
398 try:
399 os.remove(out_pth)
399 os.remove(out_pth)
400 except (IOError, OSError):
400 except (IOError, OSError):
401 pass
401 pass
402 with open(out_pth, 'w') as out_file:
402 with open(out_pth, 'w') as out_file:
403 out_file.writelines([
403 out_file.writelines([
404 '# GENERATED BY setup.py\n',
404 '# GENERATED BY setup.py\n',
405 'commit = u"%s"\n' % repo_commit,
405 'commit = u"%s"\n' % repo_commit,
406 ])
406 ])
407 return MyBuildPy
407 return MyBuildPy
408
408
General Comments 0
You need to be logged in to leave comments. Login now