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