##// END OF EJS Templates
hghave.py: fix matchoutput documentation
timeless -
r27114:a636a46f default
parent child Browse files
Show More
@@ -1,474 +1,474 b''
1 1 import errno
2 2 import os
3 3 import re
4 4 import socket
5 5 import stat
6 6 import subprocess
7 7 import sys
8 8 import tempfile
9 9
10 10 tempprefix = 'hg-hghave-'
11 11
12 12 checks = {
13 13 "true": (lambda: True, "yak shaving"),
14 14 "false": (lambda: False, "nail clipper"),
15 15 }
16 16
17 17 def check(name, desc):
18 18 def decorator(func):
19 19 checks[name] = (func, desc)
20 20 return func
21 21 return decorator
22 22
23 23 def checkfeatures(features):
24 24 result = {
25 25 'error': [],
26 26 'missing': [],
27 27 'skipped': [],
28 28 }
29 29
30 30 for feature in features:
31 31 negate = feature.startswith('no-')
32 32 if negate:
33 33 feature = feature[3:]
34 34
35 35 if feature not in checks:
36 36 result['missing'].append(feature)
37 37 continue
38 38
39 39 check, desc = checks[feature]
40 40 try:
41 41 available = check()
42 42 except Exception:
43 43 result['error'].append('hghave check failed: %s' % feature)
44 44 continue
45 45
46 46 if not negate and not available:
47 47 result['skipped'].append('missing feature: %s' % desc)
48 48 elif negate and available:
49 49 result['skipped'].append('system supports %s' % desc)
50 50
51 51 return result
52 52
53 53 def require(features):
54 54 """Require that features are available, exiting if not."""
55 55 result = checkfeatures(features)
56 56
57 57 for missing in result['missing']:
58 58 sys.stderr.write('skipped: unknown feature: %s\n' % missing)
59 59 for msg in result['skipped']:
60 60 sys.stderr.write('skipped: %s\n' % msg)
61 61 for msg in result['error']:
62 62 sys.stderr.write('%s\n' % msg)
63 63
64 64 if result['missing']:
65 65 sys.exit(2)
66 66
67 67 if result['skipped'] or result['error']:
68 68 sys.exit(1)
69 69
70 70 def matchoutput(cmd, regexp, ignorestatus=False):
71 """Return True if cmd executes successfully and its output
71 """Return the match object if cmd executes successfully and its output
72 72 is matched by the supplied regular expression.
73 73 """
74 74 r = re.compile(regexp)
75 75 try:
76 76 p = subprocess.Popen(
77 77 cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
78 78 except OSError as e:
79 79 if e.errno != errno.ENOENT:
80 80 raise
81 81 ret = -1
82 82 ret = p.wait()
83 83 s = p.stdout.read()
84 84 return (ignorestatus or not ret) and r.search(s)
85 85
86 86 @check("baz", "GNU Arch baz client")
87 87 def has_baz():
88 88 return matchoutput('baz --version 2>&1', r'baz Bazaar version')
89 89
90 90 @check("bzr", "Canonical's Bazaar client")
91 91 def has_bzr():
92 92 try:
93 93 import bzrlib
94 94 return bzrlib.__doc__ is not None
95 95 except ImportError:
96 96 return False
97 97
98 98 @check("bzr114", "Canonical's Bazaar client >= 1.14")
99 99 def has_bzr114():
100 100 try:
101 101 import bzrlib
102 102 return (bzrlib.__doc__ is not None
103 103 and bzrlib.version_info[:2] >= (1, 14))
104 104 except ImportError:
105 105 return False
106 106
107 107 @check("cvs", "cvs client/server")
108 108 def has_cvs():
109 109 re = r'Concurrent Versions System.*?server'
110 110 return matchoutput('cvs --version 2>&1', re) and not has_msys()
111 111
112 112 @check("cvs112", "cvs client/server >= 1.12")
113 113 def has_cvs112():
114 114 re = r'Concurrent Versions System \(CVS\) 1.12.*?server'
115 115 return matchoutput('cvs --version 2>&1', re) and not has_msys()
116 116
117 117 @check("darcs", "darcs client")
118 118 def has_darcs():
119 119 return matchoutput('darcs --version', r'2\.[2-9]', True)
120 120
121 121 @check("mtn", "monotone client (>= 1.0)")
122 122 def has_mtn():
123 123 return matchoutput('mtn --version', r'monotone', True) and not matchoutput(
124 124 'mtn --version', r'monotone 0\.', True)
125 125
126 126 @check("eol-in-paths", "end-of-lines in paths")
127 127 def has_eol_in_paths():
128 128 try:
129 129 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r')
130 130 os.close(fd)
131 131 os.remove(path)
132 132 return True
133 133 except (IOError, OSError):
134 134 return False
135 135
136 136 @check("execbit", "executable bit")
137 137 def has_executablebit():
138 138 try:
139 139 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
140 140 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
141 141 try:
142 142 os.close(fh)
143 143 m = os.stat(fn).st_mode & 0o777
144 144 new_file_has_exec = m & EXECFLAGS
145 145 os.chmod(fn, m ^ EXECFLAGS)
146 146 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0o777) == m)
147 147 finally:
148 148 os.unlink(fn)
149 149 except (IOError, OSError):
150 150 # we don't care, the user probably won't be able to commit anyway
151 151 return False
152 152 return not (new_file_has_exec or exec_flags_cannot_flip)
153 153
154 154 @check("icasefs", "case insensitive file system")
155 155 def has_icasefs():
156 156 # Stolen from mercurial.util
157 157 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
158 158 os.close(fd)
159 159 try:
160 160 s1 = os.stat(path)
161 161 d, b = os.path.split(path)
162 162 p2 = os.path.join(d, b.upper())
163 163 if path == p2:
164 164 p2 = os.path.join(d, b.lower())
165 165 try:
166 166 s2 = os.stat(p2)
167 167 return s2 == s1
168 168 except OSError:
169 169 return False
170 170 finally:
171 171 os.remove(path)
172 172
173 173 @check("fifo", "named pipes")
174 174 def has_fifo():
175 175 if getattr(os, "mkfifo", None) is None:
176 176 return False
177 177 name = tempfile.mktemp(dir='.', prefix=tempprefix)
178 178 try:
179 179 os.mkfifo(name)
180 180 os.unlink(name)
181 181 return True
182 182 except OSError:
183 183 return False
184 184
185 185 @check("killdaemons", 'killdaemons.py support')
186 186 def has_killdaemons():
187 187 return True
188 188
189 189 @check("cacheable", "cacheable filesystem")
190 190 def has_cacheable_fs():
191 191 from mercurial import util
192 192
193 193 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
194 194 os.close(fd)
195 195 try:
196 196 return util.cachestat(path).cacheable()
197 197 finally:
198 198 os.remove(path)
199 199
200 200 @check("lsprof", "python lsprof module")
201 201 def has_lsprof():
202 202 try:
203 203 import _lsprof
204 204 _lsprof.Profiler # silence unused import warning
205 205 return True
206 206 except ImportError:
207 207 return False
208 208
209 209 @check("gettext", "GNU Gettext (msgfmt)")
210 210 def has_gettext():
211 211 return matchoutput('msgfmt --version', 'GNU gettext-tools')
212 212
213 213 @check("git", "git command line client")
214 214 def has_git():
215 215 return matchoutput('git --version 2>&1', r'^git version')
216 216
217 217 @check("docutils", "Docutils text processing library")
218 218 def has_docutils():
219 219 try:
220 220 from docutils.core import publish_cmdline
221 221 publish_cmdline # silence unused import
222 222 return True
223 223 except ImportError:
224 224 return False
225 225
226 226 def getsvnversion():
227 227 m = matchoutput('svn --version --quiet 2>&1', r'^(\d+)\.(\d+)')
228 228 if not m:
229 229 return (0, 0)
230 230 return (int(m.group(1)), int(m.group(2)))
231 231
232 232 @check("svn15", "subversion client and admin tools >= 1.5")
233 233 def has_svn15():
234 234 return getsvnversion() >= (1, 5)
235 235
236 236 @check("svn13", "subversion client and admin tools >= 1.3")
237 237 def has_svn13():
238 238 return getsvnversion() >= (1, 3)
239 239
240 240 @check("svn", "subversion client and admin tools")
241 241 def has_svn():
242 242 return matchoutput('svn --version 2>&1', r'^svn, version') and \
243 243 matchoutput('svnadmin --version 2>&1', r'^svnadmin, version')
244 244
245 245 @check("svn-bindings", "subversion python bindings")
246 246 def has_svn_bindings():
247 247 try:
248 248 import svn.core
249 249 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
250 250 if version < (1, 4):
251 251 return False
252 252 return True
253 253 except ImportError:
254 254 return False
255 255
256 256 @check("p4", "Perforce server and client")
257 257 def has_p4():
258 258 return (matchoutput('p4 -V', r'Rev\. P4/') and
259 259 matchoutput('p4d -V', r'Rev\. P4D/'))
260 260
261 261 @check("symlink", "symbolic links")
262 262 def has_symlink():
263 263 if getattr(os, "symlink", None) is None:
264 264 return False
265 265 name = tempfile.mktemp(dir='.', prefix=tempprefix)
266 266 try:
267 267 os.symlink(".", name)
268 268 os.unlink(name)
269 269 return True
270 270 except (OSError, AttributeError):
271 271 return False
272 272
273 273 @check("hardlink", "hardlinks")
274 274 def has_hardlink():
275 275 from mercurial import util
276 276 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
277 277 os.close(fh)
278 278 name = tempfile.mktemp(dir='.', prefix=tempprefix)
279 279 try:
280 280 util.oslink(fn, name)
281 281 os.unlink(name)
282 282 return True
283 283 except OSError:
284 284 return False
285 285 finally:
286 286 os.unlink(fn)
287 287
288 288 @check("tla", "GNU Arch tla client")
289 289 def has_tla():
290 290 return matchoutput('tla --version 2>&1', r'The GNU Arch Revision')
291 291
292 292 @check("gpg", "gpg client")
293 293 def has_gpg():
294 294 return matchoutput('gpg --version 2>&1', r'GnuPG')
295 295
296 296 @check("unix-permissions", "unix-style permissions")
297 297 def has_unix_permissions():
298 298 d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
299 299 try:
300 300 fname = os.path.join(d, 'foo')
301 301 for umask in (0o77, 0o07, 0o22):
302 302 os.umask(umask)
303 303 f = open(fname, 'w')
304 304 f.close()
305 305 mode = os.stat(fname).st_mode
306 306 os.unlink(fname)
307 307 if mode & 0o777 != ~umask & 0o666:
308 308 return False
309 309 return True
310 310 finally:
311 311 os.rmdir(d)
312 312
313 313 @check("unix-socket", "AF_UNIX socket family")
314 314 def has_unix_socket():
315 315 return getattr(socket, 'AF_UNIX', None) is not None
316 316
317 317 @check("root", "root permissions")
318 318 def has_root():
319 319 return getattr(os, 'geteuid', None) and os.geteuid() == 0
320 320
321 321 @check("pyflakes", "Pyflakes python linter")
322 322 def has_pyflakes():
323 323 return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"",
324 324 r"<stdin>:1: 're' imported but unused",
325 325 True)
326 326
327 327 @check("pygments", "Pygments source highlighting library")
328 328 def has_pygments():
329 329 try:
330 330 import pygments
331 331 pygments.highlight # silence unused import warning
332 332 return True
333 333 except ImportError:
334 334 return False
335 335
336 336 @check("json", "some json module available")
337 337 def has_json():
338 338 try:
339 339 import json
340 340 json.dumps
341 341 return True
342 342 except ImportError:
343 343 try:
344 344 import simplejson as json
345 345 json.dumps
346 346 return True
347 347 except ImportError:
348 348 pass
349 349 return False
350 350
351 351 @check("outer-repo", "outer repo")
352 352 def has_outer_repo():
353 353 # failing for other reasons than 'no repo' imply that there is a repo
354 354 return not matchoutput('hg root 2>&1',
355 355 r'abort: no repository found', True)
356 356
357 357 @check("ssl", ("(python >= 2.6 ssl module and python OpenSSL) "
358 358 "OR python >= 2.7.9 ssl"))
359 359 def has_ssl():
360 360 try:
361 361 import ssl
362 362 if getattr(ssl, 'create_default_context', False):
363 363 return True
364 364 import OpenSSL
365 365 OpenSSL.SSL.Context
366 366 return True
367 367 except ImportError:
368 368 return False
369 369
370 370 @check("sslcontext", "python >= 2.7.9 ssl")
371 371 def has_sslcontext():
372 372 try:
373 373 import ssl
374 374 ssl.SSLContext
375 375 return True
376 376 except (ImportError, AttributeError):
377 377 return False
378 378
379 379 @check("defaultcacerts", "can verify SSL certs by system's CA certs store")
380 380 def has_defaultcacerts():
381 381 from mercurial import sslutil
382 382 return sslutil._defaultcacerts() != '!'
383 383
384 384 @check("windows", "Windows")
385 385 def has_windows():
386 386 return os.name == 'nt'
387 387
388 388 @check("system-sh", "system() uses sh")
389 389 def has_system_sh():
390 390 return os.name != 'nt'
391 391
392 392 @check("serve", "platform and python can manage 'hg serve -d'")
393 393 def has_serve():
394 394 return os.name != 'nt' # gross approximation
395 395
396 396 @check("test-repo", "running tests from repository")
397 397 def has_test_repo():
398 398 t = os.environ["TESTDIR"]
399 399 return os.path.isdir(os.path.join(t, "..", ".hg"))
400 400
401 401 @check("tic", "terminfo compiler and curses module")
402 402 def has_tic():
403 403 try:
404 404 import curses
405 405 curses.COLOR_BLUE
406 406 return matchoutput('test -x "`which tic`"', '')
407 407 except ImportError:
408 408 return False
409 409
410 410 @check("msys", "Windows with MSYS")
411 411 def has_msys():
412 412 return os.getenv('MSYSTEM')
413 413
414 414 @check("aix", "AIX")
415 415 def has_aix():
416 416 return sys.platform.startswith("aix")
417 417
418 418 @check("osx", "OS X")
419 419 def has_osx():
420 420 return sys.platform == 'darwin'
421 421
422 422 @check("docker", "docker support")
423 423 def has_docker():
424 424 pat = r'A self-sufficient runtime for linux containers\.'
425 425 if matchoutput('docker --help', pat):
426 426 if 'linux' not in sys.platform:
427 427 # TODO: in theory we should be able to test docker-based
428 428 # package creation on non-linux using boot2docker, but in
429 429 # practice that requires extra coordination to make sure
430 430 # $TESTTEMP is going to be visible at the same path to the
431 431 # boot2docker VM. If we figure out how to verify that, we
432 432 # can use the following instead of just saying False:
433 433 # return 'DOCKER_HOST' in os.environ
434 434 return False
435 435
436 436 return True
437 437 return False
438 438
439 439 @check("debhelper", "debian packaging tools")
440 440 def has_debhelper():
441 441 dpkg = matchoutput('dpkg --version',
442 442 "Debian `dpkg' package management program")
443 443 dh = matchoutput('dh --help',
444 444 'dh is a part of debhelper.', ignorestatus=True)
445 445 dh_py2 = matchoutput('dh_python2 --help',
446 446 'other supported Python versions')
447 447 return dpkg and dh and dh_py2
448 448
449 449 @check("absimport", "absolute_import in __future__")
450 450 def has_absimport():
451 451 import __future__
452 452 from mercurial import util
453 453 return util.safehasattr(__future__, "absolute_import")
454 454
455 455 @check("py3k", "running with Python 3.x")
456 456 def has_py3k():
457 457 return 3 == sys.version_info[0]
458 458
459 459 @check("pure", "running with pure Python code")
460 460 def has_pure():
461 461 return os.environ.get("HGTEST_RUN_TESTS_PURE") == "--pure"
462 462
463 463 @check("slow", "allow slow tests")
464 464 def has_slow():
465 465 return os.environ.get('HGTEST_SLOW') == 'slow'
466 466
467 467 @check("hypothesis", "is Hypothesis installed")
468 468 def has_hypothesis():
469 469 try:
470 470 import hypothesis
471 471 hypothesis.given
472 472 return True
473 473 except ImportError:
474 474 return False
General Comments 0
You need to be logged in to leave comments. Login now