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