##// END OF EJS Templates
hghave: make bzr checks stricter...
timeless -
r29866:a5ce381a default
parent child Browse files
Show More
@@ -1,579 +1,583 b''
1 from __future__ import absolute_import
1 from __future__ import absolute_import
2
2
3 import errno
3 import errno
4 import os
4 import os
5 import re
5 import re
6 import socket
6 import socket
7 import stat
7 import stat
8 import subprocess
8 import subprocess
9 import sys
9 import sys
10 import tempfile
10 import tempfile
11
11
12 tempprefix = 'hg-hghave-'
12 tempprefix = 'hg-hghave-'
13
13
14 checks = {
14 checks = {
15 "true": (lambda: True, "yak shaving"),
15 "true": (lambda: True, "yak shaving"),
16 "false": (lambda: False, "nail clipper"),
16 "false": (lambda: False, "nail clipper"),
17 }
17 }
18
18
19 def check(name, desc):
19 def check(name, desc):
20 """Registers a check function for a feature."""
20 """Registers a check function for a feature."""
21 def decorator(func):
21 def decorator(func):
22 checks[name] = (func, desc)
22 checks[name] = (func, desc)
23 return func
23 return func
24 return decorator
24 return decorator
25
25
26 def checkvers(name, desc, vers):
26 def checkvers(name, desc, vers):
27 """Registers a check function for each of a series of versions.
27 """Registers a check function for each of a series of versions.
28
28
29 vers can be a list or an iterator"""
29 vers can be a list or an iterator"""
30 def decorator(func):
30 def decorator(func):
31 def funcv(v):
31 def funcv(v):
32 def f():
32 def f():
33 return func(v)
33 return func(v)
34 return f
34 return f
35 for v in vers:
35 for v in vers:
36 v = str(v)
36 v = str(v)
37 f = funcv(v)
37 f = funcv(v)
38 checks['%s%s' % (name, v.replace('.', ''))] = (f, desc % v)
38 checks['%s%s' % (name, v.replace('.', ''))] = (f, desc % v)
39 return func
39 return func
40 return decorator
40 return decorator
41
41
42 def checkfeatures(features):
42 def checkfeatures(features):
43 result = {
43 result = {
44 'error': [],
44 'error': [],
45 'missing': [],
45 'missing': [],
46 'skipped': [],
46 'skipped': [],
47 }
47 }
48
48
49 for feature in features:
49 for feature in features:
50 negate = feature.startswith('no-')
50 negate = feature.startswith('no-')
51 if negate:
51 if negate:
52 feature = feature[3:]
52 feature = feature[3:]
53
53
54 if feature not in checks:
54 if feature not in checks:
55 result['missing'].append(feature)
55 result['missing'].append(feature)
56 continue
56 continue
57
57
58 check, desc = checks[feature]
58 check, desc = checks[feature]
59 try:
59 try:
60 available = check()
60 available = check()
61 except Exception:
61 except Exception:
62 result['error'].append('hghave check failed: %s' % feature)
62 result['error'].append('hghave check failed: %s' % feature)
63 continue
63 continue
64
64
65 if not negate and not available:
65 if not negate and not available:
66 result['skipped'].append('missing feature: %s' % desc)
66 result['skipped'].append('missing feature: %s' % desc)
67 elif negate and available:
67 elif negate and available:
68 result['skipped'].append('system supports %s' % desc)
68 result['skipped'].append('system supports %s' % desc)
69
69
70 return result
70 return result
71
71
72 def require(features):
72 def require(features):
73 """Require that features are available, exiting if not."""
73 """Require that features are available, exiting if not."""
74 result = checkfeatures(features)
74 result = checkfeatures(features)
75
75
76 for missing in result['missing']:
76 for missing in result['missing']:
77 sys.stderr.write('skipped: unknown feature: %s\n' % missing)
77 sys.stderr.write('skipped: unknown feature: %s\n' % missing)
78 for msg in result['skipped']:
78 for msg in result['skipped']:
79 sys.stderr.write('skipped: %s\n' % msg)
79 sys.stderr.write('skipped: %s\n' % msg)
80 for msg in result['error']:
80 for msg in result['error']:
81 sys.stderr.write('%s\n' % msg)
81 sys.stderr.write('%s\n' % msg)
82
82
83 if result['missing']:
83 if result['missing']:
84 sys.exit(2)
84 sys.exit(2)
85
85
86 if result['skipped'] or result['error']:
86 if result['skipped'] or result['error']:
87 sys.exit(1)
87 sys.exit(1)
88
88
89 def matchoutput(cmd, regexp, ignorestatus=False):
89 def matchoutput(cmd, regexp, ignorestatus=False):
90 """Return the match object if cmd executes successfully and its output
90 """Return the match object if cmd executes successfully and its output
91 is matched by the supplied regular expression.
91 is matched by the supplied regular expression.
92 """
92 """
93 r = re.compile(regexp)
93 r = re.compile(regexp)
94 try:
94 try:
95 p = subprocess.Popen(
95 p = subprocess.Popen(
96 cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
96 cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
97 except OSError as e:
97 except OSError as e:
98 if e.errno != errno.ENOENT:
98 if e.errno != errno.ENOENT:
99 raise
99 raise
100 ret = -1
100 ret = -1
101 ret = p.wait()
101 ret = p.wait()
102 s = p.stdout.read()
102 s = p.stdout.read()
103 return (ignorestatus or not ret) and r.search(s)
103 return (ignorestatus or not ret) and r.search(s)
104
104
105 @check("baz", "GNU Arch baz client")
105 @check("baz", "GNU Arch baz client")
106 def has_baz():
106 def has_baz():
107 return matchoutput('baz --version 2>&1', br'baz Bazaar version')
107 return matchoutput('baz --version 2>&1', br'baz Bazaar version')
108
108
109 @check("bzr", "Canonical's Bazaar client")
109 @check("bzr", "Canonical's Bazaar client")
110 def has_bzr():
110 def has_bzr():
111 try:
111 try:
112 import bzrlib
112 import bzrlib
113 import bzrlib.bzrdir
114 import bzrlib.errors
115 import bzrlib.revision
116 import bzrlib.revisionspec.RevisionSpec
113 return bzrlib.__doc__ is not None
117 return bzrlib.__doc__ is not None
114 except ImportError:
118 except ImportError:
115 return False
119 return False
116
120
117 @checkvers("bzr", "Canonical's Bazaar client >= %s", (1.14,))
121 @checkvers("bzr", "Canonical's Bazaar client >= %s", (1.14,))
118 def has_bzr_range(v):
122 def has_bzr_range(v):
119 major, minor = v.split('.')[0:2]
123 major, minor = v.split('.')[0:2]
120 try:
124 try:
121 import bzrlib
125 import bzrlib
122 return (bzrlib.__doc__ is not None
126 return (bzrlib.__doc__ is not None
123 and bzrlib.version_info[:2] >= (int(major), int(minor)))
127 and bzrlib.version_info[:2] >= (int(major), int(minor)))
124 except ImportError:
128 except ImportError:
125 return False
129 return False
126
130
127 @check("chg", "running with chg")
131 @check("chg", "running with chg")
128 def has_chg():
132 def has_chg():
129 return 'CHGHG' in os.environ
133 return 'CHGHG' in os.environ
130
134
131 @check("cvs", "cvs client/server")
135 @check("cvs", "cvs client/server")
132 def has_cvs():
136 def has_cvs():
133 re = br'Concurrent Versions System.*?server'
137 re = br'Concurrent Versions System.*?server'
134 return matchoutput('cvs --version 2>&1', re) and not has_msys()
138 return matchoutput('cvs --version 2>&1', re) and not has_msys()
135
139
136 @check("cvs112", "cvs client/server 1.12.* (not cvsnt)")
140 @check("cvs112", "cvs client/server 1.12.* (not cvsnt)")
137 def has_cvs112():
141 def has_cvs112():
138 re = br'Concurrent Versions System \(CVS\) 1.12.*?server'
142 re = br'Concurrent Versions System \(CVS\) 1.12.*?server'
139 return matchoutput('cvs --version 2>&1', re) and not has_msys()
143 return matchoutput('cvs --version 2>&1', re) and not has_msys()
140
144
141 @check("cvsnt", "cvsnt client/server")
145 @check("cvsnt", "cvsnt client/server")
142 def has_cvsnt():
146 def has_cvsnt():
143 re = br'Concurrent Versions System \(CVSNT\) (\d+).(\d+).*\(client/server\)'
147 re = br'Concurrent Versions System \(CVSNT\) (\d+).(\d+).*\(client/server\)'
144 return matchoutput('cvsnt --version 2>&1', re)
148 return matchoutput('cvsnt --version 2>&1', re)
145
149
146 @check("darcs", "darcs client")
150 @check("darcs", "darcs client")
147 def has_darcs():
151 def has_darcs():
148 return matchoutput('darcs --version', br'2\.[2-9]', True)
152 return matchoutput('darcs --version', br'2\.[2-9]', True)
149
153
150 @check("mtn", "monotone client (>= 1.0)")
154 @check("mtn", "monotone client (>= 1.0)")
151 def has_mtn():
155 def has_mtn():
152 return matchoutput('mtn --version', br'monotone', True) and not matchoutput(
156 return matchoutput('mtn --version', br'monotone', True) and not matchoutput(
153 'mtn --version', br'monotone 0\.', True)
157 'mtn --version', br'monotone 0\.', True)
154
158
155 @check("eol-in-paths", "end-of-lines in paths")
159 @check("eol-in-paths", "end-of-lines in paths")
156 def has_eol_in_paths():
160 def has_eol_in_paths():
157 try:
161 try:
158 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r')
162 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r')
159 os.close(fd)
163 os.close(fd)
160 os.remove(path)
164 os.remove(path)
161 return True
165 return True
162 except (IOError, OSError):
166 except (IOError, OSError):
163 return False
167 return False
164
168
165 @check("execbit", "executable bit")
169 @check("execbit", "executable bit")
166 def has_executablebit():
170 def has_executablebit():
167 try:
171 try:
168 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
172 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
169 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
173 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
170 try:
174 try:
171 os.close(fh)
175 os.close(fh)
172 m = os.stat(fn).st_mode & 0o777
176 m = os.stat(fn).st_mode & 0o777
173 new_file_has_exec = m & EXECFLAGS
177 new_file_has_exec = m & EXECFLAGS
174 os.chmod(fn, m ^ EXECFLAGS)
178 os.chmod(fn, m ^ EXECFLAGS)
175 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0o777) == m)
179 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0o777) == m)
176 finally:
180 finally:
177 os.unlink(fn)
181 os.unlink(fn)
178 except (IOError, OSError):
182 except (IOError, OSError):
179 # we don't care, the user probably won't be able to commit anyway
183 # we don't care, the user probably won't be able to commit anyway
180 return False
184 return False
181 return not (new_file_has_exec or exec_flags_cannot_flip)
185 return not (new_file_has_exec or exec_flags_cannot_flip)
182
186
183 @check("icasefs", "case insensitive file system")
187 @check("icasefs", "case insensitive file system")
184 def has_icasefs():
188 def has_icasefs():
185 # Stolen from mercurial.util
189 # Stolen from mercurial.util
186 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
190 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
187 os.close(fd)
191 os.close(fd)
188 try:
192 try:
189 s1 = os.stat(path)
193 s1 = os.stat(path)
190 d, b = os.path.split(path)
194 d, b = os.path.split(path)
191 p2 = os.path.join(d, b.upper())
195 p2 = os.path.join(d, b.upper())
192 if path == p2:
196 if path == p2:
193 p2 = os.path.join(d, b.lower())
197 p2 = os.path.join(d, b.lower())
194 try:
198 try:
195 s2 = os.stat(p2)
199 s2 = os.stat(p2)
196 return s2 == s1
200 return s2 == s1
197 except OSError:
201 except OSError:
198 return False
202 return False
199 finally:
203 finally:
200 os.remove(path)
204 os.remove(path)
201
205
202 @check("fifo", "named pipes")
206 @check("fifo", "named pipes")
203 def has_fifo():
207 def has_fifo():
204 if getattr(os, "mkfifo", None) is None:
208 if getattr(os, "mkfifo", None) is None:
205 return False
209 return False
206 name = tempfile.mktemp(dir='.', prefix=tempprefix)
210 name = tempfile.mktemp(dir='.', prefix=tempprefix)
207 try:
211 try:
208 os.mkfifo(name)
212 os.mkfifo(name)
209 os.unlink(name)
213 os.unlink(name)
210 return True
214 return True
211 except OSError:
215 except OSError:
212 return False
216 return False
213
217
214 @check("killdaemons", 'killdaemons.py support')
218 @check("killdaemons", 'killdaemons.py support')
215 def has_killdaemons():
219 def has_killdaemons():
216 return True
220 return True
217
221
218 @check("cacheable", "cacheable filesystem")
222 @check("cacheable", "cacheable filesystem")
219 def has_cacheable_fs():
223 def has_cacheable_fs():
220 from mercurial import util
224 from mercurial import util
221
225
222 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
226 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
223 os.close(fd)
227 os.close(fd)
224 try:
228 try:
225 return util.cachestat(path).cacheable()
229 return util.cachestat(path).cacheable()
226 finally:
230 finally:
227 os.remove(path)
231 os.remove(path)
228
232
229 @check("lsprof", "python lsprof module")
233 @check("lsprof", "python lsprof module")
230 def has_lsprof():
234 def has_lsprof():
231 try:
235 try:
232 import _lsprof
236 import _lsprof
233 _lsprof.Profiler # silence unused import warning
237 _lsprof.Profiler # silence unused import warning
234 return True
238 return True
235 except ImportError:
239 except ImportError:
236 return False
240 return False
237
241
238 def gethgversion():
242 def gethgversion():
239 m = matchoutput('hg --version --quiet 2>&1', br'(\d+)\.(\d+)')
243 m = matchoutput('hg --version --quiet 2>&1', br'(\d+)\.(\d+)')
240 if not m:
244 if not m:
241 return (0, 0)
245 return (0, 0)
242 return (int(m.group(1)), int(m.group(2)))
246 return (int(m.group(1)), int(m.group(2)))
243
247
244 @checkvers("hg", "Mercurial >= %s",
248 @checkvers("hg", "Mercurial >= %s",
245 list([(1.0 * x) / 10 for x in range(9, 40)]))
249 list([(1.0 * x) / 10 for x in range(9, 40)]))
246 def has_hg_range(v):
250 def has_hg_range(v):
247 major, minor = v.split('.')[0:2]
251 major, minor = v.split('.')[0:2]
248 return gethgversion() >= (int(major), int(minor))
252 return gethgversion() >= (int(major), int(minor))
249
253
250 @check("hg08", "Mercurial >= 0.8")
254 @check("hg08", "Mercurial >= 0.8")
251 def has_hg08():
255 def has_hg08():
252 if checks["hg09"][0]():
256 if checks["hg09"][0]():
253 return True
257 return True
254 return matchoutput('hg help annotate 2>&1', '--date')
258 return matchoutput('hg help annotate 2>&1', '--date')
255
259
256 @check("hg07", "Mercurial >= 0.7")
260 @check("hg07", "Mercurial >= 0.7")
257 def has_hg07():
261 def has_hg07():
258 if checks["hg08"][0]():
262 if checks["hg08"][0]():
259 return True
263 return True
260 return matchoutput('hg --version --quiet 2>&1', 'Mercurial Distributed SCM')
264 return matchoutput('hg --version --quiet 2>&1', 'Mercurial Distributed SCM')
261
265
262 @check("hg06", "Mercurial >= 0.6")
266 @check("hg06", "Mercurial >= 0.6")
263 def has_hg06():
267 def has_hg06():
264 if checks["hg07"][0]():
268 if checks["hg07"][0]():
265 return True
269 return True
266 return matchoutput('hg --version --quiet 2>&1', 'Mercurial version')
270 return matchoutput('hg --version --quiet 2>&1', 'Mercurial version')
267
271
268 @check("gettext", "GNU Gettext (msgfmt)")
272 @check("gettext", "GNU Gettext (msgfmt)")
269 def has_gettext():
273 def has_gettext():
270 return matchoutput('msgfmt --version', br'GNU gettext-tools')
274 return matchoutput('msgfmt --version', br'GNU gettext-tools')
271
275
272 @check("git", "git command line client")
276 @check("git", "git command line client")
273 def has_git():
277 def has_git():
274 return matchoutput('git --version 2>&1', br'^git version')
278 return matchoutput('git --version 2>&1', br'^git version')
275
279
276 @check("docutils", "Docutils text processing library")
280 @check("docutils", "Docutils text processing library")
277 def has_docutils():
281 def has_docutils():
278 try:
282 try:
279 import docutils.core
283 import docutils.core
280 docutils.core.publish_cmdline # silence unused import
284 docutils.core.publish_cmdline # silence unused import
281 return True
285 return True
282 except ImportError:
286 except ImportError:
283 return False
287 return False
284
288
285 def getsvnversion():
289 def getsvnversion():
286 m = matchoutput('svn --version --quiet 2>&1', br'^(\d+)\.(\d+)')
290 m = matchoutput('svn --version --quiet 2>&1', br'^(\d+)\.(\d+)')
287 if not m:
291 if not m:
288 return (0, 0)
292 return (0, 0)
289 return (int(m.group(1)), int(m.group(2)))
293 return (int(m.group(1)), int(m.group(2)))
290
294
291 @checkvers("svn", "subversion client and admin tools >= %s", (1.3, 1.5))
295 @checkvers("svn", "subversion client and admin tools >= %s", (1.3, 1.5))
292 def has_svn_range(v):
296 def has_svn_range(v):
293 major, minor = v.split('.')[0:2]
297 major, minor = v.split('.')[0:2]
294 return getsvnversion() >= (int(major), int(minor))
298 return getsvnversion() >= (int(major), int(minor))
295
299
296 @check("svn", "subversion client and admin tools")
300 @check("svn", "subversion client and admin tools")
297 def has_svn():
301 def has_svn():
298 return matchoutput('svn --version 2>&1', br'^svn, version') and \
302 return matchoutput('svn --version 2>&1', br'^svn, version') and \
299 matchoutput('svnadmin --version 2>&1', br'^svnadmin, version')
303 matchoutput('svnadmin --version 2>&1', br'^svnadmin, version')
300
304
301 @check("svn-bindings", "subversion python bindings")
305 @check("svn-bindings", "subversion python bindings")
302 def has_svn_bindings():
306 def has_svn_bindings():
303 try:
307 try:
304 import svn.core
308 import svn.core
305 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
309 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
306 if version < (1, 4):
310 if version < (1, 4):
307 return False
311 return False
308 return True
312 return True
309 except ImportError:
313 except ImportError:
310 return False
314 return False
311
315
312 @check("p4", "Perforce server and client")
316 @check("p4", "Perforce server and client")
313 def has_p4():
317 def has_p4():
314 return (matchoutput('p4 -V', br'Rev\. P4/') and
318 return (matchoutput('p4 -V', br'Rev\. P4/') and
315 matchoutput('p4d -V', br'Rev\. P4D/'))
319 matchoutput('p4d -V', br'Rev\. P4D/'))
316
320
317 @check("symlink", "symbolic links")
321 @check("symlink", "symbolic links")
318 def has_symlink():
322 def has_symlink():
319 if getattr(os, "symlink", None) is None:
323 if getattr(os, "symlink", None) is None:
320 return False
324 return False
321 name = tempfile.mktemp(dir='.', prefix=tempprefix)
325 name = tempfile.mktemp(dir='.', prefix=tempprefix)
322 try:
326 try:
323 os.symlink(".", name)
327 os.symlink(".", name)
324 os.unlink(name)
328 os.unlink(name)
325 return True
329 return True
326 except (OSError, AttributeError):
330 except (OSError, AttributeError):
327 return False
331 return False
328
332
329 @check("hardlink", "hardlinks")
333 @check("hardlink", "hardlinks")
330 def has_hardlink():
334 def has_hardlink():
331 from mercurial import util
335 from mercurial import util
332 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
336 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
333 os.close(fh)
337 os.close(fh)
334 name = tempfile.mktemp(dir='.', prefix=tempprefix)
338 name = tempfile.mktemp(dir='.', prefix=tempprefix)
335 try:
339 try:
336 util.oslink(fn, name)
340 util.oslink(fn, name)
337 os.unlink(name)
341 os.unlink(name)
338 return True
342 return True
339 except OSError:
343 except OSError:
340 return False
344 return False
341 finally:
345 finally:
342 os.unlink(fn)
346 os.unlink(fn)
343
347
344 @check("tla", "GNU Arch tla client")
348 @check("tla", "GNU Arch tla client")
345 def has_tla():
349 def has_tla():
346 return matchoutput('tla --version 2>&1', br'The GNU Arch Revision')
350 return matchoutput('tla --version 2>&1', br'The GNU Arch Revision')
347
351
348 @check("gpg", "gpg client")
352 @check("gpg", "gpg client")
349 def has_gpg():
353 def has_gpg():
350 return matchoutput('gpg --version 2>&1', br'GnuPG')
354 return matchoutput('gpg --version 2>&1', br'GnuPG')
351
355
352 @check("gpg2", "gpg client v2")
356 @check("gpg2", "gpg client v2")
353 def has_gpg2():
357 def has_gpg2():
354 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.')
358 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.')
355
359
356 @check("unix-permissions", "unix-style permissions")
360 @check("unix-permissions", "unix-style permissions")
357 def has_unix_permissions():
361 def has_unix_permissions():
358 d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
362 d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
359 try:
363 try:
360 fname = os.path.join(d, 'foo')
364 fname = os.path.join(d, 'foo')
361 for umask in (0o77, 0o07, 0o22):
365 for umask in (0o77, 0o07, 0o22):
362 os.umask(umask)
366 os.umask(umask)
363 f = open(fname, 'w')
367 f = open(fname, 'w')
364 f.close()
368 f.close()
365 mode = os.stat(fname).st_mode
369 mode = os.stat(fname).st_mode
366 os.unlink(fname)
370 os.unlink(fname)
367 if mode & 0o777 != ~umask & 0o666:
371 if mode & 0o777 != ~umask & 0o666:
368 return False
372 return False
369 return True
373 return True
370 finally:
374 finally:
371 os.rmdir(d)
375 os.rmdir(d)
372
376
373 @check("unix-socket", "AF_UNIX socket family")
377 @check("unix-socket", "AF_UNIX socket family")
374 def has_unix_socket():
378 def has_unix_socket():
375 return getattr(socket, 'AF_UNIX', None) is not None
379 return getattr(socket, 'AF_UNIX', None) is not None
376
380
377 @check("root", "root permissions")
381 @check("root", "root permissions")
378 def has_root():
382 def has_root():
379 return getattr(os, 'geteuid', None) and os.geteuid() == 0
383 return getattr(os, 'geteuid', None) and os.geteuid() == 0
380
384
381 @check("pyflakes", "Pyflakes python linter")
385 @check("pyflakes", "Pyflakes python linter")
382 def has_pyflakes():
386 def has_pyflakes():
383 return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"",
387 return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"",
384 br"<stdin>:1: 're' imported but unused",
388 br"<stdin>:1: 're' imported but unused",
385 True)
389 True)
386
390
387 @check("pygments", "Pygments source highlighting library")
391 @check("pygments", "Pygments source highlighting library")
388 def has_pygments():
392 def has_pygments():
389 try:
393 try:
390 import pygments
394 import pygments
391 pygments.highlight # silence unused import warning
395 pygments.highlight # silence unused import warning
392 return True
396 return True
393 except ImportError:
397 except ImportError:
394 return False
398 return False
395
399
396 @check("outer-repo", "outer repo")
400 @check("outer-repo", "outer repo")
397 def has_outer_repo():
401 def has_outer_repo():
398 # failing for other reasons than 'no repo' imply that there is a repo
402 # failing for other reasons than 'no repo' imply that there is a repo
399 return not matchoutput('hg root 2>&1',
403 return not matchoutput('hg root 2>&1',
400 br'abort: no repository found', True)
404 br'abort: no repository found', True)
401
405
402 @check("ssl", "ssl module available")
406 @check("ssl", "ssl module available")
403 def has_ssl():
407 def has_ssl():
404 try:
408 try:
405 import ssl
409 import ssl
406 ssl.CERT_NONE
410 ssl.CERT_NONE
407 return True
411 return True
408 except ImportError:
412 except ImportError:
409 return False
413 return False
410
414
411 @check("sslcontext", "python >= 2.7.9 ssl")
415 @check("sslcontext", "python >= 2.7.9 ssl")
412 def has_sslcontext():
416 def has_sslcontext():
413 try:
417 try:
414 import ssl
418 import ssl
415 ssl.SSLContext
419 ssl.SSLContext
416 return True
420 return True
417 except (ImportError, AttributeError):
421 except (ImportError, AttributeError):
418 return False
422 return False
419
423
420 @check("defaultcacerts", "can verify SSL certs by system's CA certs store")
424 @check("defaultcacerts", "can verify SSL certs by system's CA certs store")
421 def has_defaultcacerts():
425 def has_defaultcacerts():
422 from mercurial import sslutil, ui as uimod
426 from mercurial import sslutil, ui as uimod
423 ui = uimod.ui()
427 ui = uimod.ui()
424 return sslutil._defaultcacerts(ui) or sslutil._canloaddefaultcerts
428 return sslutil._defaultcacerts(ui) or sslutil._canloaddefaultcerts
425
429
426 @check("defaultcacertsloaded", "detected presence of loaded system CA certs")
430 @check("defaultcacertsloaded", "detected presence of loaded system CA certs")
427 def has_defaultcacertsloaded():
431 def has_defaultcacertsloaded():
428 import ssl
432 import ssl
429 from mercurial import sslutil, ui as uimod
433 from mercurial import sslutil, ui as uimod
430
434
431 if not has_defaultcacerts():
435 if not has_defaultcacerts():
432 return False
436 return False
433 if not has_sslcontext():
437 if not has_sslcontext():
434 return False
438 return False
435
439
436 ui = uimod.ui()
440 ui = uimod.ui()
437 cafile = sslutil._defaultcacerts(ui)
441 cafile = sslutil._defaultcacerts(ui)
438 ctx = ssl.create_default_context()
442 ctx = ssl.create_default_context()
439 if cafile:
443 if cafile:
440 ctx.load_verify_locations(cafile=cafile)
444 ctx.load_verify_locations(cafile=cafile)
441 else:
445 else:
442 ctx.load_default_certs()
446 ctx.load_default_certs()
443
447
444 return len(ctx.get_ca_certs()) > 0
448 return len(ctx.get_ca_certs()) > 0
445
449
446 @check("tls1.2", "TLS 1.2 protocol support")
450 @check("tls1.2", "TLS 1.2 protocol support")
447 def has_tls1_2():
451 def has_tls1_2():
448 from mercurial import sslutil
452 from mercurial import sslutil
449 return 'tls1.2' in sslutil.supportedprotocols
453 return 'tls1.2' in sslutil.supportedprotocols
450
454
451 @check("windows", "Windows")
455 @check("windows", "Windows")
452 def has_windows():
456 def has_windows():
453 return os.name == 'nt'
457 return os.name == 'nt'
454
458
455 @check("system-sh", "system() uses sh")
459 @check("system-sh", "system() uses sh")
456 def has_system_sh():
460 def has_system_sh():
457 return os.name != 'nt'
461 return os.name != 'nt'
458
462
459 @check("serve", "platform and python can manage 'hg serve -d'")
463 @check("serve", "platform and python can manage 'hg serve -d'")
460 def has_serve():
464 def has_serve():
461 return os.name != 'nt' # gross approximation
465 return os.name != 'nt' # gross approximation
462
466
463 @check("test-repo", "running tests from repository")
467 @check("test-repo", "running tests from repository")
464 def has_test_repo():
468 def has_test_repo():
465 t = os.environ["TESTDIR"]
469 t = os.environ["TESTDIR"]
466 return os.path.isdir(os.path.join(t, "..", ".hg"))
470 return os.path.isdir(os.path.join(t, "..", ".hg"))
467
471
468 @check("tic", "terminfo compiler and curses module")
472 @check("tic", "terminfo compiler and curses module")
469 def has_tic():
473 def has_tic():
470 try:
474 try:
471 import curses
475 import curses
472 curses.COLOR_BLUE
476 curses.COLOR_BLUE
473 return matchoutput('test -x "`which tic`"', br'')
477 return matchoutput('test -x "`which tic`"', br'')
474 except ImportError:
478 except ImportError:
475 return False
479 return False
476
480
477 @check("msys", "Windows with MSYS")
481 @check("msys", "Windows with MSYS")
478 def has_msys():
482 def has_msys():
479 return os.getenv('MSYSTEM')
483 return os.getenv('MSYSTEM')
480
484
481 @check("aix", "AIX")
485 @check("aix", "AIX")
482 def has_aix():
486 def has_aix():
483 return sys.platform.startswith("aix")
487 return sys.platform.startswith("aix")
484
488
485 @check("osx", "OS X")
489 @check("osx", "OS X")
486 def has_osx():
490 def has_osx():
487 return sys.platform == 'darwin'
491 return sys.platform == 'darwin'
488
492
489 @check("osxpackaging", "OS X packaging tools")
493 @check("osxpackaging", "OS X packaging tools")
490 def has_osxpackaging():
494 def has_osxpackaging():
491 try:
495 try:
492 return (matchoutput('pkgbuild', br'Usage: pkgbuild ', ignorestatus=1)
496 return (matchoutput('pkgbuild', br'Usage: pkgbuild ', ignorestatus=1)
493 and matchoutput(
497 and matchoutput(
494 'productbuild', br'Usage: productbuild ',
498 'productbuild', br'Usage: productbuild ',
495 ignorestatus=1)
499 ignorestatus=1)
496 and matchoutput('lsbom', br'Usage: lsbom', ignorestatus=1)
500 and matchoutput('lsbom', br'Usage: lsbom', ignorestatus=1)
497 and matchoutput(
501 and matchoutput(
498 'xar --help', br'Usage: xar', ignorestatus=1))
502 'xar --help', br'Usage: xar', ignorestatus=1))
499 except ImportError:
503 except ImportError:
500 return False
504 return False
501
505
502 @check("docker", "docker support")
506 @check("docker", "docker support")
503 def has_docker():
507 def has_docker():
504 pat = br'A self-sufficient runtime for'
508 pat = br'A self-sufficient runtime for'
505 if matchoutput('docker --help', pat):
509 if matchoutput('docker --help', pat):
506 if 'linux' not in sys.platform:
510 if 'linux' not in sys.platform:
507 # TODO: in theory we should be able to test docker-based
511 # TODO: in theory we should be able to test docker-based
508 # package creation on non-linux using boot2docker, but in
512 # package creation on non-linux using boot2docker, but in
509 # practice that requires extra coordination to make sure
513 # practice that requires extra coordination to make sure
510 # $TESTTEMP is going to be visible at the same path to the
514 # $TESTTEMP is going to be visible at the same path to the
511 # boot2docker VM. If we figure out how to verify that, we
515 # boot2docker VM. If we figure out how to verify that, we
512 # can use the following instead of just saying False:
516 # can use the following instead of just saying False:
513 # return 'DOCKER_HOST' in os.environ
517 # return 'DOCKER_HOST' in os.environ
514 return False
518 return False
515
519
516 return True
520 return True
517 return False
521 return False
518
522
519 @check("debhelper", "debian packaging tools")
523 @check("debhelper", "debian packaging tools")
520 def has_debhelper():
524 def has_debhelper():
521 dpkg = matchoutput('dpkg --version',
525 dpkg = matchoutput('dpkg --version',
522 br"Debian `dpkg' package management program")
526 br"Debian `dpkg' package management program")
523 dh = matchoutput('dh --help',
527 dh = matchoutput('dh --help',
524 br'dh is a part of debhelper.', ignorestatus=True)
528 br'dh is a part of debhelper.', ignorestatus=True)
525 dh_py2 = matchoutput('dh_python2 --help',
529 dh_py2 = matchoutput('dh_python2 --help',
526 br'other supported Python versions')
530 br'other supported Python versions')
527 return dpkg and dh and dh_py2
531 return dpkg and dh and dh_py2
528
532
529 @check("absimport", "absolute_import in __future__")
533 @check("absimport", "absolute_import in __future__")
530 def has_absimport():
534 def has_absimport():
531 import __future__
535 import __future__
532 from mercurial import util
536 from mercurial import util
533 return util.safehasattr(__future__, "absolute_import")
537 return util.safehasattr(__future__, "absolute_import")
534
538
535 @check("py27+", "running with Python 2.7+")
539 @check("py27+", "running with Python 2.7+")
536 def has_python27ornewer():
540 def has_python27ornewer():
537 return sys.version_info[0:2] >= (2, 7)
541 return sys.version_info[0:2] >= (2, 7)
538
542
539 @check("py3k", "running with Python 3.x")
543 @check("py3k", "running with Python 3.x")
540 def has_py3k():
544 def has_py3k():
541 return 3 == sys.version_info[0]
545 return 3 == sys.version_info[0]
542
546
543 @check("py3exe", "a Python 3.x interpreter is available")
547 @check("py3exe", "a Python 3.x interpreter is available")
544 def has_python3exe():
548 def has_python3exe():
545 return 'PYTHON3' in os.environ
549 return 'PYTHON3' in os.environ
546
550
547 @check("py3pygments", "Pygments available on Python 3.x")
551 @check("py3pygments", "Pygments available on Python 3.x")
548 def has_py3pygments():
552 def has_py3pygments():
549 if has_py3k():
553 if has_py3k():
550 return has_pygments()
554 return has_pygments()
551 elif has_python3exe():
555 elif has_python3exe():
552 # just check exit status (ignoring output)
556 # just check exit status (ignoring output)
553 py3 = os.environ['PYTHON3']
557 py3 = os.environ['PYTHON3']
554 return matchoutput('%s -c "import pygments"' % py3, br'')
558 return matchoutput('%s -c "import pygments"' % py3, br'')
555 return False
559 return False
556
560
557 @check("pure", "running with pure Python code")
561 @check("pure", "running with pure Python code")
558 def has_pure():
562 def has_pure():
559 return any([
563 return any([
560 os.environ.get("HGMODULEPOLICY") == "py",
564 os.environ.get("HGMODULEPOLICY") == "py",
561 os.environ.get("HGTEST_RUN_TESTS_PURE") == "--pure",
565 os.environ.get("HGTEST_RUN_TESTS_PURE") == "--pure",
562 ])
566 ])
563
567
564 @check("slow", "allow slow tests")
568 @check("slow", "allow slow tests")
565 def has_slow():
569 def has_slow():
566 return os.environ.get('HGTEST_SLOW') == 'slow'
570 return os.environ.get('HGTEST_SLOW') == 'slow'
567
571
568 @check("hypothesis", "Hypothesis automated test generation")
572 @check("hypothesis", "Hypothesis automated test generation")
569 def has_hypothesis():
573 def has_hypothesis():
570 try:
574 try:
571 import hypothesis
575 import hypothesis
572 hypothesis.given
576 hypothesis.given
573 return True
577 return True
574 except ImportError:
578 except ImportError:
575 return False
579 return False
576
580
577 @check("unziplinks", "unzip(1) understands and extracts symlinks")
581 @check("unziplinks", "unzip(1) understands and extracts symlinks")
578 def unzip_understands_symlinks():
582 def unzip_understands_symlinks():
579 return matchoutput('unzip --help', br'Info-ZIP')
583 return matchoutput('unzip --help', br'Info-ZIP')
General Comments 0
You need to be logged in to leave comments. Login now