##// END OF EJS Templates
hghave: add has_git_range for testing if git understands ext::sh...
Sean Farley -
r32901:559db66d default
parent child Browse files
Show More
@@ -1,649 +1,660 b''
1 from __future__ import absolute_import
1 from __future__ import absolute_import
2
2
3 import errno
3 import errno
4 import os
4 import os
5 import re
5 import re
6 import socket
6 import socket
7 import stat
7 import stat
8 import subprocess
8 import subprocess
9 import sys
9 import sys
10 import tempfile
10 import tempfile
11
11
12 tempprefix = 'hg-hghave-'
12 tempprefix = 'hg-hghave-'
13
13
14 checks = {
14 checks = {
15 "true": (lambda: True, "yak shaving"),
15 "true": (lambda: True, "yak shaving"),
16 "false": (lambda: False, "nail clipper"),
16 "false": (lambda: False, "nail clipper"),
17 }
17 }
18
18
19 def check(name, desc):
19 def check(name, desc):
20 """Registers a check function for a feature."""
20 """Registers a check function for a feature."""
21 def decorator(func):
21 def decorator(func):
22 checks[name] = (func, desc)
22 checks[name] = (func, desc)
23 return func
23 return func
24 return decorator
24 return decorator
25
25
26 def checkvers(name, desc, vers):
26 def checkvers(name, desc, vers):
27 """Registers a check function for each of a series of versions.
27 """Registers a check function for each of a series of versions.
28
28
29 vers can be a list or an iterator"""
29 vers can be a list or an iterator"""
30 def decorator(func):
30 def decorator(func):
31 def funcv(v):
31 def funcv(v):
32 def f():
32 def f():
33 return func(v)
33 return func(v)
34 return f
34 return f
35 for v in vers:
35 for v in vers:
36 v = str(v)
36 v = str(v)
37 f = funcv(v)
37 f = funcv(v)
38 checks['%s%s' % (name, v.replace('.', ''))] = (f, desc % v)
38 checks['%s%s' % (name, v.replace('.', ''))] = (f, desc % v)
39 return func
39 return func
40 return decorator
40 return decorator
41
41
42 def checkfeatures(features):
42 def checkfeatures(features):
43 result = {
43 result = {
44 'error': [],
44 'error': [],
45 'missing': [],
45 'missing': [],
46 'skipped': [],
46 'skipped': [],
47 }
47 }
48
48
49 for feature in features:
49 for feature in features:
50 negate = feature.startswith('no-')
50 negate = feature.startswith('no-')
51 if negate:
51 if negate:
52 feature = feature[3:]
52 feature = feature[3:]
53
53
54 if feature not in checks:
54 if feature not in checks:
55 result['missing'].append(feature)
55 result['missing'].append(feature)
56 continue
56 continue
57
57
58 check, desc = checks[feature]
58 check, desc = checks[feature]
59 try:
59 try:
60 available = check()
60 available = check()
61 except Exception:
61 except Exception:
62 result['error'].append('hghave check failed: %s' % feature)
62 result['error'].append('hghave check failed: %s' % feature)
63 continue
63 continue
64
64
65 if not negate and not available:
65 if not negate and not available:
66 result['skipped'].append('missing feature: %s' % desc)
66 result['skipped'].append('missing feature: %s' % desc)
67 elif negate and available:
67 elif negate and available:
68 result['skipped'].append('system supports %s' % desc)
68 result['skipped'].append('system supports %s' % desc)
69
69
70 return result
70 return result
71
71
72 def require(features):
72 def require(features):
73 """Require that features are available, exiting if not."""
73 """Require that features are available, exiting if not."""
74 result = checkfeatures(features)
74 result = checkfeatures(features)
75
75
76 for missing in result['missing']:
76 for missing in result['missing']:
77 sys.stderr.write('skipped: unknown feature: %s\n' % missing)
77 sys.stderr.write('skipped: unknown feature: %s\n' % missing)
78 for msg in result['skipped']:
78 for msg in result['skipped']:
79 sys.stderr.write('skipped: %s\n' % msg)
79 sys.stderr.write('skipped: %s\n' % msg)
80 for msg in result['error']:
80 for msg in result['error']:
81 sys.stderr.write('%s\n' % msg)
81 sys.stderr.write('%s\n' % msg)
82
82
83 if result['missing']:
83 if result['missing']:
84 sys.exit(2)
84 sys.exit(2)
85
85
86 if result['skipped'] or result['error']:
86 if result['skipped'] or result['error']:
87 sys.exit(1)
87 sys.exit(1)
88
88
89 def matchoutput(cmd, regexp, ignorestatus=False):
89 def matchoutput(cmd, regexp, ignorestatus=False):
90 """Return the match object if cmd executes successfully and its output
90 """Return the match object if cmd executes successfully and its output
91 is matched by the supplied regular expression.
91 is matched by the supplied regular expression.
92 """
92 """
93 r = re.compile(regexp)
93 r = re.compile(regexp)
94 try:
94 try:
95 p = subprocess.Popen(
95 p = subprocess.Popen(
96 cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
96 cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
97 except OSError as e:
97 except OSError as e:
98 if e.errno != errno.ENOENT:
98 if e.errno != errno.ENOENT:
99 raise
99 raise
100 ret = -1
100 ret = -1
101 ret = p.wait()
101 ret = p.wait()
102 s = p.stdout.read()
102 s = p.stdout.read()
103 return (ignorestatus or not ret) and r.search(s)
103 return (ignorestatus or not ret) and r.search(s)
104
104
105 @check("baz", "GNU Arch baz client")
105 @check("baz", "GNU Arch baz client")
106 def has_baz():
106 def has_baz():
107 return matchoutput('baz --version 2>&1', br'baz Bazaar version')
107 return matchoutput('baz --version 2>&1', br'baz Bazaar version')
108
108
109 @check("bzr", "Canonical's Bazaar client")
109 @check("bzr", "Canonical's Bazaar client")
110 def has_bzr():
110 def has_bzr():
111 try:
111 try:
112 import bzrlib
112 import bzrlib
113 import bzrlib.bzrdir
113 import bzrlib.bzrdir
114 import bzrlib.errors
114 import bzrlib.errors
115 import bzrlib.revision
115 import bzrlib.revision
116 import bzrlib.revisionspec
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():
282 m = matchoutput('git --version 2>&1', br'git version (\d+)\.(\d+)')
283 if not m:
284 return (0, 0)
285 return (int(m.group(1)), int(m.group(2)))
286
287 @checkvers("git", "git client (with ext::sh support) version >= %s", (1.9,))
288 def has_git_range(v):
289 major, minor = v.split('.')[0:2]
290 return getgitversion() >= (int(major), int(minor))
291
281 @check("docutils", "Docutils text processing library")
292 @check("docutils", "Docutils text processing library")
282 def has_docutils():
293 def has_docutils():
283 try:
294 try:
284 import docutils.core
295 import docutils.core
285 docutils.core.publish_cmdline # silence unused import
296 docutils.core.publish_cmdline # silence unused import
286 return True
297 return True
287 except ImportError:
298 except ImportError:
288 return False
299 return False
289
300
290 def getsvnversion():
301 def getsvnversion():
291 m = matchoutput('svn --version --quiet 2>&1', br'^(\d+)\.(\d+)')
302 m = matchoutput('svn --version --quiet 2>&1', br'^(\d+)\.(\d+)')
292 if not m:
303 if not m:
293 return (0, 0)
304 return (0, 0)
294 return (int(m.group(1)), int(m.group(2)))
305 return (int(m.group(1)), int(m.group(2)))
295
306
296 @checkvers("svn", "subversion client and admin tools >= %s", (1.3, 1.5))
307 @checkvers("svn", "subversion client and admin tools >= %s", (1.3, 1.5))
297 def has_svn_range(v):
308 def has_svn_range(v):
298 major, minor = v.split('.')[0:2]
309 major, minor = v.split('.')[0:2]
299 return getsvnversion() >= (int(major), int(minor))
310 return getsvnversion() >= (int(major), int(minor))
300
311
301 @check("svn", "subversion client and admin tools")
312 @check("svn", "subversion client and admin tools")
302 def has_svn():
313 def has_svn():
303 return matchoutput('svn --version 2>&1', br'^svn, version') and \
314 return matchoutput('svn --version 2>&1', br'^svn, version') and \
304 matchoutput('svnadmin --version 2>&1', br'^svnadmin, version')
315 matchoutput('svnadmin --version 2>&1', br'^svnadmin, version')
305
316
306 @check("svn-bindings", "subversion python bindings")
317 @check("svn-bindings", "subversion python bindings")
307 def has_svn_bindings():
318 def has_svn_bindings():
308 try:
319 try:
309 import svn.core
320 import svn.core
310 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
321 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
311 if version < (1, 4):
322 if version < (1, 4):
312 return False
323 return False
313 return True
324 return True
314 except ImportError:
325 except ImportError:
315 return False
326 return False
316
327
317 @check("p4", "Perforce server and client")
328 @check("p4", "Perforce server and client")
318 def has_p4():
329 def has_p4():
319 return (matchoutput('p4 -V', br'Rev\. P4/') and
330 return (matchoutput('p4 -V', br'Rev\. P4/') and
320 matchoutput('p4d -V', br'Rev\. P4D/'))
331 matchoutput('p4d -V', br'Rev\. P4D/'))
321
332
322 @check("symlink", "symbolic links")
333 @check("symlink", "symbolic links")
323 def has_symlink():
334 def has_symlink():
324 if getattr(os, "symlink", None) is None:
335 if getattr(os, "symlink", None) is None:
325 return False
336 return False
326 name = tempfile.mktemp(dir='.', prefix=tempprefix)
337 name = tempfile.mktemp(dir='.', prefix=tempprefix)
327 try:
338 try:
328 os.symlink(".", name)
339 os.symlink(".", name)
329 os.unlink(name)
340 os.unlink(name)
330 return True
341 return True
331 except (OSError, AttributeError):
342 except (OSError, AttributeError):
332 return False
343 return False
333
344
334 @check("hardlink", "hardlinks")
345 @check("hardlink", "hardlinks")
335 def has_hardlink():
346 def has_hardlink():
336 from mercurial import util
347 from mercurial import util
337 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
348 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
338 os.close(fh)
349 os.close(fh)
339 name = tempfile.mktemp(dir='.', prefix=tempprefix)
350 name = tempfile.mktemp(dir='.', prefix=tempprefix)
340 try:
351 try:
341 util.oslink(fn, name)
352 util.oslink(fn, name)
342 os.unlink(name)
353 os.unlink(name)
343 return True
354 return True
344 except OSError:
355 except OSError:
345 return False
356 return False
346 finally:
357 finally:
347 os.unlink(fn)
358 os.unlink(fn)
348
359
349 @check("hardlink-whitelisted", "hardlinks on whitelisted filesystems")
360 @check("hardlink-whitelisted", "hardlinks on whitelisted filesystems")
350 def has_hardlink_whitelisted():
361 def has_hardlink_whitelisted():
351 from mercurial import util
362 from mercurial import util
352 try:
363 try:
353 fstype = util.getfstype('.')
364 fstype = util.getfstype('.')
354 except OSError:
365 except OSError:
355 return False
366 return False
356 return fstype in util._hardlinkfswhitelist
367 return fstype in util._hardlinkfswhitelist
357
368
358 @check("rmcwd", "can remove current working directory")
369 @check("rmcwd", "can remove current working directory")
359 def has_rmcwd():
370 def has_rmcwd():
360 ocwd = os.getcwd()
371 ocwd = os.getcwd()
361 temp = tempfile.mkdtemp(dir='.', prefix=tempprefix)
372 temp = tempfile.mkdtemp(dir='.', prefix=tempprefix)
362 try:
373 try:
363 os.chdir(temp)
374 os.chdir(temp)
364 # On Linux, 'rmdir .' isn't allowed, but the other names are okay.
375 # On Linux, 'rmdir .' isn't allowed, but the other names are okay.
365 # On Solaris and Windows, the cwd can't be removed by any names.
376 # On Solaris and Windows, the cwd can't be removed by any names.
366 os.rmdir(os.getcwd())
377 os.rmdir(os.getcwd())
367 return True
378 return True
368 except OSError:
379 except OSError:
369 return False
380 return False
370 finally:
381 finally:
371 os.chdir(ocwd)
382 os.chdir(ocwd)
372 # clean up temp dir on platforms where cwd can't be removed
383 # clean up temp dir on platforms where cwd can't be removed
373 try:
384 try:
374 os.rmdir(temp)
385 os.rmdir(temp)
375 except OSError:
386 except OSError:
376 pass
387 pass
377
388
378 @check("tla", "GNU Arch tla client")
389 @check("tla", "GNU Arch tla client")
379 def has_tla():
390 def has_tla():
380 return matchoutput('tla --version 2>&1', br'The GNU Arch Revision')
391 return matchoutput('tla --version 2>&1', br'The GNU Arch Revision')
381
392
382 @check("gpg", "gpg client")
393 @check("gpg", "gpg client")
383 def has_gpg():
394 def has_gpg():
384 return matchoutput('gpg --version 2>&1', br'GnuPG')
395 return matchoutput('gpg --version 2>&1', br'GnuPG')
385
396
386 @check("gpg2", "gpg client v2")
397 @check("gpg2", "gpg client v2")
387 def has_gpg2():
398 def has_gpg2():
388 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.')
399 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.')
389
400
390 @check("gpg21", "gpg client v2.1+")
401 @check("gpg21", "gpg client v2.1+")
391 def has_gpg21():
402 def has_gpg21():
392 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.(?!0)')
403 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.(?!0)')
393
404
394 @check("unix-permissions", "unix-style permissions")
405 @check("unix-permissions", "unix-style permissions")
395 def has_unix_permissions():
406 def has_unix_permissions():
396 d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
407 d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
397 try:
408 try:
398 fname = os.path.join(d, 'foo')
409 fname = os.path.join(d, 'foo')
399 for umask in (0o77, 0o07, 0o22):
410 for umask in (0o77, 0o07, 0o22):
400 os.umask(umask)
411 os.umask(umask)
401 f = open(fname, 'w')
412 f = open(fname, 'w')
402 f.close()
413 f.close()
403 mode = os.stat(fname).st_mode
414 mode = os.stat(fname).st_mode
404 os.unlink(fname)
415 os.unlink(fname)
405 if mode & 0o777 != ~umask & 0o666:
416 if mode & 0o777 != ~umask & 0o666:
406 return False
417 return False
407 return True
418 return True
408 finally:
419 finally:
409 os.rmdir(d)
420 os.rmdir(d)
410
421
411 @check("unix-socket", "AF_UNIX socket family")
422 @check("unix-socket", "AF_UNIX socket family")
412 def has_unix_socket():
423 def has_unix_socket():
413 return getattr(socket, 'AF_UNIX', None) is not None
424 return getattr(socket, 'AF_UNIX', None) is not None
414
425
415 @check("root", "root permissions")
426 @check("root", "root permissions")
416 def has_root():
427 def has_root():
417 return getattr(os, 'geteuid', None) and os.geteuid() == 0
428 return getattr(os, 'geteuid', None) and os.geteuid() == 0
418
429
419 @check("pyflakes", "Pyflakes python linter")
430 @check("pyflakes", "Pyflakes python linter")
420 def has_pyflakes():
431 def has_pyflakes():
421 return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"",
432 return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"",
422 br"<stdin>:1: 're' imported but unused",
433 br"<stdin>:1: 're' imported but unused",
423 True)
434 True)
424
435
425 @check("pylint", "Pylint python linter")
436 @check("pylint", "Pylint python linter")
426 def has_pylint():
437 def has_pylint():
427 return matchoutput("pylint --help",
438 return matchoutput("pylint --help",
428 br"Usage: pylint",
439 br"Usage: pylint",
429 True)
440 True)
430
441
431 @check("pygments", "Pygments source highlighting library")
442 @check("pygments", "Pygments source highlighting library")
432 def has_pygments():
443 def has_pygments():
433 try:
444 try:
434 import pygments
445 import pygments
435 pygments.highlight # silence unused import warning
446 pygments.highlight # silence unused import warning
436 return True
447 return True
437 except ImportError:
448 except ImportError:
438 return False
449 return False
439
450
440 @check("outer-repo", "outer repo")
451 @check("outer-repo", "outer repo")
441 def has_outer_repo():
452 def has_outer_repo():
442 # failing for other reasons than 'no repo' imply that there is a repo
453 # failing for other reasons than 'no repo' imply that there is a repo
443 return not matchoutput('hg root 2>&1',
454 return not matchoutput('hg root 2>&1',
444 br'abort: no repository found', True)
455 br'abort: no repository found', True)
445
456
446 @check("ssl", "ssl module available")
457 @check("ssl", "ssl module available")
447 def has_ssl():
458 def has_ssl():
448 try:
459 try:
449 import ssl
460 import ssl
450 ssl.CERT_NONE
461 ssl.CERT_NONE
451 return True
462 return True
452 except ImportError:
463 except ImportError:
453 return False
464 return False
454
465
455 @check("sslcontext", "python >= 2.7.9 ssl")
466 @check("sslcontext", "python >= 2.7.9 ssl")
456 def has_sslcontext():
467 def has_sslcontext():
457 try:
468 try:
458 import ssl
469 import ssl
459 ssl.SSLContext
470 ssl.SSLContext
460 return True
471 return True
461 except (ImportError, AttributeError):
472 except (ImportError, AttributeError):
462 return False
473 return False
463
474
464 @check("defaultcacerts", "can verify SSL certs by system's CA certs store")
475 @check("defaultcacerts", "can verify SSL certs by system's CA certs store")
465 def has_defaultcacerts():
476 def has_defaultcacerts():
466 from mercurial import sslutil, ui as uimod
477 from mercurial import sslutil, ui as uimod
467 ui = uimod.ui.load()
478 ui = uimod.ui.load()
468 return sslutil._defaultcacerts(ui) or sslutil._canloaddefaultcerts
479 return sslutil._defaultcacerts(ui) or sslutil._canloaddefaultcerts
469
480
470 @check("defaultcacertsloaded", "detected presence of loaded system CA certs")
481 @check("defaultcacertsloaded", "detected presence of loaded system CA certs")
471 def has_defaultcacertsloaded():
482 def has_defaultcacertsloaded():
472 import ssl
483 import ssl
473 from mercurial import sslutil, ui as uimod
484 from mercurial import sslutil, ui as uimod
474
485
475 if not has_defaultcacerts():
486 if not has_defaultcacerts():
476 return False
487 return False
477 if not has_sslcontext():
488 if not has_sslcontext():
478 return False
489 return False
479
490
480 ui = uimod.ui.load()
491 ui = uimod.ui.load()
481 cafile = sslutil._defaultcacerts(ui)
492 cafile = sslutil._defaultcacerts(ui)
482 ctx = ssl.create_default_context()
493 ctx = ssl.create_default_context()
483 if cafile:
494 if cafile:
484 ctx.load_verify_locations(cafile=cafile)
495 ctx.load_verify_locations(cafile=cafile)
485 else:
496 else:
486 ctx.load_default_certs()
497 ctx.load_default_certs()
487
498
488 return len(ctx.get_ca_certs()) > 0
499 return len(ctx.get_ca_certs()) > 0
489
500
490 @check("tls1.2", "TLS 1.2 protocol support")
501 @check("tls1.2", "TLS 1.2 protocol support")
491 def has_tls1_2():
502 def has_tls1_2():
492 from mercurial import sslutil
503 from mercurial import sslutil
493 return 'tls1.2' in sslutil.supportedprotocols
504 return 'tls1.2' in sslutil.supportedprotocols
494
505
495 @check("windows", "Windows")
506 @check("windows", "Windows")
496 def has_windows():
507 def has_windows():
497 return os.name == 'nt'
508 return os.name == 'nt'
498
509
499 @check("system-sh", "system() uses sh")
510 @check("system-sh", "system() uses sh")
500 def has_system_sh():
511 def has_system_sh():
501 return os.name != 'nt'
512 return os.name != 'nt'
502
513
503 @check("serve", "platform and python can manage 'hg serve -d'")
514 @check("serve", "platform and python can manage 'hg serve -d'")
504 def has_serve():
515 def has_serve():
505 return True
516 return True
506
517
507 @check("test-repo", "running tests from repository")
518 @check("test-repo", "running tests from repository")
508 def has_test_repo():
519 def has_test_repo():
509 t = os.environ["TESTDIR"]
520 t = os.environ["TESTDIR"]
510 return os.path.isdir(os.path.join(t, "..", ".hg"))
521 return os.path.isdir(os.path.join(t, "..", ".hg"))
511
522
512 @check("tic", "terminfo compiler and curses module")
523 @check("tic", "terminfo compiler and curses module")
513 def has_tic():
524 def has_tic():
514 try:
525 try:
515 import curses
526 import curses
516 curses.COLOR_BLUE
527 curses.COLOR_BLUE
517 return matchoutput('test -x "`which tic`"', br'')
528 return matchoutput('test -x "`which tic`"', br'')
518 except ImportError:
529 except ImportError:
519 return False
530 return False
520
531
521 @check("msys", "Windows with MSYS")
532 @check("msys", "Windows with MSYS")
522 def has_msys():
533 def has_msys():
523 return os.getenv('MSYSTEM')
534 return os.getenv('MSYSTEM')
524
535
525 @check("aix", "AIX")
536 @check("aix", "AIX")
526 def has_aix():
537 def has_aix():
527 return sys.platform.startswith("aix")
538 return sys.platform.startswith("aix")
528
539
529 @check("osx", "OS X")
540 @check("osx", "OS X")
530 def has_osx():
541 def has_osx():
531 return sys.platform == 'darwin'
542 return sys.platform == 'darwin'
532
543
533 @check("osxpackaging", "OS X packaging tools")
544 @check("osxpackaging", "OS X packaging tools")
534 def has_osxpackaging():
545 def has_osxpackaging():
535 try:
546 try:
536 return (matchoutput('pkgbuild', br'Usage: pkgbuild ', ignorestatus=1)
547 return (matchoutput('pkgbuild', br'Usage: pkgbuild ', ignorestatus=1)
537 and matchoutput(
548 and matchoutput(
538 'productbuild', br'Usage: productbuild ',
549 'productbuild', br'Usage: productbuild ',
539 ignorestatus=1)
550 ignorestatus=1)
540 and matchoutput('lsbom', br'Usage: lsbom', ignorestatus=1)
551 and matchoutput('lsbom', br'Usage: lsbom', ignorestatus=1)
541 and matchoutput(
552 and matchoutput(
542 'xar --help', br'Usage: xar', ignorestatus=1))
553 'xar --help', br'Usage: xar', ignorestatus=1))
543 except ImportError:
554 except ImportError:
544 return False
555 return False
545
556
546 @check("docker", "docker support")
557 @check("docker", "docker support")
547 def has_docker():
558 def has_docker():
548 pat = br'A self-sufficient runtime for'
559 pat = br'A self-sufficient runtime for'
549 if matchoutput('docker --help', pat):
560 if matchoutput('docker --help', pat):
550 if 'linux' not in sys.platform:
561 if 'linux' not in sys.platform:
551 # TODO: in theory we should be able to test docker-based
562 # TODO: in theory we should be able to test docker-based
552 # package creation on non-linux using boot2docker, but in
563 # package creation on non-linux using boot2docker, but in
553 # practice that requires extra coordination to make sure
564 # practice that requires extra coordination to make sure
554 # $TESTTEMP is going to be visible at the same path to the
565 # $TESTTEMP is going to be visible at the same path to the
555 # boot2docker VM. If we figure out how to verify that, we
566 # boot2docker VM. If we figure out how to verify that, we
556 # can use the following instead of just saying False:
567 # can use the following instead of just saying False:
557 # return 'DOCKER_HOST' in os.environ
568 # return 'DOCKER_HOST' in os.environ
558 return False
569 return False
559
570
560 return True
571 return True
561 return False
572 return False
562
573
563 @check("debhelper", "debian packaging tools")
574 @check("debhelper", "debian packaging tools")
564 def has_debhelper():
575 def has_debhelper():
565 dpkg = matchoutput('dpkg --version',
576 dpkg = matchoutput('dpkg --version',
566 br"Debian `dpkg' package management program")
577 br"Debian `dpkg' package management program")
567 dh = matchoutput('dh --help',
578 dh = matchoutput('dh --help',
568 br'dh is a part of debhelper.', ignorestatus=True)
579 br'dh is a part of debhelper.', ignorestatus=True)
569 dh_py2 = matchoutput('dh_python2 --help',
580 dh_py2 = matchoutput('dh_python2 --help',
570 br'other supported Python versions')
581 br'other supported Python versions')
571 return dpkg and dh and dh_py2
582 return dpkg and dh and dh_py2
572
583
573 @check("demandimport", "demandimport enabled")
584 @check("demandimport", "demandimport enabled")
574 def has_demandimport():
585 def has_demandimport():
575 return os.environ.get('HGDEMANDIMPORT') != 'disable'
586 return os.environ.get('HGDEMANDIMPORT') != 'disable'
576
587
577 @check("absimport", "absolute_import in __future__")
588 @check("absimport", "absolute_import in __future__")
578 def has_absimport():
589 def has_absimport():
579 import __future__
590 import __future__
580 from mercurial import util
591 from mercurial import util
581 return util.safehasattr(__future__, "absolute_import")
592 return util.safehasattr(__future__, "absolute_import")
582
593
583 @check("py3k", "running with Python 3.x")
594 @check("py3k", "running with Python 3.x")
584 def has_py3k():
595 def has_py3k():
585 return 3 == sys.version_info[0]
596 return 3 == sys.version_info[0]
586
597
587 @check("py3exe", "a Python 3.x interpreter is available")
598 @check("py3exe", "a Python 3.x interpreter is available")
588 def has_python3exe():
599 def has_python3exe():
589 return 'PYTHON3' in os.environ
600 return 'PYTHON3' in os.environ
590
601
591 @check("py3pygments", "Pygments available on Python 3.x")
602 @check("py3pygments", "Pygments available on Python 3.x")
592 def has_py3pygments():
603 def has_py3pygments():
593 if has_py3k():
604 if has_py3k():
594 return has_pygments()
605 return has_pygments()
595 elif has_python3exe():
606 elif has_python3exe():
596 # just check exit status (ignoring output)
607 # just check exit status (ignoring output)
597 py3 = os.environ['PYTHON3']
608 py3 = os.environ['PYTHON3']
598 return matchoutput('%s -c "import pygments"' % py3, br'')
609 return matchoutput('%s -c "import pygments"' % py3, br'')
599 return False
610 return False
600
611
601 @check("pure", "running with pure Python code")
612 @check("pure", "running with pure Python code")
602 def has_pure():
613 def has_pure():
603 return any([
614 return any([
604 os.environ.get("HGMODULEPOLICY") == "py",
615 os.environ.get("HGMODULEPOLICY") == "py",
605 os.environ.get("HGTEST_RUN_TESTS_PURE") == "--pure",
616 os.environ.get("HGTEST_RUN_TESTS_PURE") == "--pure",
606 ])
617 ])
607
618
608 @check("slow", "allow slow tests (use --allow-slow-tests)")
619 @check("slow", "allow slow tests (use --allow-slow-tests)")
609 def has_slow():
620 def has_slow():
610 return os.environ.get('HGTEST_SLOW') == 'slow'
621 return os.environ.get('HGTEST_SLOW') == 'slow'
611
622
612 @check("hypothesis", "Hypothesis automated test generation")
623 @check("hypothesis", "Hypothesis automated test generation")
613 def has_hypothesis():
624 def has_hypothesis():
614 try:
625 try:
615 import hypothesis
626 import hypothesis
616 hypothesis.given
627 hypothesis.given
617 return True
628 return True
618 except ImportError:
629 except ImportError:
619 return False
630 return False
620
631
621 @check("unziplinks", "unzip(1) understands and extracts symlinks")
632 @check("unziplinks", "unzip(1) understands and extracts symlinks")
622 def unzip_understands_symlinks():
633 def unzip_understands_symlinks():
623 return matchoutput('unzip --help', br'Info-ZIP')
634 return matchoutput('unzip --help', br'Info-ZIP')
624
635
625 @check("zstd", "zstd Python module available")
636 @check("zstd", "zstd Python module available")
626 def has_zstd():
637 def has_zstd():
627 try:
638 try:
628 import mercurial.zstd
639 import mercurial.zstd
629 mercurial.zstd.__version__
640 mercurial.zstd.__version__
630 return True
641 return True
631 except ImportError:
642 except ImportError:
632 return False
643 return False
633
644
634 @check("devfull", "/dev/full special file")
645 @check("devfull", "/dev/full special file")
635 def has_dev_full():
646 def has_dev_full():
636 return os.path.exists('/dev/full')
647 return os.path.exists('/dev/full')
637
648
638 @check("virtualenv", "Python virtualenv support")
649 @check("virtualenv", "Python virtualenv support")
639 def has_virtualenv():
650 def has_virtualenv():
640 try:
651 try:
641 import virtualenv
652 import virtualenv
642 virtualenv.ACTIVATE_SH
653 virtualenv.ACTIVATE_SH
643 return True
654 return True
644 except ImportError:
655 except ImportError:
645 return False
656 return False
646
657
647 @check("fsmonitor", "running tests with fsmonitor")
658 @check("fsmonitor", "running tests with fsmonitor")
648 def has_fsmonitor():
659 def has_fsmonitor():
649 return 'HGFSMONITOR_TESTS' in os.environ
660 return 'HGFSMONITOR_TESTS' in os.environ
General Comments 0
You need to be logged in to leave comments. Login now