##// END OF EJS Templates
tests: add b'' when testing for tls1.2...
Gregory Szorc -
r41426:423a6b2d default
parent child Browse files
Show More
@@ -1,817 +1,817 b''
1 from __future__ import absolute_import
1 from __future__ import absolute_import
2
2
3 import os
3 import os
4 import re
4 import re
5 import socket
5 import socket
6 import stat
6 import stat
7 import subprocess
7 import subprocess
8 import sys
8 import sys
9 import tempfile
9 import tempfile
10
10
11 tempprefix = 'hg-hghave-'
11 tempprefix = 'hg-hghave-'
12
12
13 checks = {
13 checks = {
14 "true": (lambda: True, "yak shaving"),
14 "true": (lambda: True, "yak shaving"),
15 "false": (lambda: False, "nail clipper"),
15 "false": (lambda: False, "nail clipper"),
16 }
16 }
17
17
18 try:
18 try:
19 import msvcrt
19 import msvcrt
20 msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
20 msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
21 msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
21 msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
22 except ImportError:
22 except ImportError:
23 pass
23 pass
24
24
25 stdout = getattr(sys.stdout, 'buffer', sys.stdout)
25 stdout = getattr(sys.stdout, 'buffer', sys.stdout)
26 stderr = getattr(sys.stderr, 'buffer', sys.stderr)
26 stderr = getattr(sys.stderr, 'buffer', sys.stderr)
27
27
28 if sys.version_info[0] >= 3:
28 if sys.version_info[0] >= 3:
29 def _bytespath(p):
29 def _bytespath(p):
30 if p is None:
30 if p is None:
31 return p
31 return p
32 return p.encode('utf-8')
32 return p.encode('utf-8')
33
33
34 def _strpath(p):
34 def _strpath(p):
35 if p is None:
35 if p is None:
36 return p
36 return p
37 return p.decode('utf-8')
37 return p.decode('utf-8')
38 else:
38 else:
39 def _bytespath(p):
39 def _bytespath(p):
40 return p
40 return p
41
41
42 _strpath = _bytespath
42 _strpath = _bytespath
43
43
44 def check(name, desc):
44 def check(name, desc):
45 """Registers a check function for a feature."""
45 """Registers a check function for a feature."""
46 def decorator(func):
46 def decorator(func):
47 checks[name] = (func, desc)
47 checks[name] = (func, desc)
48 return func
48 return func
49 return decorator
49 return decorator
50
50
51 def checkvers(name, desc, vers):
51 def checkvers(name, desc, vers):
52 """Registers a check function for each of a series of versions.
52 """Registers a check function for each of a series of versions.
53
53
54 vers can be a list or an iterator"""
54 vers can be a list or an iterator"""
55 def decorator(func):
55 def decorator(func):
56 def funcv(v):
56 def funcv(v):
57 def f():
57 def f():
58 return func(v)
58 return func(v)
59 return f
59 return f
60 for v in vers:
60 for v in vers:
61 v = str(v)
61 v = str(v)
62 f = funcv(v)
62 f = funcv(v)
63 checks['%s%s' % (name, v.replace('.', ''))] = (f, desc % v)
63 checks['%s%s' % (name, v.replace('.', ''))] = (f, desc % v)
64 return func
64 return func
65 return decorator
65 return decorator
66
66
67 def checkfeatures(features):
67 def checkfeatures(features):
68 result = {
68 result = {
69 'error': [],
69 'error': [],
70 'missing': [],
70 'missing': [],
71 'skipped': [],
71 'skipped': [],
72 }
72 }
73
73
74 for feature in features:
74 for feature in features:
75 negate = feature.startswith('no-')
75 negate = feature.startswith('no-')
76 if negate:
76 if negate:
77 feature = feature[3:]
77 feature = feature[3:]
78
78
79 if feature not in checks:
79 if feature not in checks:
80 result['missing'].append(feature)
80 result['missing'].append(feature)
81 continue
81 continue
82
82
83 check, desc = checks[feature]
83 check, desc = checks[feature]
84 try:
84 try:
85 available = check()
85 available = check()
86 except Exception:
86 except Exception:
87 result['error'].append('hghave check failed: %s' % feature)
87 result['error'].append('hghave check failed: %s' % feature)
88 continue
88 continue
89
89
90 if not negate and not available:
90 if not negate and not available:
91 result['skipped'].append('missing feature: %s' % desc)
91 result['skipped'].append('missing feature: %s' % desc)
92 elif negate and available:
92 elif negate and available:
93 result['skipped'].append('system supports %s' % desc)
93 result['skipped'].append('system supports %s' % desc)
94
94
95 return result
95 return result
96
96
97 def require(features):
97 def require(features):
98 """Require that features are available, exiting if not."""
98 """Require that features are available, exiting if not."""
99 result = checkfeatures(features)
99 result = checkfeatures(features)
100
100
101 for missing in result['missing']:
101 for missing in result['missing']:
102 stderr.write(('skipped: unknown feature: %s\n'
102 stderr.write(('skipped: unknown feature: %s\n'
103 % missing).encode('utf-8'))
103 % missing).encode('utf-8'))
104 for msg in result['skipped']:
104 for msg in result['skipped']:
105 stderr.write(('skipped: %s\n' % msg).encode('utf-8'))
105 stderr.write(('skipped: %s\n' % msg).encode('utf-8'))
106 for msg in result['error']:
106 for msg in result['error']:
107 stderr.write(('%s\n' % msg).encode('utf-8'))
107 stderr.write(('%s\n' % msg).encode('utf-8'))
108
108
109 if result['missing']:
109 if result['missing']:
110 sys.exit(2)
110 sys.exit(2)
111
111
112 if result['skipped'] or result['error']:
112 if result['skipped'] or result['error']:
113 sys.exit(1)
113 sys.exit(1)
114
114
115 def matchoutput(cmd, regexp, ignorestatus=False):
115 def matchoutput(cmd, regexp, ignorestatus=False):
116 """Return the match object if cmd executes successfully and its output
116 """Return the match object if cmd executes successfully and its output
117 is matched by the supplied regular expression.
117 is matched by the supplied regular expression.
118 """
118 """
119 r = re.compile(regexp)
119 r = re.compile(regexp)
120 p = subprocess.Popen(
120 p = subprocess.Popen(
121 cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
121 cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
122 s = p.communicate()[0]
122 s = p.communicate()[0]
123 ret = p.returncode
123 ret = p.returncode
124 return (ignorestatus or not ret) and r.search(s)
124 return (ignorestatus or not ret) and r.search(s)
125
125
126 @check("baz", "GNU Arch baz client")
126 @check("baz", "GNU Arch baz client")
127 def has_baz():
127 def has_baz():
128 return matchoutput('baz --version 2>&1', br'baz Bazaar version')
128 return matchoutput('baz --version 2>&1', br'baz Bazaar version')
129
129
130 @check("bzr", "Canonical's Bazaar client")
130 @check("bzr", "Canonical's Bazaar client")
131 def has_bzr():
131 def has_bzr():
132 try:
132 try:
133 import bzrlib
133 import bzrlib
134 import bzrlib.bzrdir
134 import bzrlib.bzrdir
135 import bzrlib.errors
135 import bzrlib.errors
136 import bzrlib.revision
136 import bzrlib.revision
137 import bzrlib.revisionspec
137 import bzrlib.revisionspec
138 bzrlib.revisionspec.RevisionSpec
138 bzrlib.revisionspec.RevisionSpec
139 return bzrlib.__doc__ is not None
139 return bzrlib.__doc__ is not None
140 except (AttributeError, ImportError):
140 except (AttributeError, ImportError):
141 return False
141 return False
142
142
143 @checkvers("bzr", "Canonical's Bazaar client >= %s", (1.14,))
143 @checkvers("bzr", "Canonical's Bazaar client >= %s", (1.14,))
144 def has_bzr_range(v):
144 def has_bzr_range(v):
145 major, minor = v.split('.')[0:2]
145 major, minor = v.split('.')[0:2]
146 try:
146 try:
147 import bzrlib
147 import bzrlib
148 return (bzrlib.__doc__ is not None
148 return (bzrlib.__doc__ is not None
149 and bzrlib.version_info[:2] >= (int(major), int(minor)))
149 and bzrlib.version_info[:2] >= (int(major), int(minor)))
150 except ImportError:
150 except ImportError:
151 return False
151 return False
152
152
153 @check("chg", "running with chg")
153 @check("chg", "running with chg")
154 def has_chg():
154 def has_chg():
155 return 'CHGHG' in os.environ
155 return 'CHGHG' in os.environ
156
156
157 @check("cvs", "cvs client/server")
157 @check("cvs", "cvs client/server")
158 def has_cvs():
158 def has_cvs():
159 re = br'Concurrent Versions System.*?server'
159 re = br'Concurrent Versions System.*?server'
160 return matchoutput('cvs --version 2>&1', re) and not has_msys()
160 return matchoutput('cvs --version 2>&1', re) and not has_msys()
161
161
162 @check("cvs112", "cvs client/server 1.12.* (not cvsnt)")
162 @check("cvs112", "cvs client/server 1.12.* (not cvsnt)")
163 def has_cvs112():
163 def has_cvs112():
164 re = br'Concurrent Versions System \(CVS\) 1.12.*?server'
164 re = br'Concurrent Versions System \(CVS\) 1.12.*?server'
165 return matchoutput('cvs --version 2>&1', re) and not has_msys()
165 return matchoutput('cvs --version 2>&1', re) and not has_msys()
166
166
167 @check("cvsnt", "cvsnt client/server")
167 @check("cvsnt", "cvsnt client/server")
168 def has_cvsnt():
168 def has_cvsnt():
169 re = br'Concurrent Versions System \(CVSNT\) (\d+).(\d+).*\(client/server\)'
169 re = br'Concurrent Versions System \(CVSNT\) (\d+).(\d+).*\(client/server\)'
170 return matchoutput('cvsnt --version 2>&1', re)
170 return matchoutput('cvsnt --version 2>&1', re)
171
171
172 @check("darcs", "darcs client")
172 @check("darcs", "darcs client")
173 def has_darcs():
173 def has_darcs():
174 return matchoutput('darcs --version', br'\b2\.([2-9]|\d{2})', True)
174 return matchoutput('darcs --version', br'\b2\.([2-9]|\d{2})', True)
175
175
176 @check("mtn", "monotone client (>= 1.0)")
176 @check("mtn", "monotone client (>= 1.0)")
177 def has_mtn():
177 def has_mtn():
178 return matchoutput('mtn --version', br'monotone', True) and not matchoutput(
178 return matchoutput('mtn --version', br'monotone', True) and not matchoutput(
179 'mtn --version', br'monotone 0\.', True)
179 'mtn --version', br'monotone 0\.', True)
180
180
181 @check("eol-in-paths", "end-of-lines in paths")
181 @check("eol-in-paths", "end-of-lines in paths")
182 def has_eol_in_paths():
182 def has_eol_in_paths():
183 try:
183 try:
184 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r')
184 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r')
185 os.close(fd)
185 os.close(fd)
186 os.remove(path)
186 os.remove(path)
187 return True
187 return True
188 except (IOError, OSError):
188 except (IOError, OSError):
189 return False
189 return False
190
190
191 @check("execbit", "executable bit")
191 @check("execbit", "executable bit")
192 def has_executablebit():
192 def has_executablebit():
193 try:
193 try:
194 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
194 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
195 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
195 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
196 try:
196 try:
197 os.close(fh)
197 os.close(fh)
198 m = os.stat(fn).st_mode & 0o777
198 m = os.stat(fn).st_mode & 0o777
199 new_file_has_exec = m & EXECFLAGS
199 new_file_has_exec = m & EXECFLAGS
200 os.chmod(fn, m ^ EXECFLAGS)
200 os.chmod(fn, m ^ EXECFLAGS)
201 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0o777) == m)
201 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0o777) == m)
202 finally:
202 finally:
203 os.unlink(fn)
203 os.unlink(fn)
204 except (IOError, OSError):
204 except (IOError, OSError):
205 # we don't care, the user probably won't be able to commit anyway
205 # we don't care, the user probably won't be able to commit anyway
206 return False
206 return False
207 return not (new_file_has_exec or exec_flags_cannot_flip)
207 return not (new_file_has_exec or exec_flags_cannot_flip)
208
208
209 @check("icasefs", "case insensitive file system")
209 @check("icasefs", "case insensitive file system")
210 def has_icasefs():
210 def has_icasefs():
211 # Stolen from mercurial.util
211 # Stolen from mercurial.util
212 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
212 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
213 os.close(fd)
213 os.close(fd)
214 try:
214 try:
215 s1 = os.stat(path)
215 s1 = os.stat(path)
216 d, b = os.path.split(path)
216 d, b = os.path.split(path)
217 p2 = os.path.join(d, b.upper())
217 p2 = os.path.join(d, b.upper())
218 if path == p2:
218 if path == p2:
219 p2 = os.path.join(d, b.lower())
219 p2 = os.path.join(d, b.lower())
220 try:
220 try:
221 s2 = os.stat(p2)
221 s2 = os.stat(p2)
222 return s2 == s1
222 return s2 == s1
223 except OSError:
223 except OSError:
224 return False
224 return False
225 finally:
225 finally:
226 os.remove(path)
226 os.remove(path)
227
227
228 @check("fifo", "named pipes")
228 @check("fifo", "named pipes")
229 def has_fifo():
229 def has_fifo():
230 if getattr(os, "mkfifo", None) is None:
230 if getattr(os, "mkfifo", None) is None:
231 return False
231 return False
232 name = tempfile.mktemp(dir='.', prefix=tempprefix)
232 name = tempfile.mktemp(dir='.', prefix=tempprefix)
233 try:
233 try:
234 os.mkfifo(name)
234 os.mkfifo(name)
235 os.unlink(name)
235 os.unlink(name)
236 return True
236 return True
237 except OSError:
237 except OSError:
238 return False
238 return False
239
239
240 @check("killdaemons", 'killdaemons.py support')
240 @check("killdaemons", 'killdaemons.py support')
241 def has_killdaemons():
241 def has_killdaemons():
242 return True
242 return True
243
243
244 @check("cacheable", "cacheable filesystem")
244 @check("cacheable", "cacheable filesystem")
245 def has_cacheable_fs():
245 def has_cacheable_fs():
246 from mercurial import util
246 from mercurial import util
247
247
248 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
248 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
249 os.close(fd)
249 os.close(fd)
250 try:
250 try:
251 return util.cachestat(path).cacheable()
251 return util.cachestat(path).cacheable()
252 finally:
252 finally:
253 os.remove(path)
253 os.remove(path)
254
254
255 @check("lsprof", "python lsprof module")
255 @check("lsprof", "python lsprof module")
256 def has_lsprof():
256 def has_lsprof():
257 try:
257 try:
258 import _lsprof
258 import _lsprof
259 _lsprof.Profiler # silence unused import warning
259 _lsprof.Profiler # silence unused import warning
260 return True
260 return True
261 except ImportError:
261 except ImportError:
262 return False
262 return False
263
263
264 def gethgversion():
264 def gethgversion():
265 m = matchoutput('hg --version --quiet 2>&1', br'(\d+)\.(\d+)')
265 m = matchoutput('hg --version --quiet 2>&1', br'(\d+)\.(\d+)')
266 if not m:
266 if not m:
267 return (0, 0)
267 return (0, 0)
268 return (int(m.group(1)), int(m.group(2)))
268 return (int(m.group(1)), int(m.group(2)))
269
269
270 @checkvers("hg", "Mercurial >= %s",
270 @checkvers("hg", "Mercurial >= %s",
271 list([(1.0 * x) / 10 for x in range(9, 99)]))
271 list([(1.0 * x) / 10 for x in range(9, 99)]))
272 def has_hg_range(v):
272 def has_hg_range(v):
273 major, minor = v.split('.')[0:2]
273 major, minor = v.split('.')[0:2]
274 return gethgversion() >= (int(major), int(minor))
274 return gethgversion() >= (int(major), int(minor))
275
275
276 @check("hg08", "Mercurial >= 0.8")
276 @check("hg08", "Mercurial >= 0.8")
277 def has_hg08():
277 def has_hg08():
278 if checks["hg09"][0]():
278 if checks["hg09"][0]():
279 return True
279 return True
280 return matchoutput('hg help annotate 2>&1', '--date')
280 return matchoutput('hg help annotate 2>&1', '--date')
281
281
282 @check("hg07", "Mercurial >= 0.7")
282 @check("hg07", "Mercurial >= 0.7")
283 def has_hg07():
283 def has_hg07():
284 if checks["hg08"][0]():
284 if checks["hg08"][0]():
285 return True
285 return True
286 return matchoutput('hg --version --quiet 2>&1', 'Mercurial Distributed SCM')
286 return matchoutput('hg --version --quiet 2>&1', 'Mercurial Distributed SCM')
287
287
288 @check("hg06", "Mercurial >= 0.6")
288 @check("hg06", "Mercurial >= 0.6")
289 def has_hg06():
289 def has_hg06():
290 if checks["hg07"][0]():
290 if checks["hg07"][0]():
291 return True
291 return True
292 return matchoutput('hg --version --quiet 2>&1', 'Mercurial version')
292 return matchoutput('hg --version --quiet 2>&1', 'Mercurial version')
293
293
294 @check("gettext", "GNU Gettext (msgfmt)")
294 @check("gettext", "GNU Gettext (msgfmt)")
295 def has_gettext():
295 def has_gettext():
296 return matchoutput('msgfmt --version', br'GNU gettext-tools')
296 return matchoutput('msgfmt --version', br'GNU gettext-tools')
297
297
298 @check("git", "git command line client")
298 @check("git", "git command line client")
299 def has_git():
299 def has_git():
300 return matchoutput('git --version 2>&1', br'^git version')
300 return matchoutput('git --version 2>&1', br'^git version')
301
301
302 def getgitversion():
302 def getgitversion():
303 m = matchoutput('git --version 2>&1', br'git version (\d+)\.(\d+)')
303 m = matchoutput('git --version 2>&1', br'git version (\d+)\.(\d+)')
304 if not m:
304 if not m:
305 return (0, 0)
305 return (0, 0)
306 return (int(m.group(1)), int(m.group(2)))
306 return (int(m.group(1)), int(m.group(2)))
307
307
308 # https://github.com/git-lfs/lfs-test-server
308 # https://github.com/git-lfs/lfs-test-server
309 @check("lfs-test-server", "git-lfs test server")
309 @check("lfs-test-server", "git-lfs test server")
310 def has_lfsserver():
310 def has_lfsserver():
311 exe = 'lfs-test-server'
311 exe = 'lfs-test-server'
312 if has_windows():
312 if has_windows():
313 exe = 'lfs-test-server.exe'
313 exe = 'lfs-test-server.exe'
314 return any(
314 return any(
315 os.access(os.path.join(path, exe), os.X_OK)
315 os.access(os.path.join(path, exe), os.X_OK)
316 for path in os.environ["PATH"].split(os.pathsep)
316 for path in os.environ["PATH"].split(os.pathsep)
317 )
317 )
318
318
319 @checkvers("git", "git client (with ext::sh support) version >= %s", (1.9,))
319 @checkvers("git", "git client (with ext::sh support) version >= %s", (1.9,))
320 def has_git_range(v):
320 def has_git_range(v):
321 major, minor = v.split('.')[0:2]
321 major, minor = v.split('.')[0:2]
322 return getgitversion() >= (int(major), int(minor))
322 return getgitversion() >= (int(major), int(minor))
323
323
324 @check("docutils", "Docutils text processing library")
324 @check("docutils", "Docutils text processing library")
325 def has_docutils():
325 def has_docutils():
326 try:
326 try:
327 import docutils.core
327 import docutils.core
328 docutils.core.publish_cmdline # silence unused import
328 docutils.core.publish_cmdline # silence unused import
329 return True
329 return True
330 except ImportError:
330 except ImportError:
331 return False
331 return False
332
332
333 def getsvnversion():
333 def getsvnversion():
334 m = matchoutput('svn --version --quiet 2>&1', br'^(\d+)\.(\d+)')
334 m = matchoutput('svn --version --quiet 2>&1', br'^(\d+)\.(\d+)')
335 if not m:
335 if not m:
336 return (0, 0)
336 return (0, 0)
337 return (int(m.group(1)), int(m.group(2)))
337 return (int(m.group(1)), int(m.group(2)))
338
338
339 @checkvers("svn", "subversion client and admin tools >= %s", (1.3, 1.5))
339 @checkvers("svn", "subversion client and admin tools >= %s", (1.3, 1.5))
340 def has_svn_range(v):
340 def has_svn_range(v):
341 major, minor = v.split('.')[0:2]
341 major, minor = v.split('.')[0:2]
342 return getsvnversion() >= (int(major), int(minor))
342 return getsvnversion() >= (int(major), int(minor))
343
343
344 @check("svn", "subversion client and admin tools")
344 @check("svn", "subversion client and admin tools")
345 def has_svn():
345 def has_svn():
346 return matchoutput('svn --version 2>&1', br'^svn, version') and \
346 return matchoutput('svn --version 2>&1', br'^svn, version') and \
347 matchoutput('svnadmin --version 2>&1', br'^svnadmin, version')
347 matchoutput('svnadmin --version 2>&1', br'^svnadmin, version')
348
348
349 @check("svn-bindings", "subversion python bindings")
349 @check("svn-bindings", "subversion python bindings")
350 def has_svn_bindings():
350 def has_svn_bindings():
351 try:
351 try:
352 import svn.core
352 import svn.core
353 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
353 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
354 if version < (1, 4):
354 if version < (1, 4):
355 return False
355 return False
356 return True
356 return True
357 except ImportError:
357 except ImportError:
358 return False
358 return False
359
359
360 @check("p4", "Perforce server and client")
360 @check("p4", "Perforce server and client")
361 def has_p4():
361 def has_p4():
362 return (matchoutput('p4 -V', br'Rev\. P4/') and
362 return (matchoutput('p4 -V', br'Rev\. P4/') and
363 matchoutput('p4d -V', br'Rev\. P4D/'))
363 matchoutput('p4d -V', br'Rev\. P4D/'))
364
364
365 @check("symlink", "symbolic links")
365 @check("symlink", "symbolic links")
366 def has_symlink():
366 def has_symlink():
367 if getattr(os, "symlink", None) is None:
367 if getattr(os, "symlink", None) is None:
368 return False
368 return False
369 name = tempfile.mktemp(dir='.', prefix=tempprefix)
369 name = tempfile.mktemp(dir='.', prefix=tempprefix)
370 try:
370 try:
371 os.symlink(".", name)
371 os.symlink(".", name)
372 os.unlink(name)
372 os.unlink(name)
373 return True
373 return True
374 except (OSError, AttributeError):
374 except (OSError, AttributeError):
375 return False
375 return False
376
376
377 @check("hardlink", "hardlinks")
377 @check("hardlink", "hardlinks")
378 def has_hardlink():
378 def has_hardlink():
379 from mercurial import util
379 from mercurial import util
380 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
380 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
381 os.close(fh)
381 os.close(fh)
382 name = tempfile.mktemp(dir='.', prefix=tempprefix)
382 name = tempfile.mktemp(dir='.', prefix=tempprefix)
383 try:
383 try:
384 util.oslink(_bytespath(fn), _bytespath(name))
384 util.oslink(_bytespath(fn), _bytespath(name))
385 os.unlink(name)
385 os.unlink(name)
386 return True
386 return True
387 except OSError:
387 except OSError:
388 return False
388 return False
389 finally:
389 finally:
390 os.unlink(fn)
390 os.unlink(fn)
391
391
392 @check("hardlink-whitelisted", "hardlinks on whitelisted filesystems")
392 @check("hardlink-whitelisted", "hardlinks on whitelisted filesystems")
393 def has_hardlink_whitelisted():
393 def has_hardlink_whitelisted():
394 from mercurial import util
394 from mercurial import util
395 try:
395 try:
396 fstype = util.getfstype(b'.')
396 fstype = util.getfstype(b'.')
397 except OSError:
397 except OSError:
398 return False
398 return False
399 return fstype in util._hardlinkfswhitelist
399 return fstype in util._hardlinkfswhitelist
400
400
401 @check("rmcwd", "can remove current working directory")
401 @check("rmcwd", "can remove current working directory")
402 def has_rmcwd():
402 def has_rmcwd():
403 ocwd = os.getcwd()
403 ocwd = os.getcwd()
404 temp = tempfile.mkdtemp(dir='.', prefix=tempprefix)
404 temp = tempfile.mkdtemp(dir='.', prefix=tempprefix)
405 try:
405 try:
406 os.chdir(temp)
406 os.chdir(temp)
407 # On Linux, 'rmdir .' isn't allowed, but the other names are okay.
407 # On Linux, 'rmdir .' isn't allowed, but the other names are okay.
408 # On Solaris and Windows, the cwd can't be removed by any names.
408 # On Solaris and Windows, the cwd can't be removed by any names.
409 os.rmdir(os.getcwd())
409 os.rmdir(os.getcwd())
410 return True
410 return True
411 except OSError:
411 except OSError:
412 return False
412 return False
413 finally:
413 finally:
414 os.chdir(ocwd)
414 os.chdir(ocwd)
415 # clean up temp dir on platforms where cwd can't be removed
415 # clean up temp dir on platforms where cwd can't be removed
416 try:
416 try:
417 os.rmdir(temp)
417 os.rmdir(temp)
418 except OSError:
418 except OSError:
419 pass
419 pass
420
420
421 @check("tla", "GNU Arch tla client")
421 @check("tla", "GNU Arch tla client")
422 def has_tla():
422 def has_tla():
423 return matchoutput('tla --version 2>&1', br'The GNU Arch Revision')
423 return matchoutput('tla --version 2>&1', br'The GNU Arch Revision')
424
424
425 @check("gpg", "gpg client")
425 @check("gpg", "gpg client")
426 def has_gpg():
426 def has_gpg():
427 return matchoutput('gpg --version 2>&1', br'GnuPG')
427 return matchoutput('gpg --version 2>&1', br'GnuPG')
428
428
429 @check("gpg2", "gpg client v2")
429 @check("gpg2", "gpg client v2")
430 def has_gpg2():
430 def has_gpg2():
431 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.')
431 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.')
432
432
433 @check("gpg21", "gpg client v2.1+")
433 @check("gpg21", "gpg client v2.1+")
434 def has_gpg21():
434 def has_gpg21():
435 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.(?!0)')
435 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.(?!0)')
436
436
437 @check("unix-permissions", "unix-style permissions")
437 @check("unix-permissions", "unix-style permissions")
438 def has_unix_permissions():
438 def has_unix_permissions():
439 d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
439 d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
440 try:
440 try:
441 fname = os.path.join(d, 'foo')
441 fname = os.path.join(d, 'foo')
442 for umask in (0o77, 0o07, 0o22):
442 for umask in (0o77, 0o07, 0o22):
443 os.umask(umask)
443 os.umask(umask)
444 f = open(fname, 'w')
444 f = open(fname, 'w')
445 f.close()
445 f.close()
446 mode = os.stat(fname).st_mode
446 mode = os.stat(fname).st_mode
447 os.unlink(fname)
447 os.unlink(fname)
448 if mode & 0o777 != ~umask & 0o666:
448 if mode & 0o777 != ~umask & 0o666:
449 return False
449 return False
450 return True
450 return True
451 finally:
451 finally:
452 os.rmdir(d)
452 os.rmdir(d)
453
453
454 @check("unix-socket", "AF_UNIX socket family")
454 @check("unix-socket", "AF_UNIX socket family")
455 def has_unix_socket():
455 def has_unix_socket():
456 return getattr(socket, 'AF_UNIX', None) is not None
456 return getattr(socket, 'AF_UNIX', None) is not None
457
457
458 @check("root", "root permissions")
458 @check("root", "root permissions")
459 def has_root():
459 def has_root():
460 return getattr(os, 'geteuid', None) and os.geteuid() == 0
460 return getattr(os, 'geteuid', None) and os.geteuid() == 0
461
461
462 @check("pyflakes", "Pyflakes python linter")
462 @check("pyflakes", "Pyflakes python linter")
463 def has_pyflakes():
463 def has_pyflakes():
464 return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"",
464 return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"",
465 br"<stdin>:1: 're' imported but unused",
465 br"<stdin>:1: 're' imported but unused",
466 True)
466 True)
467
467
468 @check("pylint", "Pylint python linter")
468 @check("pylint", "Pylint python linter")
469 def has_pylint():
469 def has_pylint():
470 return matchoutput("pylint --help",
470 return matchoutput("pylint --help",
471 br"Usage: pylint",
471 br"Usage: pylint",
472 True)
472 True)
473
473
474 @check("clang-format", "clang-format C code formatter")
474 @check("clang-format", "clang-format C code formatter")
475 def has_clang_format():
475 def has_clang_format():
476 m = matchoutput('clang-format --version', br'clang-format version (\d)')
476 m = matchoutput('clang-format --version', br'clang-format version (\d)')
477 # style changed somewhere between 4.x and 6.x
477 # style changed somewhere between 4.x and 6.x
478 return m and int(m.group(1)) >= 6
478 return m and int(m.group(1)) >= 6
479
479
480 @check("jshint", "JSHint static code analysis tool")
480 @check("jshint", "JSHint static code analysis tool")
481 def has_jshint():
481 def has_jshint():
482 return matchoutput("jshint --version 2>&1", br"jshint v")
482 return matchoutput("jshint --version 2>&1", br"jshint v")
483
483
484 @check("pygments", "Pygments source highlighting library")
484 @check("pygments", "Pygments source highlighting library")
485 def has_pygments():
485 def has_pygments():
486 try:
486 try:
487 import pygments
487 import pygments
488 pygments.highlight # silence unused import warning
488 pygments.highlight # silence unused import warning
489 return True
489 return True
490 except ImportError:
490 except ImportError:
491 return False
491 return False
492
492
493 @check("outer-repo", "outer repo")
493 @check("outer-repo", "outer repo")
494 def has_outer_repo():
494 def has_outer_repo():
495 # failing for other reasons than 'no repo' imply that there is a repo
495 # failing for other reasons than 'no repo' imply that there is a repo
496 return not matchoutput('hg root 2>&1',
496 return not matchoutput('hg root 2>&1',
497 br'abort: no repository found', True)
497 br'abort: no repository found', True)
498
498
499 @check("ssl", "ssl module available")
499 @check("ssl", "ssl module available")
500 def has_ssl():
500 def has_ssl():
501 try:
501 try:
502 import ssl
502 import ssl
503 ssl.CERT_NONE
503 ssl.CERT_NONE
504 return True
504 return True
505 except ImportError:
505 except ImportError:
506 return False
506 return False
507
507
508 @check("sslcontext", "python >= 2.7.9 ssl")
508 @check("sslcontext", "python >= 2.7.9 ssl")
509 def has_sslcontext():
509 def has_sslcontext():
510 try:
510 try:
511 import ssl
511 import ssl
512 ssl.SSLContext
512 ssl.SSLContext
513 return True
513 return True
514 except (ImportError, AttributeError):
514 except (ImportError, AttributeError):
515 return False
515 return False
516
516
517 @check("defaultcacerts", "can verify SSL certs by system's CA certs store")
517 @check("defaultcacerts", "can verify SSL certs by system's CA certs store")
518 def has_defaultcacerts():
518 def has_defaultcacerts():
519 from mercurial import sslutil, ui as uimod
519 from mercurial import sslutil, ui as uimod
520 ui = uimod.ui.load()
520 ui = uimod.ui.load()
521 return sslutil._defaultcacerts(ui) or sslutil._canloaddefaultcerts
521 return sslutil._defaultcacerts(ui) or sslutil._canloaddefaultcerts
522
522
523 @check("defaultcacertsloaded", "detected presence of loaded system CA certs")
523 @check("defaultcacertsloaded", "detected presence of loaded system CA certs")
524 def has_defaultcacertsloaded():
524 def has_defaultcacertsloaded():
525 import ssl
525 import ssl
526 from mercurial import sslutil, ui as uimod
526 from mercurial import sslutil, ui as uimod
527
527
528 if not has_defaultcacerts():
528 if not has_defaultcacerts():
529 return False
529 return False
530 if not has_sslcontext():
530 if not has_sslcontext():
531 return False
531 return False
532
532
533 ui = uimod.ui.load()
533 ui = uimod.ui.load()
534 cafile = sslutil._defaultcacerts(ui)
534 cafile = sslutil._defaultcacerts(ui)
535 ctx = ssl.create_default_context()
535 ctx = ssl.create_default_context()
536 if cafile:
536 if cafile:
537 ctx.load_verify_locations(cafile=cafile)
537 ctx.load_verify_locations(cafile=cafile)
538 else:
538 else:
539 ctx.load_default_certs()
539 ctx.load_default_certs()
540
540
541 return len(ctx.get_ca_certs()) > 0
541 return len(ctx.get_ca_certs()) > 0
542
542
543 @check("tls1.2", "TLS 1.2 protocol support")
543 @check("tls1.2", "TLS 1.2 protocol support")
544 def has_tls1_2():
544 def has_tls1_2():
545 from mercurial import sslutil
545 from mercurial import sslutil
546 return 'tls1.2' in sslutil.supportedprotocols
546 return b'tls1.2' in sslutil.supportedprotocols
547
547
548 @check("windows", "Windows")
548 @check("windows", "Windows")
549 def has_windows():
549 def has_windows():
550 return os.name == 'nt'
550 return os.name == 'nt'
551
551
552 @check("system-sh", "system() uses sh")
552 @check("system-sh", "system() uses sh")
553 def has_system_sh():
553 def has_system_sh():
554 return os.name != 'nt'
554 return os.name != 'nt'
555
555
556 @check("serve", "platform and python can manage 'hg serve -d'")
556 @check("serve", "platform and python can manage 'hg serve -d'")
557 def has_serve():
557 def has_serve():
558 return True
558 return True
559
559
560 @check("test-repo", "running tests from repository")
560 @check("test-repo", "running tests from repository")
561 def has_test_repo():
561 def has_test_repo():
562 t = os.environ["TESTDIR"]
562 t = os.environ["TESTDIR"]
563 return os.path.isdir(os.path.join(t, "..", ".hg"))
563 return os.path.isdir(os.path.join(t, "..", ".hg"))
564
564
565 @check("tic", "terminfo compiler and curses module")
565 @check("tic", "terminfo compiler and curses module")
566 def has_tic():
566 def has_tic():
567 try:
567 try:
568 import curses
568 import curses
569 curses.COLOR_BLUE
569 curses.COLOR_BLUE
570 return matchoutput('test -x "`which tic`"', br'')
570 return matchoutput('test -x "`which tic`"', br'')
571 except ImportError:
571 except ImportError:
572 return False
572 return False
573
573
574 @check("msys", "Windows with MSYS")
574 @check("msys", "Windows with MSYS")
575 def has_msys():
575 def has_msys():
576 return os.getenv('MSYSTEM')
576 return os.getenv('MSYSTEM')
577
577
578 @check("aix", "AIX")
578 @check("aix", "AIX")
579 def has_aix():
579 def has_aix():
580 return sys.platform.startswith("aix")
580 return sys.platform.startswith("aix")
581
581
582 @check("osx", "OS X")
582 @check("osx", "OS X")
583 def has_osx():
583 def has_osx():
584 return sys.platform == 'darwin'
584 return sys.platform == 'darwin'
585
585
586 @check("osxpackaging", "OS X packaging tools")
586 @check("osxpackaging", "OS X packaging tools")
587 def has_osxpackaging():
587 def has_osxpackaging():
588 try:
588 try:
589 return (matchoutput('pkgbuild', br'Usage: pkgbuild ', ignorestatus=1)
589 return (matchoutput('pkgbuild', br'Usage: pkgbuild ', ignorestatus=1)
590 and matchoutput(
590 and matchoutput(
591 'productbuild', br'Usage: productbuild ',
591 'productbuild', br'Usage: productbuild ',
592 ignorestatus=1)
592 ignorestatus=1)
593 and matchoutput('lsbom', br'Usage: lsbom', ignorestatus=1)
593 and matchoutput('lsbom', br'Usage: lsbom', ignorestatus=1)
594 and matchoutput(
594 and matchoutput(
595 'xar --help', br'Usage: xar', ignorestatus=1))
595 'xar --help', br'Usage: xar', ignorestatus=1))
596 except ImportError:
596 except ImportError:
597 return False
597 return False
598
598
599 @check('linuxormacos', 'Linux or MacOS')
599 @check('linuxormacos', 'Linux or MacOS')
600 def has_linuxormacos():
600 def has_linuxormacos():
601 # This isn't a perfect test for MacOS. But it is sufficient for our needs.
601 # This isn't a perfect test for MacOS. But it is sufficient for our needs.
602 return sys.platform.startswith(('linux', 'darwin'))
602 return sys.platform.startswith(('linux', 'darwin'))
603
603
604 @check("docker", "docker support")
604 @check("docker", "docker support")
605 def has_docker():
605 def has_docker():
606 pat = br'A self-sufficient runtime for'
606 pat = br'A self-sufficient runtime for'
607 if matchoutput('docker --help', pat):
607 if matchoutput('docker --help', pat):
608 if 'linux' not in sys.platform:
608 if 'linux' not in sys.platform:
609 # TODO: in theory we should be able to test docker-based
609 # TODO: in theory we should be able to test docker-based
610 # package creation on non-linux using boot2docker, but in
610 # package creation on non-linux using boot2docker, but in
611 # practice that requires extra coordination to make sure
611 # practice that requires extra coordination to make sure
612 # $TESTTEMP is going to be visible at the same path to the
612 # $TESTTEMP is going to be visible at the same path to the
613 # boot2docker VM. If we figure out how to verify that, we
613 # boot2docker VM. If we figure out how to verify that, we
614 # can use the following instead of just saying False:
614 # can use the following instead of just saying False:
615 # return 'DOCKER_HOST' in os.environ
615 # return 'DOCKER_HOST' in os.environ
616 return False
616 return False
617
617
618 return True
618 return True
619 return False
619 return False
620
620
621 @check("debhelper", "debian packaging tools")
621 @check("debhelper", "debian packaging tools")
622 def has_debhelper():
622 def has_debhelper():
623 # Some versions of dpkg say `dpkg', some say 'dpkg' (` vs ' on the first
623 # Some versions of dpkg say `dpkg', some say 'dpkg' (` vs ' on the first
624 # quote), so just accept anything in that spot.
624 # quote), so just accept anything in that spot.
625 dpkg = matchoutput('dpkg --version',
625 dpkg = matchoutput('dpkg --version',
626 br"Debian .dpkg' package management program")
626 br"Debian .dpkg' package management program")
627 dh = matchoutput('dh --help',
627 dh = matchoutput('dh --help',
628 br'dh is a part of debhelper.', ignorestatus=True)
628 br'dh is a part of debhelper.', ignorestatus=True)
629 dh_py2 = matchoutput('dh_python2 --help',
629 dh_py2 = matchoutput('dh_python2 --help',
630 br'other supported Python versions')
630 br'other supported Python versions')
631 # debuild comes from the 'devscripts' package, though you might want
631 # debuild comes from the 'devscripts' package, though you might want
632 # the 'build-debs' package instead, which has a dependency on devscripts.
632 # the 'build-debs' package instead, which has a dependency on devscripts.
633 debuild = matchoutput('debuild --help',
633 debuild = matchoutput('debuild --help',
634 br'to run debian/rules with given parameter')
634 br'to run debian/rules with given parameter')
635 return dpkg and dh and dh_py2 and debuild
635 return dpkg and dh and dh_py2 and debuild
636
636
637 @check("debdeps",
637 @check("debdeps",
638 "debian build dependencies (run dpkg-checkbuilddeps in contrib/)")
638 "debian build dependencies (run dpkg-checkbuilddeps in contrib/)")
639 def has_debdeps():
639 def has_debdeps():
640 # just check exit status (ignoring output)
640 # just check exit status (ignoring output)
641 path = '%s/../contrib/packaging/debian/control' % os.environ['TESTDIR']
641 path = '%s/../contrib/packaging/debian/control' % os.environ['TESTDIR']
642 return matchoutput('dpkg-checkbuilddeps %s' % path, br'')
642 return matchoutput('dpkg-checkbuilddeps %s' % path, br'')
643
643
644 @check("demandimport", "demandimport enabled")
644 @check("demandimport", "demandimport enabled")
645 def has_demandimport():
645 def has_demandimport():
646 # chg disables demandimport intentionally for performance wins.
646 # chg disables demandimport intentionally for performance wins.
647 return ((not has_chg()) and os.environ.get('HGDEMANDIMPORT') != 'disable')
647 return ((not has_chg()) and os.environ.get('HGDEMANDIMPORT') != 'disable')
648
648
649 @check("py3", "running with Python 3.x")
649 @check("py3", "running with Python 3.x")
650 def has_py3():
650 def has_py3():
651 return 3 == sys.version_info[0]
651 return 3 == sys.version_info[0]
652
652
653 @check("py3exe", "a Python 3.x interpreter is available")
653 @check("py3exe", "a Python 3.x interpreter is available")
654 def has_python3exe():
654 def has_python3exe():
655 return matchoutput('python3 -V', br'^Python 3.(5|6|7|8|9)')
655 return matchoutput('python3 -V', br'^Python 3.(5|6|7|8|9)')
656
656
657 @check("pure", "running with pure Python code")
657 @check("pure", "running with pure Python code")
658 def has_pure():
658 def has_pure():
659 return any([
659 return any([
660 os.environ.get("HGMODULEPOLICY") == "py",
660 os.environ.get("HGMODULEPOLICY") == "py",
661 os.environ.get("HGTEST_RUN_TESTS_PURE") == "--pure",
661 os.environ.get("HGTEST_RUN_TESTS_PURE") == "--pure",
662 ])
662 ])
663
663
664 @check("slow", "allow slow tests (use --allow-slow-tests)")
664 @check("slow", "allow slow tests (use --allow-slow-tests)")
665 def has_slow():
665 def has_slow():
666 return os.environ.get('HGTEST_SLOW') == 'slow'
666 return os.environ.get('HGTEST_SLOW') == 'slow'
667
667
668 @check("hypothesis", "Hypothesis automated test generation")
668 @check("hypothesis", "Hypothesis automated test generation")
669 def has_hypothesis():
669 def has_hypothesis():
670 try:
670 try:
671 import hypothesis
671 import hypothesis
672 hypothesis.given
672 hypothesis.given
673 return True
673 return True
674 except ImportError:
674 except ImportError:
675 return False
675 return False
676
676
677 @check("unziplinks", "unzip(1) understands and extracts symlinks")
677 @check("unziplinks", "unzip(1) understands and extracts symlinks")
678 def unzip_understands_symlinks():
678 def unzip_understands_symlinks():
679 return matchoutput('unzip --help', br'Info-ZIP')
679 return matchoutput('unzip --help', br'Info-ZIP')
680
680
681 @check("zstd", "zstd Python module available")
681 @check("zstd", "zstd Python module available")
682 def has_zstd():
682 def has_zstd():
683 try:
683 try:
684 import mercurial.zstd
684 import mercurial.zstd
685 mercurial.zstd.__version__
685 mercurial.zstd.__version__
686 return True
686 return True
687 except ImportError:
687 except ImportError:
688 return False
688 return False
689
689
690 @check("devfull", "/dev/full special file")
690 @check("devfull", "/dev/full special file")
691 def has_dev_full():
691 def has_dev_full():
692 return os.path.exists('/dev/full')
692 return os.path.exists('/dev/full')
693
693
694 @check("virtualenv", "Python virtualenv support")
694 @check("virtualenv", "Python virtualenv support")
695 def has_virtualenv():
695 def has_virtualenv():
696 try:
696 try:
697 import virtualenv
697 import virtualenv
698 virtualenv.ACTIVATE_SH
698 virtualenv.ACTIVATE_SH
699 return True
699 return True
700 except ImportError:
700 except ImportError:
701 return False
701 return False
702
702
703 @check("fsmonitor", "running tests with fsmonitor")
703 @check("fsmonitor", "running tests with fsmonitor")
704 def has_fsmonitor():
704 def has_fsmonitor():
705 return 'HGFSMONITOR_TESTS' in os.environ
705 return 'HGFSMONITOR_TESTS' in os.environ
706
706
707 @check("fuzzywuzzy", "Fuzzy string matching library")
707 @check("fuzzywuzzy", "Fuzzy string matching library")
708 def has_fuzzywuzzy():
708 def has_fuzzywuzzy():
709 try:
709 try:
710 import fuzzywuzzy
710 import fuzzywuzzy
711 fuzzywuzzy.__version__
711 fuzzywuzzy.__version__
712 return True
712 return True
713 except ImportError:
713 except ImportError:
714 return False
714 return False
715
715
716 @check("clang-libfuzzer", "clang new enough to include libfuzzer")
716 @check("clang-libfuzzer", "clang new enough to include libfuzzer")
717 def has_clang_libfuzzer():
717 def has_clang_libfuzzer():
718 mat = matchoutput('clang --version', b'clang version (\d)')
718 mat = matchoutput('clang --version', b'clang version (\d)')
719 if mat:
719 if mat:
720 # libfuzzer is new in clang 6
720 # libfuzzer is new in clang 6
721 return int(mat.group(1)) > 5
721 return int(mat.group(1)) > 5
722 return False
722 return False
723
723
724 @check("clang-6.0", "clang 6.0 with version suffix (libfuzzer included)")
724 @check("clang-6.0", "clang 6.0 with version suffix (libfuzzer included)")
725 def has_clang60():
725 def has_clang60():
726 return matchoutput('clang-6.0 --version', b'clang version 6\.')
726 return matchoutput('clang-6.0 --version', b'clang version 6\.')
727
727
728 @check("xdiff", "xdiff algorithm")
728 @check("xdiff", "xdiff algorithm")
729 def has_xdiff():
729 def has_xdiff():
730 try:
730 try:
731 from mercurial import policy
731 from mercurial import policy
732 bdiff = policy.importmod('bdiff')
732 bdiff = policy.importmod('bdiff')
733 return bdiff.xdiffblocks(b'', b'') == [(0, 0, 0, 0)]
733 return bdiff.xdiffblocks(b'', b'') == [(0, 0, 0, 0)]
734 except (ImportError, AttributeError):
734 except (ImportError, AttributeError):
735 return False
735 return False
736
736
737 @check('extraextensions', 'whether tests are running with extra extensions')
737 @check('extraextensions', 'whether tests are running with extra extensions')
738 def has_extraextensions():
738 def has_extraextensions():
739 return 'HGTESTEXTRAEXTENSIONS' in os.environ
739 return 'HGTESTEXTRAEXTENSIONS' in os.environ
740
740
741 def getrepofeatures():
741 def getrepofeatures():
742 """Obtain set of repository features in use.
742 """Obtain set of repository features in use.
743
743
744 HGREPOFEATURES can be used to define or remove features. It contains
744 HGREPOFEATURES can be used to define or remove features. It contains
745 a space-delimited list of feature strings. Strings beginning with ``-``
745 a space-delimited list of feature strings. Strings beginning with ``-``
746 mean to remove.
746 mean to remove.
747 """
747 """
748 # Default list provided by core.
748 # Default list provided by core.
749 features = {
749 features = {
750 'bundlerepo',
750 'bundlerepo',
751 'revlogstore',
751 'revlogstore',
752 'fncache',
752 'fncache',
753 }
753 }
754
754
755 # Features that imply other features.
755 # Features that imply other features.
756 implies = {
756 implies = {
757 'simplestore': ['-revlogstore', '-bundlerepo', '-fncache'],
757 'simplestore': ['-revlogstore', '-bundlerepo', '-fncache'],
758 }
758 }
759
759
760 for override in os.environ.get('HGREPOFEATURES', '').split(' '):
760 for override in os.environ.get('HGREPOFEATURES', '').split(' '):
761 if not override:
761 if not override:
762 continue
762 continue
763
763
764 if override.startswith('-'):
764 if override.startswith('-'):
765 if override[1:] in features:
765 if override[1:] in features:
766 features.remove(override[1:])
766 features.remove(override[1:])
767 else:
767 else:
768 features.add(override)
768 features.add(override)
769
769
770 for imply in implies.get(override, []):
770 for imply in implies.get(override, []):
771 if imply.startswith('-'):
771 if imply.startswith('-'):
772 if imply[1:] in features:
772 if imply[1:] in features:
773 features.remove(imply[1:])
773 features.remove(imply[1:])
774 else:
774 else:
775 features.add(imply)
775 features.add(imply)
776
776
777 return features
777 return features
778
778
779 @check('reporevlogstore', 'repository using the default revlog store')
779 @check('reporevlogstore', 'repository using the default revlog store')
780 def has_reporevlogstore():
780 def has_reporevlogstore():
781 return 'revlogstore' in getrepofeatures()
781 return 'revlogstore' in getrepofeatures()
782
782
783 @check('reposimplestore', 'repository using simple storage extension')
783 @check('reposimplestore', 'repository using simple storage extension')
784 def has_reposimplestore():
784 def has_reposimplestore():
785 return 'simplestore' in getrepofeatures()
785 return 'simplestore' in getrepofeatures()
786
786
787 @check('repobundlerepo', 'whether we can open bundle files as repos')
787 @check('repobundlerepo', 'whether we can open bundle files as repos')
788 def has_repobundlerepo():
788 def has_repobundlerepo():
789 return 'bundlerepo' in getrepofeatures()
789 return 'bundlerepo' in getrepofeatures()
790
790
791 @check('repofncache', 'repository has an fncache')
791 @check('repofncache', 'repository has an fncache')
792 def has_repofncache():
792 def has_repofncache():
793 return 'fncache' in getrepofeatures()
793 return 'fncache' in getrepofeatures()
794
794
795 @check('sqlite', 'sqlite3 module is available')
795 @check('sqlite', 'sqlite3 module is available')
796 def has_sqlite():
796 def has_sqlite():
797 try:
797 try:
798 import sqlite3
798 import sqlite3
799 version = sqlite3.sqlite_version_info
799 version = sqlite3.sqlite_version_info
800 except ImportError:
800 except ImportError:
801 return False
801 return False
802
802
803 if version < (3, 8, 3):
803 if version < (3, 8, 3):
804 # WITH clause not supported
804 # WITH clause not supported
805 return False
805 return False
806
806
807 return matchoutput('sqlite3 -version', b'^3\.\d+')
807 return matchoutput('sqlite3 -version', b'^3\.\d+')
808
808
809 @check('vcr', 'vcr http mocking library')
809 @check('vcr', 'vcr http mocking library')
810 def has_vcr():
810 def has_vcr():
811 try:
811 try:
812 import vcr
812 import vcr
813 vcr.VCR
813 vcr.VCR
814 return True
814 return True
815 except (ImportError, AttributeError):
815 except (ImportError, AttributeError):
816 pass
816 pass
817 return False
817 return False
General Comments 0
You need to be logged in to leave comments. Login now