|
|
#!/usr/bin/env python
|
|
|
"""Test the running system for features availability. Exit with zero
|
|
|
if all features are there, non-zero otherwise. If a feature name is
|
|
|
prefixed with "no-", the absence of feature is tested.
|
|
|
"""
|
|
|
import optparse
|
|
|
import os
|
|
|
import re
|
|
|
import sys
|
|
|
import tempfile
|
|
|
|
|
|
tempprefix = 'hg-hghave-'
|
|
|
|
|
|
def matchoutput(cmd, regexp, ignorestatus=False):
|
|
|
"""Return True if cmd executes successfully and its output
|
|
|
is matched by the supplied regular expression.
|
|
|
"""
|
|
|
r = re.compile(regexp)
|
|
|
fh = os.popen(cmd)
|
|
|
s = fh.read()
|
|
|
try:
|
|
|
ret = fh.close()
|
|
|
except IOError:
|
|
|
# Happen in Windows test environment
|
|
|
ret = 1
|
|
|
return (ignorestatus or ret is None) and r.search(s)
|
|
|
|
|
|
def has_baz():
|
|
|
return matchoutput('baz --version 2>&1', r'baz Bazaar version')
|
|
|
|
|
|
def has_bzr():
|
|
|
try:
|
|
|
import bzrlib
|
|
|
return bzrlib.__doc__ != None
|
|
|
except ImportError:
|
|
|
return False
|
|
|
|
|
|
def has_bzr114():
|
|
|
try:
|
|
|
import bzrlib
|
|
|
return (bzrlib.__doc__ != None
|
|
|
and bzrlib.version_info[:2] >= (1, 14))
|
|
|
except ImportError:
|
|
|
return False
|
|
|
|
|
|
def has_cvs():
|
|
|
re = r'Concurrent Versions System.*?server'
|
|
|
return matchoutput('cvs --version 2>&1', re)
|
|
|
|
|
|
def has_darcs():
|
|
|
return matchoutput('darcs --version', r'2\.[2-9]', True)
|
|
|
|
|
|
def has_mtn():
|
|
|
return matchoutput('mtn --version', r'monotone', True) and not matchoutput(
|
|
|
'mtn --version', r'monotone 0\.', True)
|
|
|
|
|
|
def has_eol_in_paths():
|
|
|
try:
|
|
|
fd, path = tempfile.mkstemp(prefix=tempprefix, suffix='\n\r')
|
|
|
os.close(fd)
|
|
|
os.remove(path)
|
|
|
return True
|
|
|
except:
|
|
|
return False
|
|
|
|
|
|
def has_executablebit():
|
|
|
fd, path = tempfile.mkstemp(prefix=tempprefix)
|
|
|
os.close(fd)
|
|
|
try:
|
|
|
s = os.lstat(path).st_mode
|
|
|
os.chmod(path, s | 0100)
|
|
|
return (os.lstat(path).st_mode & 0100 != 0)
|
|
|
finally:
|
|
|
os.remove(path)
|
|
|
|
|
|
def has_icasefs():
|
|
|
# Stolen from mercurial.util
|
|
|
fd, path = tempfile.mkstemp(prefix=tempprefix, dir='.')
|
|
|
os.close(fd)
|
|
|
try:
|
|
|
s1 = os.stat(path)
|
|
|
d, b = os.path.split(path)
|
|
|
p2 = os.path.join(d, b.upper())
|
|
|
if path == p2:
|
|
|
p2 = os.path.join(d, b.lower())
|
|
|
try:
|
|
|
s2 = os.stat(p2)
|
|
|
return s2 == s1
|
|
|
except:
|
|
|
return False
|
|
|
finally:
|
|
|
os.remove(path)
|
|
|
|
|
|
def has_inotify():
|
|
|
try:
|
|
|
import hgext.inotify.linux.watcher
|
|
|
return True
|
|
|
except ImportError:
|
|
|
return False
|
|
|
|
|
|
def has_fifo():
|
|
|
return hasattr(os, "mkfifo")
|
|
|
|
|
|
def has_cacheable_fs():
|
|
|
from mercurial import util
|
|
|
|
|
|
fd, path = tempfile.mkstemp(prefix=tempprefix)
|
|
|
os.close(fd)
|
|
|
try:
|
|
|
return util.cachestat(path).cacheable()
|
|
|
finally:
|
|
|
os.remove(path)
|
|
|
|
|
|
def has_lsprof():
|
|
|
try:
|
|
|
import _lsprof
|
|
|
return True
|
|
|
except ImportError:
|
|
|
return False
|
|
|
|
|
|
def has_gettext():
|
|
|
return matchoutput('msgfmt --version', 'GNU gettext-tools')
|
|
|
|
|
|
def has_git():
|
|
|
return matchoutput('git --version 2>&1', r'^git version')
|
|
|
|
|
|
def has_docutils():
|
|
|
try:
|
|
|
from docutils.core import publish_cmdline
|
|
|
return True
|
|
|
except ImportError:
|
|
|
return False
|
|
|
|
|
|
def getsvnversion():
|
|
|
m = matchoutput('svn --version 2>&1', r'^svn,\s+version\s+(\d+)\.(\d+)')
|
|
|
if not m:
|
|
|
return (0, 0)
|
|
|
return (int(m.group(1)), int(m.group(2)))
|
|
|
|
|
|
def has_svn15():
|
|
|
return getsvnversion() >= (1, 5)
|
|
|
|
|
|
def has_svn13():
|
|
|
return getsvnversion() >= (1, 3)
|
|
|
|
|
|
def has_svn():
|
|
|
return matchoutput('svn --version 2>&1', r'^svn, version') and \
|
|
|
matchoutput('svnadmin --version 2>&1', r'^svnadmin, version')
|
|
|
|
|
|
def has_svn_bindings():
|
|
|
try:
|
|
|
import svn.core
|
|
|
version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
|
|
|
if version < (1, 4):
|
|
|
return False
|
|
|
return True
|
|
|
except ImportError:
|
|
|
return False
|
|
|
|
|
|
def has_p4():
|
|
|
return matchoutput('p4 -V', r'Rev\. P4/') and matchoutput('p4d -V', r'Rev\. P4D/')
|
|
|
|
|
|
def has_symlink():
|
|
|
return hasattr(os, "symlink") # FIXME: should also check file system and os
|
|
|
|
|
|
def has_tla():
|
|
|
return matchoutput('tla --version 2>&1', r'The GNU Arch Revision')
|
|
|
|
|
|
def has_gpg():
|
|
|
return matchoutput('gpg --version 2>&1', r'GnuPG')
|
|
|
|
|
|
def has_unix_permissions():
|
|
|
d = tempfile.mkdtemp(prefix=tempprefix, dir=".")
|
|
|
try:
|
|
|
fname = os.path.join(d, 'foo')
|
|
|
for umask in (077, 007, 022):
|
|
|
os.umask(umask)
|
|
|
f = open(fname, 'w')
|
|
|
f.close()
|
|
|
mode = os.stat(fname).st_mode
|
|
|
os.unlink(fname)
|
|
|
if mode & 0777 != ~umask & 0666:
|
|
|
return False
|
|
|
return True
|
|
|
finally:
|
|
|
os.rmdir(d)
|
|
|
|
|
|
def has_pyflakes():
|
|
|
return matchoutput('echo "import re" 2>&1 | pyflakes',
|
|
|
r"<stdin>:1: 're' imported but unused",
|
|
|
True)
|
|
|
|
|
|
def has_pygments():
|
|
|
try:
|
|
|
import pygments
|
|
|
return True
|
|
|
except ImportError:
|
|
|
return False
|
|
|
|
|
|
def has_outer_repo():
|
|
|
return matchoutput('hg root 2>&1', r'')
|
|
|
|
|
|
def has_ssl():
|
|
|
try:
|
|
|
import ssl
|
|
|
import OpenSSL
|
|
|
OpenSSL.SSL.Context
|
|
|
return True
|
|
|
except ImportError:
|
|
|
return False
|
|
|
|
|
|
def has_windows():
|
|
|
return os.name == 'nt'
|
|
|
|
|
|
def has_system_sh():
|
|
|
return os.name != 'nt'
|
|
|
|
|
|
def has_serve():
|
|
|
return os.name != 'nt' # gross approximation
|
|
|
|
|
|
def has_tic():
|
|
|
return matchoutput('test -x "`which tic`"', '')
|
|
|
|
|
|
checks = {
|
|
|
"baz": (has_baz, "GNU Arch baz client"),
|
|
|
"bzr": (has_bzr, "Canonical's Bazaar client"),
|
|
|
"bzr114": (has_bzr114, "Canonical's Bazaar client >= 1.14"),
|
|
|
"cacheable": (has_cacheable_fs, "cacheable filesystem"),
|
|
|
"cvs": (has_cvs, "cvs client/server"),
|
|
|
"darcs": (has_darcs, "darcs client"),
|
|
|
"docutils": (has_docutils, "Docutils text processing library"),
|
|
|
"eol-in-paths": (has_eol_in_paths, "end-of-lines in paths"),
|
|
|
"execbit": (has_executablebit, "executable bit"),
|
|
|
"fifo": (has_fifo, "named pipes"),
|
|
|
"gettext": (has_gettext, "GNU Gettext (msgfmt)"),
|
|
|
"git": (has_git, "git command line client"),
|
|
|
"gpg": (has_gpg, "gpg client"),
|
|
|
"icasefs": (has_icasefs, "case insensitive file system"),
|
|
|
"inotify": (has_inotify, "inotify extension support"),
|
|
|
"lsprof": (has_lsprof, "python lsprof module"),
|
|
|
"mtn": (has_mtn, "monotone client (>= 1.0)"),
|
|
|
"outer-repo": (has_outer_repo, "outer repo"),
|
|
|
"p4": (has_p4, "Perforce server and client"),
|
|
|
"pyflakes": (has_pyflakes, "Pyflakes python linter"),
|
|
|
"pygments": (has_pygments, "Pygments source highlighting library"),
|
|
|
"serve": (has_serve, "platform and python can manage 'hg serve -d'"),
|
|
|
"ssl": (has_ssl, "python >= 2.6 ssl module and python OpenSSL"),
|
|
|
"svn": (has_svn, "subversion client and admin tools"),
|
|
|
"svn13": (has_svn13, "subversion client and admin tools >= 1.3"),
|
|
|
"svn15": (has_svn15, "subversion client and admin tools >= 1.5"),
|
|
|
"svn-bindings": (has_svn_bindings, "subversion python bindings"),
|
|
|
"symlink": (has_symlink, "symbolic links"),
|
|
|
"system-sh": (has_system_sh, "system() uses sh"),
|
|
|
"tic": (has_tic, "terminfo compiler"),
|
|
|
"tla": (has_tla, "GNU Arch tla client"),
|
|
|
"unix-permissions": (has_unix_permissions, "unix-style permissions"),
|
|
|
"windows": (has_windows, "Windows"),
|
|
|
}
|
|
|
|
|
|
def list_features():
|
|
|
for name, feature in checks.iteritems():
|
|
|
desc = feature[1]
|
|
|
print name + ':', desc
|
|
|
|
|
|
def test_features():
|
|
|
failed = 0
|
|
|
for name, feature in checks.iteritems():
|
|
|
check, _ = feature
|
|
|
try:
|
|
|
check()
|
|
|
except Exception, e:
|
|
|
print "feature %s failed: %s" % (name, e)
|
|
|
failed += 1
|
|
|
return failed
|
|
|
|
|
|
parser = optparse.OptionParser("%prog [options] [features]")
|
|
|
parser.add_option("--test-features", action="store_true",
|
|
|
help="test available features")
|
|
|
parser.add_option("--list-features", action="store_true",
|
|
|
help="list available features")
|
|
|
parser.add_option("-q", "--quiet", action="store_true",
|
|
|
help="check features silently")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
options, args = parser.parse_args()
|
|
|
if options.list_features:
|
|
|
list_features()
|
|
|
sys.exit(0)
|
|
|
|
|
|
if options.test_features:
|
|
|
sys.exit(test_features())
|
|
|
|
|
|
quiet = options.quiet
|
|
|
|
|
|
failures = 0
|
|
|
|
|
|
def error(msg):
|
|
|
global failures
|
|
|
if not quiet:
|
|
|
sys.stderr.write(msg + '\n')
|
|
|
failures += 1
|
|
|
|
|
|
for feature in args:
|
|
|
negate = feature.startswith('no-')
|
|
|
if negate:
|
|
|
feature = feature[3:]
|
|
|
|
|
|
if feature not in checks:
|
|
|
error('skipped: unknown feature: ' + feature)
|
|
|
continue
|
|
|
|
|
|
check, desc = checks[feature]
|
|
|
try:
|
|
|
available = check()
|
|
|
except Exception, e:
|
|
|
error('hghave check failed: ' + feature)
|
|
|
continue
|
|
|
|
|
|
if not negate and not available:
|
|
|
error('skipped: missing feature: ' + desc)
|
|
|
elif negate and available:
|
|
|
error('skipped: system supports %s' % desc)
|
|
|
|
|
|
if failures != 0:
|
|
|
sys.exit(1)
|
|
|
|