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