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