##// END OF EJS Templates
setup.py: Remove commands symlink, unsymlink
Matthias Koeppe -
Show More
@@ -1,158 +1,150 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Setup script for IPython.
2 """Setup script for IPython.
3
3
4 Under Posix environments it works like a typical setup.py script.
4 Under Posix environments it works like a typical setup.py script.
5 Under Windows, the command sdist is not supported, since IPython
5 Under Windows, the command sdist is not supported, since IPython
6 requires utilities which are not available under Windows."""
6 requires utilities which are not available under Windows."""
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (c) 2008-2011, IPython Development Team.
9 # Copyright (c) 2008-2011, IPython Development Team.
10 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
10 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
11 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
11 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
12 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
12 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
13 #
13 #
14 # Distributed under the terms of the Modified BSD License.
14 # Distributed under the terms of the Modified BSD License.
15 #
15 #
16 # The full license is in the file COPYING.rst, distributed with this software.
16 # The full license is in the file COPYING.rst, distributed with this software.
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 import os
19 import os
20 import sys
20 import sys
21
21
22 # **Python version check**
22 # **Python version check**
23 #
23 #
24 # This check is also made in IPython/__init__, don't forget to update both when
24 # This check is also made in IPython/__init__, don't forget to update both when
25 # changing Python version requirements.
25 # changing Python version requirements.
26 if sys.version_info < (3, 10):
26 if sys.version_info < (3, 10):
27 pip_message = 'This may be due to an out of date pip. Make sure you have pip >= 9.0.1.'
27 pip_message = 'This may be due to an out of date pip. Make sure you have pip >= 9.0.1.'
28 try:
28 try:
29 import pip
29 import pip
30 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
30 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
31 if pip_version < (9, 0, 1) :
31 if pip_version < (9, 0, 1) :
32 pip_message = 'Your pip version is out of date, please install pip >= 9.0.1. '\
32 pip_message = 'Your pip version is out of date, please install pip >= 9.0.1. '\
33 'pip {} detected.'.format(pip.__version__)
33 'pip {} detected.'.format(pip.__version__)
34 else:
34 else:
35 # pip is new enough - it must be something else
35 # pip is new enough - it must be something else
36 pip_message = ''
36 pip_message = ''
37 except Exception:
37 except Exception:
38 pass
38 pass
39
39
40
40
41 error = """
41 error = """
42 IPython 8.19+ supports Python 3.10 and above, following SPEC0
42 IPython 8.19+ supports Python 3.10 and above, following SPEC0
43 IPython 8.13+ supports Python 3.9 and above, following NEP 29.
43 IPython 8.13+ supports Python 3.9 and above, following NEP 29.
44 IPython 8.0-8.12 supports Python 3.8 and above, following NEP 29.
44 IPython 8.0-8.12 supports Python 3.8 and above, following NEP 29.
45 When using Python 2.7, please install IPython 5.x LTS Long Term Support version.
45 When using Python 2.7, please install IPython 5.x LTS Long Term Support version.
46 Python 3.3 and 3.4 were supported up to IPython 6.x.
46 Python 3.3 and 3.4 were supported up to IPython 6.x.
47 Python 3.5 was supported with IPython 7.0 to 7.9.
47 Python 3.5 was supported with IPython 7.0 to 7.9.
48 Python 3.6 was supported with IPython up to 7.16.
48 Python 3.6 was supported with IPython up to 7.16.
49 Python 3.7 was still supported with the 7.x branch.
49 Python 3.7 was still supported with the 7.x branch.
50
50
51 See IPython `README.rst` file for more information:
51 See IPython `README.rst` file for more information:
52
52
53 https://github.com/ipython/ipython/blob/main/README.rst
53 https://github.com/ipython/ipython/blob/main/README.rst
54
54
55 Python {py} detected.
55 Python {py} detected.
56 {pip}
56 {pip}
57 """.format(
57 """.format(
58 py=sys.version_info, pip=pip_message
58 py=sys.version_info, pip=pip_message
59 )
59 )
60
60
61 print(error, file=sys.stderr)
61 print(error, file=sys.stderr)
62 sys.exit(1)
62 sys.exit(1)
63
63
64 # At least we're on the python version we need, move on.
64 # At least we're on the python version we need, move on.
65
65
66 from setuptools import setup
66 from setuptools import setup
67
67
68 # Our own imports
68 # Our own imports
69
69
70 from setupbase import target_update, find_entry_points
70 from setupbase import target_update, find_entry_points
71
71
72 from setupbase import (
72 from setupbase import (
73 setup_args,
73 setup_args,
74 check_package_data_first,
74 check_package_data_first,
75 find_data_files,
75 find_data_files,
76 git_prebuild,
76 git_prebuild,
77 install_symlinked,
78 install_lib_symlink,
79 install_scripts_for_symlink,
80 unsymlink,
81 )
77 )
82
78
83 #-------------------------------------------------------------------------------
79 #-------------------------------------------------------------------------------
84 # Handle OS specific things
80 # Handle OS specific things
85 #-------------------------------------------------------------------------------
81 #-------------------------------------------------------------------------------
86
82
87 if os.name in ('nt','dos'):
83 if os.name in ('nt','dos'):
88 os_name = 'windows'
84 os_name = 'windows'
89 else:
85 else:
90 os_name = os.name
86 os_name = os.name
91
87
92 # Under Windows, 'sdist' has not been supported. Now that the docs build with
88 # Under Windows, 'sdist' has not been supported. Now that the docs build with
93 # Sphinx it might work, but let's not turn it on until someone confirms that it
89 # Sphinx it might work, but let's not turn it on until someone confirms that it
94 # actually works.
90 # actually works.
95 if os_name == 'windows' and 'sdist' in sys.argv:
91 if os_name == 'windows' and 'sdist' in sys.argv:
96 print('The sdist command is not available under Windows. Exiting.')
92 print('The sdist command is not available under Windows. Exiting.')
97 sys.exit(1)
93 sys.exit(1)
98
94
99
95
100 #-------------------------------------------------------------------------------
96 #-------------------------------------------------------------------------------
101 # Things related to the IPython documentation
97 # Things related to the IPython documentation
102 #-------------------------------------------------------------------------------
98 #-------------------------------------------------------------------------------
103
99
104 # update the manuals when building a source dist
100 # update the manuals when building a source dist
105 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
101 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
106
102
107 # List of things to be updated. Each entry is a triplet of args for
103 # List of things to be updated. Each entry is a triplet of args for
108 # target_update()
104 # target_update()
109 to_update = [
105 to_update = [
110 (
106 (
111 "docs/man/ipython.1.gz",
107 "docs/man/ipython.1.gz",
112 ["docs/man/ipython.1"],
108 ["docs/man/ipython.1"],
113 "cd docs/man && python -m gzip --best ipython.1",
109 "cd docs/man && python -m gzip --best ipython.1",
114 ),
110 ),
115 ]
111 ]
116
112
117
113
118 [ target_update(*t) for t in to_update ]
114 [ target_update(*t) for t in to_update ]
119
115
120 #---------------------------------------------------------------------------
116 #---------------------------------------------------------------------------
121 # Find all the packages, package data, and data_files
117 # Find all the packages, package data, and data_files
122 #---------------------------------------------------------------------------
118 #---------------------------------------------------------------------------
123
119
124 data_files = find_data_files()
120 data_files = find_data_files()
125
121
126 setup_args['data_files'] = data_files
122 setup_args['data_files'] = data_files
127
123
128 #---------------------------------------------------------------------------
124 #---------------------------------------------------------------------------
129 # custom distutils commands
125 # custom distutils commands
130 #---------------------------------------------------------------------------
126 #---------------------------------------------------------------------------
131 # imports here, so they are after setuptools import if there was one
127 # imports here, so they are after setuptools import if there was one
132 from setuptools.command.sdist import sdist
128 from setuptools.command.sdist import sdist
133
129
134 setup_args['cmdclass'] = {
130 setup_args['cmdclass'] = {
135 'build_py': \
131 'build_py': \
136 check_package_data_first(git_prebuild('IPython')),
132 check_package_data_first(git_prebuild('IPython')),
137 'sdist' : git_prebuild('IPython', sdist),
133 'sdist' : git_prebuild('IPython', sdist),
138 'symlink': install_symlinked,
139 'install_lib_symlink': install_lib_symlink,
140 'install_scripts_sym': install_scripts_for_symlink,
141 'unsymlink': unsymlink,
142 }
134 }
143
135
144 setup_args["entry_points"] = {
136 setup_args["entry_points"] = {
145 "console_scripts": find_entry_points(),
137 "console_scripts": find_entry_points(),
146 "pygments.lexers": [
138 "pygments.lexers": [
147 "ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer",
139 "ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer",
148 "ipython = IPython.lib.lexers:IPythonLexer",
140 "ipython = IPython.lib.lexers:IPythonLexer",
149 "ipython3 = IPython.lib.lexers:IPython3Lexer",
141 "ipython3 = IPython.lib.lexers:IPython3Lexer",
150 ],
142 ],
151 }
143 }
152
144
153 #---------------------------------------------------------------------------
145 #---------------------------------------------------------------------------
154 # Do the actual setup now
146 # Do the actual setup now
155 #---------------------------------------------------------------------------
147 #---------------------------------------------------------------------------
156
148
157 if __name__ == "__main__":
149 if __name__ == "__main__":
158 setup(**setup_args)
150 setup(**setup_args)
@@ -1,349 +1,285 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
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 from setuptools.command.install import install
24 from setuptools.command.install import install
25 from setuptools.command.install_scripts import install_scripts
25 from setuptools.command.install_scripts import install_scripts
26
26
27
27
28 #-------------------------------------------------------------------------------
28 #-------------------------------------------------------------------------------
29 # Useful globals and utility functions
29 # Useful globals and utility functions
30 #-------------------------------------------------------------------------------
30 #-------------------------------------------------------------------------------
31
31
32 # A few handy globals
32 # A few handy globals
33 isfile = os.path.isfile
33 isfile = os.path.isfile
34 pjoin = os.path.join
34 pjoin = os.path.join
35 repo_root = os.path.dirname(os.path.abspath(__file__))
35 repo_root = os.path.dirname(os.path.abspath(__file__))
36
36
37 def execfile(fname, globs, locs=None):
37 def execfile(fname, globs, locs=None):
38 locs = locs or globs
38 locs = locs or globs
39 with open(fname, encoding="utf-8") as f:
39 with open(fname, encoding="utf-8") as f:
40 exec(compile(f.read(), fname, "exec"), globs, locs)
40 exec(compile(f.read(), fname, "exec"), globs, locs)
41
41
42 # A little utility we'll need below, since glob() does NOT allow you to do
42 # A little utility we'll need below, since glob() does NOT allow you to do
43 # exclusion on multiple endings!
43 # exclusion on multiple endings!
44 def file_doesnt_endwith(test,endings):
44 def file_doesnt_endwith(test,endings):
45 """Return true if test is a file and its name does NOT end with any
45 """Return true if test is a file and its name does NOT end with any
46 of the strings listed in endings."""
46 of the strings listed in endings."""
47 if not isfile(test):
47 if not isfile(test):
48 return False
48 return False
49 for e in endings:
49 for e in endings:
50 if test.endswith(e):
50 if test.endswith(e):
51 return False
51 return False
52 return True
52 return True
53
53
54 #---------------------------------------------------------------------------
54 #---------------------------------------------------------------------------
55 # Basic project information
55 # Basic project information
56 #---------------------------------------------------------------------------
56 #---------------------------------------------------------------------------
57
57
58 # release.py contains version, authors, license, url, keywords, etc.
58 # release.py contains version, authors, license, url, keywords, etc.
59 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
59 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
60
60
61 # Create a dict with the basic information
61 # Create a dict with the basic information
62 # This dict is eventually passed to setup after additional keys are added.
62 # This dict is eventually passed to setup after additional keys are added.
63 setup_args = dict(
63 setup_args = dict(
64 author = author,
64 author = author,
65 author_email = author_email,
65 author_email = author_email,
66 license = license,
66 license = license,
67 )
67 )
68
68
69
69
70 #---------------------------------------------------------------------------
70 #---------------------------------------------------------------------------
71 # Find packages
71 # Find packages
72 #---------------------------------------------------------------------------
72 #---------------------------------------------------------------------------
73
73
74 def find_packages():
74 def find_packages():
75 """
75 """
76 Find all of IPython's packages.
76 Find all of IPython's packages.
77 """
77 """
78 excludes = ['deathrow', 'quarantine']
78 excludes = ['deathrow', 'quarantine']
79 packages = []
79 packages = []
80 for directory, subdirs, files in os.walk("IPython"):
80 for directory, subdirs, files in os.walk("IPython"):
81 package = directory.replace(os.path.sep, ".")
81 package = directory.replace(os.path.sep, ".")
82 if any(package.startswith("IPython." + exc) for exc in excludes):
82 if any(package.startswith("IPython." + exc) for exc in excludes):
83 # package is to be excluded (e.g. deathrow)
83 # package is to be excluded (e.g. deathrow)
84 continue
84 continue
85 if '__init__.py' not in files:
85 if '__init__.py' not in files:
86 # not a package
86 # not a package
87 continue
87 continue
88 packages.append(package)
88 packages.append(package)
89 return packages
89 return packages
90
90
91 #---------------------------------------------------------------------------
91 #---------------------------------------------------------------------------
92 # Find package data
92 # Find package data
93 #---------------------------------------------------------------------------
93 #---------------------------------------------------------------------------
94
94
95 def find_package_data():
95 def find_package_data():
96 """
96 """
97 Find IPython's package_data.
97 Find IPython's package_data.
98 """
98 """
99 # This is not enough for these things to appear in an sdist.
99 # This is not enough for these things to appear in an sdist.
100 # We need to muck with the MANIFEST to get this to work
100 # We need to muck with the MANIFEST to get this to work
101
101
102 package_data = {
102 package_data = {
103 'IPython.core' : ['profile/README*'],
103 'IPython.core' : ['profile/README*'],
104 'IPython.core.tests' : ['*.png', '*.jpg', 'daft_extension/*.py'],
104 'IPython.core.tests' : ['*.png', '*.jpg', 'daft_extension/*.py'],
105 'IPython.lib.tests' : ['*.wav'],
105 'IPython.lib.tests' : ['*.wav'],
106 'IPython.testing.plugin' : ['*.txt'],
106 'IPython.testing.plugin' : ['*.txt'],
107 }
107 }
108
108
109 return package_data
109 return package_data
110
110
111
111
112 def check_package_data(package_data):
112 def check_package_data(package_data):
113 """verify that package_data globs make sense"""
113 """verify that package_data globs make sense"""
114 print("checking package data")
114 print("checking package data")
115 for pkg, data in package_data.items():
115 for pkg, data in package_data.items():
116 pkg_root = pjoin(*pkg.split('.'))
116 pkg_root = pjoin(*pkg.split('.'))
117 for d in data:
117 for d in data:
118 path = pjoin(pkg_root, d)
118 path = pjoin(pkg_root, d)
119 if '*' in path:
119 if '*' in path:
120 assert len(glob(path)) > 0, "No files match pattern %s" % path
120 assert len(glob(path)) > 0, "No files match pattern %s" % path
121 else:
121 else:
122 assert os.path.exists(path), "Missing package data: %s" % path
122 assert os.path.exists(path), "Missing package data: %s" % path
123
123
124
124
125 def check_package_data_first(command):
125 def check_package_data_first(command):
126 """decorator for checking package_data before running a given command
126 """decorator for checking package_data before running a given command
127
127
128 Probably only needs to wrap build_py
128 Probably only needs to wrap build_py
129 """
129 """
130 class DecoratedCommand(command):
130 class DecoratedCommand(command):
131 def run(self):
131 def run(self):
132 check_package_data(self.package_data)
132 check_package_data(self.package_data)
133 command.run(self)
133 command.run(self)
134 return DecoratedCommand
134 return DecoratedCommand
135
135
136
136
137 #---------------------------------------------------------------------------
137 #---------------------------------------------------------------------------
138 # Find data files
138 # Find data files
139 #---------------------------------------------------------------------------
139 #---------------------------------------------------------------------------
140
140
141 def find_data_files():
141 def find_data_files():
142 """
142 """
143 Find IPython's data_files.
143 Find IPython's data_files.
144
144
145 Just man pages at this point.
145 Just man pages at this point.
146 """
146 """
147
147
148 if "freebsd" in sys.platform:
148 if "freebsd" in sys.platform:
149 manpagebase = pjoin('man', 'man1')
149 manpagebase = pjoin('man', 'man1')
150 else:
150 else:
151 manpagebase = pjoin('share', 'man', 'man1')
151 manpagebase = pjoin('share', 'man', 'man1')
152
152
153 # Simple file lists can be made by hand
153 # Simple file lists can be made by hand
154 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
154 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
155 if not manpages:
155 if not manpages:
156 # When running from a source tree, the manpages aren't gzipped
156 # When running from a source tree, the manpages aren't gzipped
157 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
157 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
158
158
159 # And assemble the entire output list
159 # And assemble the entire output list
160 data_files = [ (manpagebase, manpages) ]
160 data_files = [ (manpagebase, manpages) ]
161
161
162 return data_files
162 return data_files
163
163
164
164
165 # The two functions below are copied from IPython.utils.path, so we don't need
165 # The two functions below are copied from IPython.utils.path, so we don't need
166 # to import IPython during setup, which fails on Python 3.
166 # to import IPython during setup, which fails on Python 3.
167
167
168 def target_outdated(target,deps):
168 def target_outdated(target,deps):
169 """Determine whether a target is out of date.
169 """Determine whether a target is out of date.
170
170
171 target_outdated(target,deps) -> 1/0
171 target_outdated(target,deps) -> 1/0
172
172
173 deps: list of filenames which MUST exist.
173 deps: list of filenames which MUST exist.
174 target: single filename which may or may not exist.
174 target: single filename which may or may not exist.
175
175
176 If target doesn't exist or is older than any file listed in deps, return
176 If target doesn't exist or is older than any file listed in deps, return
177 true, otherwise return false.
177 true, otherwise return false.
178 """
178 """
179 try:
179 try:
180 target_time = os.path.getmtime(target)
180 target_time = os.path.getmtime(target)
181 except os.error:
181 except os.error:
182 return 1
182 return 1
183 for dep in deps:
183 for dep in deps:
184 dep_time = os.path.getmtime(dep)
184 dep_time = os.path.getmtime(dep)
185 if dep_time > target_time:
185 if dep_time > target_time:
186 #print "For target",target,"Dep failed:",dep # dbg
186 #print "For target",target,"Dep failed:",dep # dbg
187 #print "times (dep,tar):",dep_time,target_time # dbg
187 #print "times (dep,tar):",dep_time,target_time # dbg
188 return 1
188 return 1
189 return 0
189 return 0
190
190
191
191
192 def target_update(target,deps,cmd):
192 def target_update(target,deps,cmd):
193 """Update a target with a given command given a list of dependencies.
193 """Update a target with a given command given a list of dependencies.
194
194
195 target_update(target,deps,cmd) -> runs cmd if target is outdated.
195 target_update(target,deps,cmd) -> runs cmd if target is outdated.
196
196
197 This is just a wrapper around target_outdated() which calls the given
197 This is just a wrapper around target_outdated() which calls the given
198 command if target is outdated."""
198 command if target is outdated."""
199
199
200 if target_outdated(target,deps):
200 if target_outdated(target,deps):
201 os.system(cmd)
201 os.system(cmd)
202
202
203 #---------------------------------------------------------------------------
203 #---------------------------------------------------------------------------
204 # Find scripts
204 # Find scripts
205 #---------------------------------------------------------------------------
205 #---------------------------------------------------------------------------
206
206
207 def find_entry_points():
207 def find_entry_points():
208 """Defines the command line entry points for IPython
208 """Defines the command line entry points for IPython
209
209
210 This always uses setuptools-style entry points. When setuptools is not in
210 This always uses setuptools-style entry points. When setuptools is not in
211 use, our own build_scripts_entrypt class below parses these and builds
211 use, our own build_scripts_entrypt class below parses these and builds
212 command line scripts.
212 command line scripts.
213
213
214 Each of our entry points gets a plain name, e.g. ipython, and a name
214 Each of our entry points gets a plain name, e.g. ipython, and a name
215 suffixed with the Python major version number, e.g. ipython3.
215 suffixed with the Python major version number, e.g. ipython3.
216 """
216 """
217 ep = [
217 ep = [
218 'ipython%s = IPython:start_ipython',
218 'ipython%s = IPython:start_ipython',
219 ]
219 ]
220 major_suffix = str(sys.version_info[0])
220 major_suffix = str(sys.version_info[0])
221 return [e % "" for e in ep] + [e % major_suffix for e in ep]
221 return [e % "" for e in ep] + [e % major_suffix for e in ep]
222
222
223
224 class install_lib_symlink(Command):
225 user_options = [
226 ('install-dir=', 'd', "directory to install to"),
227 ]
228
229 def initialize_options(self):
230 self.install_dir = None
231
232 def finalize_options(self):
233 self.set_undefined_options('symlink',
234 ('install_lib', 'install_dir'),
235 )
236
237 def run(self):
238 if sys.platform == 'win32':
239 raise Exception("This doesn't work on Windows.")
240 pkg = os.path.join(os.getcwd(), 'IPython')
241 dest = os.path.join(self.install_dir, 'IPython')
242 if os.path.islink(dest):
243 print('removing existing symlink at %s' % dest)
244 os.unlink(dest)
245 print('symlinking %s -> %s' % (pkg, dest))
246 os.symlink(pkg, dest)
247
248 class unsymlink(install):
249 def run(self):
250 dest = os.path.join(self.install_lib, 'IPython')
251 if os.path.islink(dest):
252 print('removing symlink at %s' % dest)
253 os.unlink(dest)
254 else:
255 print('No symlink exists at %s' % dest)
256
257 class install_symlinked(install):
258 def run(self):
259 if sys.platform == 'win32':
260 raise Exception("This doesn't work on Windows.")
261
262 # Run all sub-commands (at least those that need to be run)
263 for cmd_name in self.get_sub_commands():
264 self.run_command(cmd_name)
265
266 # 'sub_commands': a list of commands this command might have to run to
267 # get its work done. See cmd.py for more info.
268 sub_commands = [('install_lib_symlink', lambda self:True),
269 ('install_scripts_sym', lambda self:True),
270 ]
271
272 class install_scripts_for_symlink(install_scripts):
273 """Redefined to get options from 'symlink' instead of 'install'.
274
275 I love distutils almost as much as I love setuptools.
276 """
277 def finalize_options(self):
278 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
279 self.set_undefined_options('symlink',
280 ('install_scripts', 'install_dir'),
281 ('force', 'force'),
282 ('skip_build', 'skip_build'),
283 )
284
285
286 #---------------------------------------------------------------------------
223 #---------------------------------------------------------------------------
287 # VCS related
224 # VCS related
288 #---------------------------------------------------------------------------
225 #---------------------------------------------------------------------------
289
226
290
291 def git_prebuild(pkg_dir, build_cmd=build_py):
227 def git_prebuild(pkg_dir, build_cmd=build_py):
292 """Return extended build or sdist command class for recording commit
228 """Return extended build or sdist command class for recording commit
293
229
294 records git commit in IPython.utils._sysinfo.commit
230 records git commit in IPython.utils._sysinfo.commit
295
231
296 for use in IPython.utils.sysinfo.sys_info() calls after installation.
232 for use in IPython.utils.sysinfo.sys_info() calls after installation.
297 """
233 """
298
234
299 class MyBuildPy(build_cmd):
235 class MyBuildPy(build_cmd):
300 ''' Subclass to write commit data into installation tree '''
236 ''' Subclass to write commit data into installation tree '''
301 def run(self):
237 def run(self):
302 # loose as `.dev` is suppose to be invalid
238 # loose as `.dev` is suppose to be invalid
303 print("check version number")
239 print("check version number")
304 loose_pep440re = re.compile(r'^(\d+)\.(\d+)\.(\d+((a|b|rc)\d+)?)(\.post\d+)?(\.dev\d*)?$')
240 loose_pep440re = re.compile(r'^(\d+)\.(\d+)\.(\d+((a|b|rc)\d+)?)(\.post\d+)?(\.dev\d*)?$')
305 if not loose_pep440re.match(version):
241 if not loose_pep440re.match(version):
306 raise ValueError("Version number '%s' is not valid (should match [N!]N(.N)*[{a|b|rc}N][.postN][.devN])" % version)
242 raise ValueError("Version number '%s' is not valid (should match [N!]N(.N)*[{a|b|rc}N][.postN][.devN])" % version)
307
243
308
244
309 build_cmd.run(self)
245 build_cmd.run(self)
310 # this one will only fire for build commands
246 # this one will only fire for build commands
311 if hasattr(self, 'build_lib'):
247 if hasattr(self, 'build_lib'):
312 self._record_commit(self.build_lib)
248 self._record_commit(self.build_lib)
313
249
314 def make_release_tree(self, base_dir, files):
250 def make_release_tree(self, base_dir, files):
315 # this one will fire for sdist
251 # this one will fire for sdist
316 build_cmd.make_release_tree(self, base_dir, files)
252 build_cmd.make_release_tree(self, base_dir, files)
317 self._record_commit(base_dir)
253 self._record_commit(base_dir)
318
254
319 def _record_commit(self, base_dir):
255 def _record_commit(self, base_dir):
320 import subprocess
256 import subprocess
321 proc = subprocess.Popen('git rev-parse --short HEAD',
257 proc = subprocess.Popen('git rev-parse --short HEAD',
322 stdout=subprocess.PIPE,
258 stdout=subprocess.PIPE,
323 stderr=subprocess.PIPE,
259 stderr=subprocess.PIPE,
324 shell=True)
260 shell=True)
325 repo_commit, _ = proc.communicate()
261 repo_commit, _ = proc.communicate()
326 repo_commit = repo_commit.strip().decode("ascii")
262 repo_commit = repo_commit.strip().decode("ascii")
327
263
328 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
264 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
329 if os.path.isfile(out_pth) and not repo_commit:
265 if os.path.isfile(out_pth) and not repo_commit:
330 # nothing to write, don't clobber
266 # nothing to write, don't clobber
331 return
267 return
332
268
333 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
269 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
334
270
335 # remove to avoid overwriting original via hard link
271 # remove to avoid overwriting original via hard link
336 try:
272 try:
337 os.remove(out_pth)
273 os.remove(out_pth)
338 except (IOError, OSError):
274 except (IOError, OSError):
339 pass
275 pass
340 with open(out_pth, "w", encoding="utf-8") as out_file:
276 with open(out_pth, "w", encoding="utf-8") as out_file:
341 out_file.writelines(
277 out_file.writelines(
342 [
278 [
343 "# GENERATED BY setup.py\n",
279 "# GENERATED BY setup.py\n",
344 'commit = "%s"\n' % repo_commit,
280 'commit = "%s"\n' % repo_commit,
345 ]
281 ]
346 )
282 )
347
283
348 return MyBuildPy
284 return MyBuildPy
349
285
General Comments 0
You need to be logged in to leave comments. Login now