|
@@
-1,347
+1,345
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
|
from setupext import install_data_ext
|
|
|
|
|
28
|
|
|
27
|
|
|
29
|
#-------------------------------------------------------------------------------
|
|
28
|
#-------------------------------------------------------------------------------
|
|
30
|
# Useful globals and utility functions
|
|
29
|
# Useful globals and utility functions
|
|
31
|
#-------------------------------------------------------------------------------
|
|
30
|
#-------------------------------------------------------------------------------
|
|
32
|
|
|
31
|
|
|
33
|
# A few handy globals
|
|
32
|
# A few handy globals
|
|
34
|
isfile = os.path.isfile
|
|
33
|
isfile = os.path.isfile
|
|
35
|
pjoin = os.path.join
|
|
34
|
pjoin = os.path.join
|
|
36
|
repo_root = os.path.dirname(os.path.abspath(__file__))
|
|
35
|
repo_root = os.path.dirname(os.path.abspath(__file__))
|
|
37
|
|
|
36
|
|
|
38
|
def execfile(fname, globs, locs=None):
|
|
37
|
def execfile(fname, globs, locs=None):
|
|
39
|
locs = locs or globs
|
|
38
|
locs = locs or globs
|
|
40
|
with open(fname) as f:
|
|
39
|
with open(fname) as f:
|
|
41
|
exec(compile(f.read(), fname, "exec"), globs, locs)
|
|
40
|
exec(compile(f.read(), fname, "exec"), globs, locs)
|
|
42
|
|
|
41
|
|
|
43
|
# 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
|
|
44
|
# exclusion on multiple endings!
|
|
43
|
# exclusion on multiple endings!
|
|
45
|
def file_doesnt_endwith(test,endings):
|
|
44
|
def file_doesnt_endwith(test,endings):
|
|
46
|
"""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
|
|
47
|
of the strings listed in endings."""
|
|
46
|
of the strings listed in endings."""
|
|
48
|
if not isfile(test):
|
|
47
|
if not isfile(test):
|
|
49
|
return False
|
|
48
|
return False
|
|
50
|
for e in endings:
|
|
49
|
for e in endings:
|
|
51
|
if test.endswith(e):
|
|
50
|
if test.endswith(e):
|
|
52
|
return False
|
|
51
|
return False
|
|
53
|
return True
|
|
52
|
return True
|
|
54
|
|
|
53
|
|
|
55
|
#---------------------------------------------------------------------------
|
|
54
|
#---------------------------------------------------------------------------
|
|
56
|
# Basic project information
|
|
55
|
# Basic project information
|
|
57
|
#---------------------------------------------------------------------------
|
|
56
|
#---------------------------------------------------------------------------
|
|
58
|
|
|
57
|
|
|
59
|
# release.py contains version, authors, license, url, keywords, etc.
|
|
58
|
# release.py contains version, authors, license, url, keywords, etc.
|
|
60
|
execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
|
|
59
|
execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
|
|
61
|
|
|
60
|
|
|
62
|
# Create a dict with the basic information
|
|
61
|
# Create a dict with the basic information
|
|
63
|
# 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.
|
|
64
|
setup_args = dict(
|
|
63
|
setup_args = dict(
|
|
65
|
author = author,
|
|
64
|
author = author,
|
|
66
|
author_email = author_email,
|
|
65
|
author_email = author_email,
|
|
67
|
license = license,
|
|
66
|
license = license,
|
|
68
|
cmdclass = {'install_data': install_data_ext},
|
|
|
|
|
69
|
)
|
|
67
|
)
|
|
70
|
|
|
68
|
|
|
71
|
|
|
69
|
|
|
72
|
#---------------------------------------------------------------------------
|
|
70
|
#---------------------------------------------------------------------------
|
|
73
|
# Find packages
|
|
71
|
# Find packages
|
|
74
|
#---------------------------------------------------------------------------
|
|
72
|
#---------------------------------------------------------------------------
|
|
75
|
|
|
73
|
|
|
76
|
def find_packages():
|
|
74
|
def find_packages():
|
|
77
|
"""
|
|
75
|
"""
|
|
78
|
Find all of IPython's packages.
|
|
76
|
Find all of IPython's packages.
|
|
79
|
"""
|
|
77
|
"""
|
|
80
|
excludes = ['deathrow', 'quarantine']
|
|
78
|
excludes = ['deathrow', 'quarantine']
|
|
81
|
packages = []
|
|
79
|
packages = []
|
|
82
|
for directory, subdirs, files in os.walk("IPython"):
|
|
80
|
for directory, subdirs, files in os.walk("IPython"):
|
|
83
|
package = directory.replace(os.path.sep, ".")
|
|
81
|
package = directory.replace(os.path.sep, ".")
|
|
84
|
if any(package.startswith("IPython." + exc) for exc in excludes):
|
|
82
|
if any(package.startswith("IPython." + exc) for exc in excludes):
|
|
85
|
# package is to be excluded (e.g. deathrow)
|
|
83
|
# package is to be excluded (e.g. deathrow)
|
|
86
|
continue
|
|
84
|
continue
|
|
87
|
if '__init__.py' not in files:
|
|
85
|
if '__init__.py' not in files:
|
|
88
|
# not a package
|
|
86
|
# not a package
|
|
89
|
continue
|
|
87
|
continue
|
|
90
|
packages.append(package)
|
|
88
|
packages.append(package)
|
|
91
|
return packages
|
|
89
|
return packages
|
|
92
|
|
|
90
|
|
|
93
|
#---------------------------------------------------------------------------
|
|
91
|
#---------------------------------------------------------------------------
|
|
94
|
# Find package data
|
|
92
|
# Find package data
|
|
95
|
#---------------------------------------------------------------------------
|
|
93
|
#---------------------------------------------------------------------------
|
|
96
|
|
|
94
|
|
|
97
|
def find_package_data():
|
|
95
|
def find_package_data():
|
|
98
|
"""
|
|
96
|
"""
|
|
99
|
Find IPython's package_data.
|
|
97
|
Find IPython's package_data.
|
|
100
|
"""
|
|
98
|
"""
|
|
101
|
# 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.
|
|
102
|
# 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
|
|
103
|
|
|
101
|
|
|
104
|
package_data = {
|
|
102
|
package_data = {
|
|
105
|
'IPython.core' : ['profile/README*'],
|
|
103
|
'IPython.core' : ['profile/README*'],
|
|
106
|
'IPython.core.tests' : ['*.png', '*.jpg', 'daft_extension/*.py'],
|
|
104
|
'IPython.core.tests' : ['*.png', '*.jpg', 'daft_extension/*.py'],
|
|
107
|
'IPython.lib.tests' : ['*.wav'],
|
|
105
|
'IPython.lib.tests' : ['*.wav'],
|
|
108
|
'IPython.testing.plugin' : ['*.txt'],
|
|
106
|
'IPython.testing.plugin' : ['*.txt'],
|
|
109
|
}
|
|
107
|
}
|
|
110
|
|
|
108
|
|
|
111
|
return package_data
|
|
109
|
return package_data
|
|
112
|
|
|
110
|
|
|
113
|
|
|
111
|
|
|
114
|
def check_package_data(package_data):
|
|
112
|
def check_package_data(package_data):
|
|
115
|
"""verify that package_data globs make sense"""
|
|
113
|
"""verify that package_data globs make sense"""
|
|
116
|
print("checking package data")
|
|
114
|
print("checking package data")
|
|
117
|
for pkg, data in package_data.items():
|
|
115
|
for pkg, data in package_data.items():
|
|
118
|
pkg_root = pjoin(*pkg.split('.'))
|
|
116
|
pkg_root = pjoin(*pkg.split('.'))
|
|
119
|
for d in data:
|
|
117
|
for d in data:
|
|
120
|
path = pjoin(pkg_root, d)
|
|
118
|
path = pjoin(pkg_root, d)
|
|
121
|
if '*' in path:
|
|
119
|
if '*' in path:
|
|
122
|
assert len(glob(path)) > 0, "No files match pattern %s" % path
|
|
120
|
assert len(glob(path)) > 0, "No files match pattern %s" % path
|
|
123
|
else:
|
|
121
|
else:
|
|
124
|
assert os.path.exists(path), "Missing package data: %s" % path
|
|
122
|
assert os.path.exists(path), "Missing package data: %s" % path
|
|
125
|
|
|
123
|
|
|
126
|
|
|
124
|
|
|
127
|
def check_package_data_first(command):
|
|
125
|
def check_package_data_first(command):
|
|
128
|
"""decorator for checking package_data before running a given command
|
|
126
|
"""decorator for checking package_data before running a given command
|
|
129
|
|
|
127
|
|
|
130
|
Probably only needs to wrap build_py
|
|
128
|
Probably only needs to wrap build_py
|
|
131
|
"""
|
|
129
|
"""
|
|
132
|
class DecoratedCommand(command):
|
|
130
|
class DecoratedCommand(command):
|
|
133
|
def run(self):
|
|
131
|
def run(self):
|
|
134
|
check_package_data(self.package_data)
|
|
132
|
check_package_data(self.package_data)
|
|
135
|
command.run(self)
|
|
133
|
command.run(self)
|
|
136
|
return DecoratedCommand
|
|
134
|
return DecoratedCommand
|
|
137
|
|
|
135
|
|
|
138
|
|
|
136
|
|
|
139
|
#---------------------------------------------------------------------------
|
|
137
|
#---------------------------------------------------------------------------
|
|
140
|
# Find data files
|
|
138
|
# Find data files
|
|
141
|
#---------------------------------------------------------------------------
|
|
139
|
#---------------------------------------------------------------------------
|
|
142
|
|
|
140
|
|
|
143
|
def find_data_files():
|
|
141
|
def find_data_files():
|
|
144
|
"""
|
|
142
|
"""
|
|
145
|
Find IPython's data_files.
|
|
143
|
Find IPython's data_files.
|
|
146
|
|
|
144
|
|
|
147
|
Just man pages at this point.
|
|
145
|
Just man pages at this point.
|
|
148
|
"""
|
|
146
|
"""
|
|
149
|
|
|
147
|
|
|
150
|
if "freebsd" in sys.platform:
|
|
148
|
if "freebsd" in sys.platform:
|
|
151
|
manpagebase = pjoin('man', 'man1')
|
|
149
|
manpagebase = pjoin('man', 'man1')
|
|
152
|
else:
|
|
150
|
else:
|
|
153
|
manpagebase = pjoin('share', 'man', 'man1')
|
|
151
|
manpagebase = pjoin('share', 'man', 'man1')
|
|
154
|
|
|
152
|
|
|
155
|
# Simple file lists can be made by hand
|
|
153
|
# Simple file lists can be made by hand
|
|
156
|
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)]
|
|
157
|
if not manpages:
|
|
155
|
if not manpages:
|
|
158
|
# When running from a source tree, the manpages aren't gzipped
|
|
156
|
# When running from a source tree, the manpages aren't gzipped
|
|
159
|
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)]
|
|
160
|
|
|
158
|
|
|
161
|
# And assemble the entire output list
|
|
159
|
# And assemble the entire output list
|
|
162
|
data_files = [ (manpagebase, manpages) ]
|
|
160
|
data_files = [ (manpagebase, manpages) ]
|
|
163
|
|
|
161
|
|
|
164
|
return data_files
|
|
162
|
return data_files
|
|
165
|
|
|
163
|
|
|
166
|
|
|
164
|
|
|
167
|
# 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
|
|
168
|
# to import IPython during setup, which fails on Python 3.
|
|
166
|
# to import IPython during setup, which fails on Python 3.
|
|
169
|
|
|
167
|
|
|
170
|
def target_outdated(target,deps):
|
|
168
|
def target_outdated(target,deps):
|
|
171
|
"""Determine whether a target is out of date.
|
|
169
|
"""Determine whether a target is out of date.
|
|
172
|
|
|
170
|
|
|
173
|
target_outdated(target,deps) -> 1/0
|
|
171
|
target_outdated(target,deps) -> 1/0
|
|
174
|
|
|
172
|
|
|
175
|
deps: list of filenames which MUST exist.
|
|
173
|
deps: list of filenames which MUST exist.
|
|
176
|
target: single filename which may or may not exist.
|
|
174
|
target: single filename which may or may not exist.
|
|
177
|
|
|
175
|
|
|
178
|
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
|
|
179
|
true, otherwise return false.
|
|
177
|
true, otherwise return false.
|
|
180
|
"""
|
|
178
|
"""
|
|
181
|
try:
|
|
179
|
try:
|
|
182
|
target_time = os.path.getmtime(target)
|
|
180
|
target_time = os.path.getmtime(target)
|
|
183
|
except os.error:
|
|
181
|
except os.error:
|
|
184
|
return 1
|
|
182
|
return 1
|
|
185
|
for dep in deps:
|
|
183
|
for dep in deps:
|
|
186
|
dep_time = os.path.getmtime(dep)
|
|
184
|
dep_time = os.path.getmtime(dep)
|
|
187
|
if dep_time > target_time:
|
|
185
|
if dep_time > target_time:
|
|
188
|
#print "For target",target,"Dep failed:",dep # dbg
|
|
186
|
#print "For target",target,"Dep failed:",dep # dbg
|
|
189
|
#print "times (dep,tar):",dep_time,target_time # dbg
|
|
187
|
#print "times (dep,tar):",dep_time,target_time # dbg
|
|
190
|
return 1
|
|
188
|
return 1
|
|
191
|
return 0
|
|
189
|
return 0
|
|
192
|
|
|
190
|
|
|
193
|
|
|
191
|
|
|
194
|
def target_update(target,deps,cmd):
|
|
192
|
def target_update(target,deps,cmd):
|
|
195
|
"""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.
|
|
196
|
|
|
194
|
|
|
197
|
target_update(target,deps,cmd) -> runs cmd if target is outdated.
|
|
195
|
target_update(target,deps,cmd) -> runs cmd if target is outdated.
|
|
198
|
|
|
196
|
|
|
199
|
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
|
|
200
|
command if target is outdated."""
|
|
198
|
command if target is outdated."""
|
|
201
|
|
|
199
|
|
|
202
|
if target_outdated(target,deps):
|
|
200
|
if target_outdated(target,deps):
|
|
203
|
os.system(cmd)
|
|
201
|
os.system(cmd)
|
|
204
|
|
|
202
|
|
|
205
|
#---------------------------------------------------------------------------
|
|
203
|
#---------------------------------------------------------------------------
|
|
206
|
# Find scripts
|
|
204
|
# Find scripts
|
|
207
|
#---------------------------------------------------------------------------
|
|
205
|
#---------------------------------------------------------------------------
|
|
208
|
|
|
206
|
|
|
209
|
def find_entry_points():
|
|
207
|
def find_entry_points():
|
|
210
|
"""Defines the command line entry points for IPython
|
|
208
|
"""Defines the command line entry points for IPython
|
|
211
|
|
|
209
|
|
|
212
|
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
|
|
213
|
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
|
|
214
|
command line scripts.
|
|
212
|
command line scripts.
|
|
215
|
|
|
213
|
|
|
216
|
Each of our entry points gets both a plain name, e.g. ipython, and one
|
|
214
|
Each of our entry points gets both a plain name, e.g. ipython, and one
|
|
217
|
suffixed with the Python major version number, e.g. ipython3.
|
|
215
|
suffixed with the Python major version number, e.g. ipython3.
|
|
218
|
"""
|
|
216
|
"""
|
|
219
|
ep = [
|
|
217
|
ep = [
|
|
220
|
'ipython%s = IPython:start_ipython',
|
|
218
|
'ipython%s = IPython:start_ipython',
|
|
221
|
]
|
|
219
|
]
|
|
222
|
suffix = str(sys.version_info[0])
|
|
220
|
suffix = str(sys.version_info[0])
|
|
223
|
return [e % '' for e in ep] + [e % suffix for e in ep]
|
|
221
|
return [e % '' for e in ep] + [e % suffix for e in ep]
|
|
224
|
|
|
222
|
|
|
225
|
class install_lib_symlink(Command):
|
|
223
|
class install_lib_symlink(Command):
|
|
226
|
user_options = [
|
|
224
|
user_options = [
|
|
227
|
('install-dir=', 'd', "directory to install to"),
|
|
225
|
('install-dir=', 'd', "directory to install to"),
|
|
228
|
]
|
|
226
|
]
|
|
229
|
|
|
227
|
|
|
230
|
def initialize_options(self):
|
|
228
|
def initialize_options(self):
|
|
231
|
self.install_dir = None
|
|
229
|
self.install_dir = None
|
|
232
|
|
|
230
|
|
|
233
|
def finalize_options(self):
|
|
231
|
def finalize_options(self):
|
|
234
|
self.set_undefined_options('symlink',
|
|
232
|
self.set_undefined_options('symlink',
|
|
235
|
('install_lib', 'install_dir'),
|
|
233
|
('install_lib', 'install_dir'),
|
|
236
|
)
|
|
234
|
)
|
|
237
|
|
|
235
|
|
|
238
|
def run(self):
|
|
236
|
def run(self):
|
|
239
|
if sys.platform == 'win32':
|
|
237
|
if sys.platform == 'win32':
|
|
240
|
raise Exception("This doesn't work on Windows.")
|
|
238
|
raise Exception("This doesn't work on Windows.")
|
|
241
|
pkg = os.path.join(os.getcwd(), 'IPython')
|
|
239
|
pkg = os.path.join(os.getcwd(), 'IPython')
|
|
242
|
dest = os.path.join(self.install_dir, 'IPython')
|
|
240
|
dest = os.path.join(self.install_dir, 'IPython')
|
|
243
|
if os.path.islink(dest):
|
|
241
|
if os.path.islink(dest):
|
|
244
|
print('removing existing symlink at %s' % dest)
|
|
242
|
print('removing existing symlink at %s' % dest)
|
|
245
|
os.unlink(dest)
|
|
243
|
os.unlink(dest)
|
|
246
|
print('symlinking %s -> %s' % (pkg, dest))
|
|
244
|
print('symlinking %s -> %s' % (pkg, dest))
|
|
247
|
os.symlink(pkg, dest)
|
|
245
|
os.symlink(pkg, dest)
|
|
248
|
|
|
246
|
|
|
249
|
class unsymlink(install):
|
|
247
|
class unsymlink(install):
|
|
250
|
def run(self):
|
|
248
|
def run(self):
|
|
251
|
dest = os.path.join(self.install_lib, 'IPython')
|
|
249
|
dest = os.path.join(self.install_lib, 'IPython')
|
|
252
|
if os.path.islink(dest):
|
|
250
|
if os.path.islink(dest):
|
|
253
|
print('removing symlink at %s' % dest)
|
|
251
|
print('removing symlink at %s' % dest)
|
|
254
|
os.unlink(dest)
|
|
252
|
os.unlink(dest)
|
|
255
|
else:
|
|
253
|
else:
|
|
256
|
print('No symlink exists at %s' % dest)
|
|
254
|
print('No symlink exists at %s' % dest)
|
|
257
|
|
|
255
|
|
|
258
|
class install_symlinked(install):
|
|
256
|
class install_symlinked(install):
|
|
259
|
def run(self):
|
|
257
|
def run(self):
|
|
260
|
if sys.platform == 'win32':
|
|
258
|
if sys.platform == 'win32':
|
|
261
|
raise Exception("This doesn't work on Windows.")
|
|
259
|
raise Exception("This doesn't work on Windows.")
|
|
262
|
|
|
260
|
|
|
263
|
# Run all sub-commands (at least those that need to be run)
|
|
261
|
# Run all sub-commands (at least those that need to be run)
|
|
264
|
for cmd_name in self.get_sub_commands():
|
|
262
|
for cmd_name in self.get_sub_commands():
|
|
265
|
self.run_command(cmd_name)
|
|
263
|
self.run_command(cmd_name)
|
|
266
|
|
|
264
|
|
|
267
|
# 'sub_commands': a list of commands this command might have to run to
|
|
265
|
# 'sub_commands': a list of commands this command might have to run to
|
|
268
|
# get its work done. See cmd.py for more info.
|
|
266
|
# get its work done. See cmd.py for more info.
|
|
269
|
sub_commands = [('install_lib_symlink', lambda self:True),
|
|
267
|
sub_commands = [('install_lib_symlink', lambda self:True),
|
|
270
|
('install_scripts_sym', lambda self:True),
|
|
268
|
('install_scripts_sym', lambda self:True),
|
|
271
|
]
|
|
269
|
]
|
|
272
|
|
|
270
|
|
|
273
|
class install_scripts_for_symlink(install_scripts):
|
|
271
|
class install_scripts_for_symlink(install_scripts):
|
|
274
|
"""Redefined to get options from 'symlink' instead of 'install'.
|
|
272
|
"""Redefined to get options from 'symlink' instead of 'install'.
|
|
275
|
|
|
273
|
|
|
276
|
I love distutils almost as much as I love setuptools.
|
|
274
|
I love distutils almost as much as I love setuptools.
|
|
277
|
"""
|
|
275
|
"""
|
|
278
|
def finalize_options(self):
|
|
276
|
def finalize_options(self):
|
|
279
|
self.set_undefined_options('build', ('build_scripts', 'build_dir'))
|
|
277
|
self.set_undefined_options('build', ('build_scripts', 'build_dir'))
|
|
280
|
self.set_undefined_options('symlink',
|
|
278
|
self.set_undefined_options('symlink',
|
|
281
|
('install_scripts', 'install_dir'),
|
|
279
|
('install_scripts', 'install_dir'),
|
|
282
|
('force', 'force'),
|
|
280
|
('force', 'force'),
|
|
283
|
('skip_build', 'skip_build'),
|
|
281
|
('skip_build', 'skip_build'),
|
|
284
|
)
|
|
282
|
)
|
|
285
|
|
|
283
|
|
|
286
|
|
|
284
|
|
|
287
|
#---------------------------------------------------------------------------
|
|
285
|
#---------------------------------------------------------------------------
|
|
288
|
# VCS related
|
|
286
|
# VCS related
|
|
289
|
#---------------------------------------------------------------------------
|
|
287
|
#---------------------------------------------------------------------------
|
|
290
|
|
|
288
|
|
|
291
|
|
|
289
|
|
|
292
|
def git_prebuild(pkg_dir, build_cmd=build_py):
|
|
290
|
def git_prebuild(pkg_dir, build_cmd=build_py):
|
|
293
|
"""Return extended build or sdist command class for recording commit
|
|
291
|
"""Return extended build or sdist command class for recording commit
|
|
294
|
|
|
292
|
|
|
295
|
records git commit in IPython.utils._sysinfo.commit
|
|
293
|
records git commit in IPython.utils._sysinfo.commit
|
|
296
|
|
|
294
|
|
|
297
|
for use in IPython.utils.sysinfo.sys_info() calls after installation.
|
|
295
|
for use in IPython.utils.sysinfo.sys_info() calls after installation.
|
|
298
|
"""
|
|
296
|
"""
|
|
299
|
|
|
297
|
|
|
300
|
class MyBuildPy(build_cmd):
|
|
298
|
class MyBuildPy(build_cmd):
|
|
301
|
''' Subclass to write commit data into installation tree '''
|
|
299
|
''' Subclass to write commit data into installation tree '''
|
|
302
|
def run(self):
|
|
300
|
def run(self):
|
|
303
|
# loose as `.dev` is suppose to be invalid
|
|
301
|
# loose as `.dev` is suppose to be invalid
|
|
304
|
print("check version number")
|
|
302
|
print("check version number")
|
|
305
|
loose_pep440re = re.compile(r'^(\d+)\.(\d+)\.(\d+((a|b|rc)\d+)?)(\.post\d+)?(\.dev\d*)?$')
|
|
303
|
loose_pep440re = re.compile(r'^(\d+)\.(\d+)\.(\d+((a|b|rc)\d+)?)(\.post\d+)?(\.dev\d*)?$')
|
|
306
|
if not loose_pep440re.match(version):
|
|
304
|
if not loose_pep440re.match(version):
|
|
307
|
raise ValueError("Version number '%s' is not valid (should match [N!]N(.N)*[{a|b|rc}N][.postN][.devN])" % version)
|
|
305
|
raise ValueError("Version number '%s' is not valid (should match [N!]N(.N)*[{a|b|rc}N][.postN][.devN])" % version)
|
|
308
|
|
|
306
|
|
|
309
|
|
|
307
|
|
|
310
|
build_cmd.run(self)
|
|
308
|
build_cmd.run(self)
|
|
311
|
# this one will only fire for build commands
|
|
309
|
# this one will only fire for build commands
|
|
312
|
if hasattr(self, 'build_lib'):
|
|
310
|
if hasattr(self, 'build_lib'):
|
|
313
|
self._record_commit(self.build_lib)
|
|
311
|
self._record_commit(self.build_lib)
|
|
314
|
|
|
312
|
|
|
315
|
def make_release_tree(self, base_dir, files):
|
|
313
|
def make_release_tree(self, base_dir, files):
|
|
316
|
# this one will fire for sdist
|
|
314
|
# this one will fire for sdist
|
|
317
|
build_cmd.make_release_tree(self, base_dir, files)
|
|
315
|
build_cmd.make_release_tree(self, base_dir, files)
|
|
318
|
self._record_commit(base_dir)
|
|
316
|
self._record_commit(base_dir)
|
|
319
|
|
|
317
|
|
|
320
|
def _record_commit(self, base_dir):
|
|
318
|
def _record_commit(self, base_dir):
|
|
321
|
import subprocess
|
|
319
|
import subprocess
|
|
322
|
proc = subprocess.Popen('git rev-parse --short HEAD',
|
|
320
|
proc = subprocess.Popen('git rev-parse --short HEAD',
|
|
323
|
stdout=subprocess.PIPE,
|
|
321
|
stdout=subprocess.PIPE,
|
|
324
|
stderr=subprocess.PIPE,
|
|
322
|
stderr=subprocess.PIPE,
|
|
325
|
shell=True)
|
|
323
|
shell=True)
|
|
326
|
repo_commit, _ = proc.communicate()
|
|
324
|
repo_commit, _ = proc.communicate()
|
|
327
|
repo_commit = repo_commit.strip().decode("ascii")
|
|
325
|
repo_commit = repo_commit.strip().decode("ascii")
|
|
328
|
|
|
326
|
|
|
329
|
out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
|
|
327
|
out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
|
|
330
|
if os.path.isfile(out_pth) and not repo_commit:
|
|
328
|
if os.path.isfile(out_pth) and not repo_commit:
|
|
331
|
# nothing to write, don't clobber
|
|
329
|
# nothing to write, don't clobber
|
|
332
|
return
|
|
330
|
return
|
|
333
|
|
|
331
|
|
|
334
|
print("writing git commit '%s' to %s" % (repo_commit, out_pth))
|
|
332
|
print("writing git commit '%s' to %s" % (repo_commit, out_pth))
|
|
335
|
|
|
333
|
|
|
336
|
# remove to avoid overwriting original via hard link
|
|
334
|
# remove to avoid overwriting original via hard link
|
|
337
|
try:
|
|
335
|
try:
|
|
338
|
os.remove(out_pth)
|
|
336
|
os.remove(out_pth)
|
|
339
|
except (IOError, OSError):
|
|
337
|
except (IOError, OSError):
|
|
340
|
pass
|
|
338
|
pass
|
|
341
|
with open(out_pth, 'w') as out_file:
|
|
339
|
with open(out_pth, 'w') as out_file:
|
|
342
|
out_file.writelines([
|
|
340
|
out_file.writelines([
|
|
343
|
'# GENERATED BY setup.py\n',
|
|
341
|
'# GENERATED BY setup.py\n',
|
|
344
|
'commit = u"%s"\n' % repo_commit,
|
|
342
|
'commit = u"%s"\n' % repo_commit,
|
|
345
|
])
|
|
343
|
])
|
|
346
|
return MyBuildPy
|
|
344
|
return MyBuildPy
|
|
347
|
|
|
345
|
|