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