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