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