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