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