tests/hghave.py
changeset 22093 45611a306f77
parent 21208 0e1cbd3d52f7
child 22198 77142de48ae4
equal deleted inserted replaced
22092:6e5ff8e26af6 22093:45611a306f77
     2 import re
     2 import re
     3 import sys
     3 import sys
     4 import tempfile
     4 import tempfile
     5 
     5 
     6 tempprefix = 'hg-hghave-'
     6 tempprefix = 'hg-hghave-'
       
     7 
       
     8 checks = {
       
     9     "true": (lambda: True, "yak shaving"),
       
    10     "false": (lambda: False, "nail clipper"),
       
    11 }
       
    12 
       
    13 def check(name, desc):
       
    14     def decorator(func):
       
    15         checks[name] = (func, desc)
       
    16         return func
       
    17     return decorator
     7 
    18 
     8 def matchoutput(cmd, regexp, ignorestatus=False):
    19 def matchoutput(cmd, regexp, ignorestatus=False):
     9     """Return True if cmd executes successfully and its output
    20     """Return True if cmd executes successfully and its output
    10     is matched by the supplied regular expression.
    21     is matched by the supplied regular expression.
    11     """
    22     """
    17     except IOError:
    28     except IOError:
    18         # Happen in Windows test environment
    29         # Happen in Windows test environment
    19         ret = 1
    30         ret = 1
    20     return (ignorestatus or ret is None) and r.search(s)
    31     return (ignorestatus or ret is None) and r.search(s)
    21 
    32 
       
    33 @check("baz", "GNU Arch baz client")
    22 def has_baz():
    34 def has_baz():
    23     return matchoutput('baz --version 2>&1', r'baz Bazaar version')
    35     return matchoutput('baz --version 2>&1', r'baz Bazaar version')
    24 
    36 
       
    37 @check("bzr", "Canonical's Bazaar client")
    25 def has_bzr():
    38 def has_bzr():
    26     try:
    39     try:
    27         import bzrlib
    40         import bzrlib
    28         return bzrlib.__doc__ is not None
    41         return bzrlib.__doc__ is not None
    29     except ImportError:
    42     except ImportError:
    30         return False
    43         return False
    31 
    44 
       
    45 @check("bzr114", "Canonical's Bazaar client >= 1.14")
    32 def has_bzr114():
    46 def has_bzr114():
    33     try:
    47     try:
    34         import bzrlib
    48         import bzrlib
    35         return (bzrlib.__doc__ is not None
    49         return (bzrlib.__doc__ is not None
    36                 and bzrlib.version_info[:2] >= (1, 14))
    50                 and bzrlib.version_info[:2] >= (1, 14))
    37     except ImportError:
    51     except ImportError:
    38         return False
    52         return False
    39 
    53 
       
    54 @check("cvs", "cvs client/server")
    40 def has_cvs():
    55 def has_cvs():
    41     re = r'Concurrent Versions System.*?server'
    56     re = r'Concurrent Versions System.*?server'
    42     return matchoutput('cvs --version 2>&1', re) and not has_msys()
    57     return matchoutput('cvs --version 2>&1', re) and not has_msys()
    43 
    58 
       
    59 @check("cvs112", "cvs client/server >= 1.12")
    44 def has_cvs112():
    60 def has_cvs112():
    45     re = r'Concurrent Versions System \(CVS\) 1.12.*?server'
    61     re = r'Concurrent Versions System \(CVS\) 1.12.*?server'
    46     return matchoutput('cvs --version 2>&1', re) and not has_msys()
    62     return matchoutput('cvs --version 2>&1', re) and not has_msys()
    47 
    63 
       
    64 @check("darcs", "darcs client")
    48 def has_darcs():
    65 def has_darcs():
    49     return matchoutput('darcs --version', r'2\.[2-9]', True)
    66     return matchoutput('darcs --version', r'2\.[2-9]', True)
    50 
    67 
       
    68 @check("mtn", "monotone client (>= 1.0)")
    51 def has_mtn():
    69 def has_mtn():
    52     return matchoutput('mtn --version', r'monotone', True) and not matchoutput(
    70     return matchoutput('mtn --version', r'monotone', True) and not matchoutput(
    53         'mtn --version', r'monotone 0\.', True)
    71         'mtn --version', r'monotone 0\.', True)
    54 
    72 
       
    73 @check("eol-in-paths", "end-of-lines in paths")
    55 def has_eol_in_paths():
    74 def has_eol_in_paths():
    56     try:
    75     try:
    57         fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r')
    76         fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r')
    58         os.close(fd)
    77         os.close(fd)
    59         os.remove(path)
    78         os.remove(path)
    60         return True
    79         return True
    61     except (IOError, OSError):
    80     except (IOError, OSError):
    62         return False
    81         return False
    63 
    82 
       
    83 @check("execbit", "executable bit")
    64 def has_executablebit():
    84 def has_executablebit():
    65     try:
    85     try:
    66         EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
    86         EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
    67         fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
    87         fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
    68         try:
    88         try:
    76     except (IOError, OSError):
    96     except (IOError, OSError):
    77         # we don't care, the user probably won't be able to commit anyway
    97         # we don't care, the user probably won't be able to commit anyway
    78         return False
    98         return False
    79     return not (new_file_has_exec or exec_flags_cannot_flip)
    99     return not (new_file_has_exec or exec_flags_cannot_flip)
    80 
   100 
       
   101 @check("icasefs", "case insensitive file system")
    81 def has_icasefs():
   102 def has_icasefs():
    82     # Stolen from mercurial.util
   103     # Stolen from mercurial.util
    83     fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
   104     fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
    84     os.close(fd)
   105     os.close(fd)
    85     try:
   106     try:
    94         except OSError:
   115         except OSError:
    95             return False
   116             return False
    96     finally:
   117     finally:
    97         os.remove(path)
   118         os.remove(path)
    98 
   119 
       
   120 @check("fifo", "named pipes")
    99 def has_fifo():
   121 def has_fifo():
   100     if getattr(os, "mkfifo", None) is None:
   122     if getattr(os, "mkfifo", None) is None:
   101         return False
   123         return False
   102     name = tempfile.mktemp(dir='.', prefix=tempprefix)
   124     name = tempfile.mktemp(dir='.', prefix=tempprefix)
   103     try:
   125     try:
   105         os.unlink(name)
   127         os.unlink(name)
   106         return True
   128         return True
   107     except OSError:
   129     except OSError:
   108         return False
   130         return False
   109 
   131 
       
   132 @check("killdaemons", 'killdaemons.py support')
   110 def has_killdaemons():
   133 def has_killdaemons():
   111     return True
   134     return True
   112 
   135 
       
   136 @check("cacheable", "cacheable filesystem")
   113 def has_cacheable_fs():
   137 def has_cacheable_fs():
   114     from mercurial import util
   138     from mercurial import util
   115 
   139 
   116     fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
   140     fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
   117     os.close(fd)
   141     os.close(fd)
   118     try:
   142     try:
   119         return util.cachestat(path).cacheable()
   143         return util.cachestat(path).cacheable()
   120     finally:
   144     finally:
   121         os.remove(path)
   145         os.remove(path)
   122 
   146 
       
   147 @check("lsprof", "python lsprof module")
   123 def has_lsprof():
   148 def has_lsprof():
   124     try:
   149     try:
   125         import _lsprof
   150         import _lsprof
   126         return True
   151         return True
   127     except ImportError:
   152     except ImportError:
   128         return False
   153         return False
   129 
   154 
       
   155 @check("gettext", "GNU Gettext (msgfmt)")
   130 def has_gettext():
   156 def has_gettext():
   131     return matchoutput('msgfmt --version', 'GNU gettext-tools')
   157     return matchoutput('msgfmt --version', 'GNU gettext-tools')
   132 
   158 
       
   159 @check("git", "git command line client")
   133 def has_git():
   160 def has_git():
   134     return matchoutput('git --version 2>&1', r'^git version')
   161     return matchoutput('git --version 2>&1', r'^git version')
   135 
   162 
       
   163 @check("docutils", "Docutils text processing library")
   136 def has_docutils():
   164 def has_docutils():
   137     try:
   165     try:
   138         from docutils.core import publish_cmdline
   166         from docutils.core import publish_cmdline
   139         return True
   167         return True
   140     except ImportError:
   168     except ImportError:
   144     m = matchoutput('svn --version --quiet 2>&1', r'^(\d+)\.(\d+)')
   172     m = matchoutput('svn --version --quiet 2>&1', r'^(\d+)\.(\d+)')
   145     if not m:
   173     if not m:
   146         return (0, 0)
   174         return (0, 0)
   147     return (int(m.group(1)), int(m.group(2)))
   175     return (int(m.group(1)), int(m.group(2)))
   148 
   176 
       
   177 @check("svn15", "subversion client and admin tools >= 1.5")
   149 def has_svn15():
   178 def has_svn15():
   150     return getsvnversion() >= (1, 5)
   179     return getsvnversion() >= (1, 5)
   151 
   180 
       
   181 @check("svn13", "subversion client and admin tools >= 1.3")
   152 def has_svn13():
   182 def has_svn13():
   153     return getsvnversion() >= (1, 3)
   183     return getsvnversion() >= (1, 3)
   154 
   184 
       
   185 @check("svn", "subversion client and admin tools")
   155 def has_svn():
   186 def has_svn():
   156     return matchoutput('svn --version 2>&1', r'^svn, version') and \
   187     return matchoutput('svn --version 2>&1', r'^svn, version') and \
   157         matchoutput('svnadmin --version 2>&1', r'^svnadmin, version')
   188         matchoutput('svnadmin --version 2>&1', r'^svnadmin, version')
   158 
   189 
       
   190 @check("svn-bindings", "subversion python bindings")
   159 def has_svn_bindings():
   191 def has_svn_bindings():
   160     try:
   192     try:
   161         import svn.core
   193         import svn.core
   162         version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
   194         version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
   163         if version < (1, 4):
   195         if version < (1, 4):
   164             return False
   196             return False
   165         return True
   197         return True
   166     except ImportError:
   198     except ImportError:
   167         return False
   199         return False
   168 
   200 
       
   201 @check("p4", "Perforce server and client")
   169 def has_p4():
   202 def has_p4():
   170     return (matchoutput('p4 -V', r'Rev\. P4/') and
   203     return (matchoutput('p4 -V', r'Rev\. P4/') and
   171             matchoutput('p4d -V', r'Rev\. P4D/'))
   204             matchoutput('p4d -V', r'Rev\. P4D/'))
   172 
   205 
       
   206 @check("symlink", "symbolic links")
   173 def has_symlink():
   207 def has_symlink():
   174     if getattr(os, "symlink", None) is None:
   208     if getattr(os, "symlink", None) is None:
   175         return False
   209         return False
   176     name = tempfile.mktemp(dir='.', prefix=tempprefix)
   210     name = tempfile.mktemp(dir='.', prefix=tempprefix)
   177     try:
   211     try:
   179         os.unlink(name)
   213         os.unlink(name)
   180         return True
   214         return True
   181     except (OSError, AttributeError):
   215     except (OSError, AttributeError):
   182         return False
   216         return False
   183 
   217 
       
   218 @check("hardlink", "hardlinks")
   184 def has_hardlink():
   219 def has_hardlink():
   185     from mercurial import util
   220     from mercurial import util
   186     fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
   221     fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
   187     os.close(fh)
   222     os.close(fh)
   188     name = tempfile.mktemp(dir='.', prefix=tempprefix)
   223     name = tempfile.mktemp(dir='.', prefix=tempprefix)
   194         except OSError:
   229         except OSError:
   195             return False
   230             return False
   196     finally:
   231     finally:
   197         os.unlink(fn)
   232         os.unlink(fn)
   198 
   233 
       
   234 @check("tla", "GNU Arch tla client")
   199 def has_tla():
   235 def has_tla():
   200     return matchoutput('tla --version 2>&1', r'The GNU Arch Revision')
   236     return matchoutput('tla --version 2>&1', r'The GNU Arch Revision')
   201 
   237 
       
   238 @check("gpg", "gpg client")
   202 def has_gpg():
   239 def has_gpg():
   203     return matchoutput('gpg --version 2>&1', r'GnuPG')
   240     return matchoutput('gpg --version 2>&1', r'GnuPG')
   204 
   241 
       
   242 @check("unix-permissions", "unix-style permissions")
   205 def has_unix_permissions():
   243 def has_unix_permissions():
   206     d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
   244     d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
   207     try:
   245     try:
   208         fname = os.path.join(d, 'foo')
   246         fname = os.path.join(d, 'foo')
   209         for umask in (077, 007, 022):
   247         for umask in (077, 007, 022):
   216                 return False
   254                 return False
   217         return True
   255         return True
   218     finally:
   256     finally:
   219         os.rmdir(d)
   257         os.rmdir(d)
   220 
   258 
       
   259 @check("root", "root permissions")
   221 def has_root():
   260 def has_root():
   222     return getattr(os, 'geteuid', None) and os.geteuid() == 0
   261     return getattr(os, 'geteuid', None) and os.geteuid() == 0
   223 
   262 
       
   263 @check("pyflakes", "Pyflakes python linter")
   224 def has_pyflakes():
   264 def has_pyflakes():
   225     return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"",
   265     return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"",
   226                        r"<stdin>:1: 're' imported but unused",
   266                        r"<stdin>:1: 're' imported but unused",
   227                        True)
   267                        True)
   228 
   268 
       
   269 @check("pygments", "Pygments source highlighting library")
   229 def has_pygments():
   270 def has_pygments():
   230     try:
   271     try:
   231         import pygments
   272         import pygments
   232         return True
   273         return True
   233     except ImportError:
   274     except ImportError:
   234         return False
   275         return False
   235 
   276 
       
   277 @check("python243", "python >= 2.4.3")
   236 def has_python243():
   278 def has_python243():
   237     return sys.version_info >= (2, 4, 3)
   279     return sys.version_info >= (2, 4, 3)
   238 
   280 
       
   281 @check("outer-repo", "outer repo")
   239 def has_outer_repo():
   282 def has_outer_repo():
   240     # failing for other reasons than 'no repo' imply that there is a repo
   283     # failing for other reasons than 'no repo' imply that there is a repo
   241     return not matchoutput('hg root 2>&1',
   284     return not matchoutput('hg root 2>&1',
   242                            r'abort: no repository found', True)
   285                            r'abort: no repository found', True)
   243 
   286 
       
   287 @check("ssl", "python >= 2.6 ssl module and python OpenSSL")
   244 def has_ssl():
   288 def has_ssl():
   245     try:
   289     try:
   246         import ssl
   290         import ssl
   247         import OpenSSL
   291         import OpenSSL
   248         OpenSSL.SSL.Context
   292         OpenSSL.SSL.Context
   249         return True
   293         return True
   250     except ImportError:
   294     except ImportError:
   251         return False
   295         return False
   252 
   296 
       
   297 @check("windows", "Windows")
   253 def has_windows():
   298 def has_windows():
   254     return os.name == 'nt'
   299     return os.name == 'nt'
   255 
   300 
       
   301 @check("system-sh", "system() uses sh")
   256 def has_system_sh():
   302 def has_system_sh():
   257     return os.name != 'nt'
   303     return os.name != 'nt'
   258 
   304 
       
   305 @check("serve", "platform and python can manage 'hg serve -d'")
   259 def has_serve():
   306 def has_serve():
   260     return os.name != 'nt' # gross approximation
   307     return os.name != 'nt' # gross approximation
   261 
   308 
       
   309 @check("test-repo", "running tests from repository")
   262 def has_test_repo():
   310 def has_test_repo():
   263     t = os.environ["TESTDIR"]
   311     t = os.environ["TESTDIR"]
   264     return os.path.isdir(os.path.join(t, "..", ".hg"))
   312     return os.path.isdir(os.path.join(t, "..", ".hg"))
   265 
   313 
       
   314 @check("tic", "terminfo compiler and curses module")
   266 def has_tic():
   315 def has_tic():
   267     try:
   316     try:
   268         import curses
   317         import curses
   269         curses.COLOR_BLUE
   318         curses.COLOR_BLUE
   270         return matchoutput('test -x "`which tic`"', '')
   319         return matchoutput('test -x "`which tic`"', '')
   271     except ImportError:
   320     except ImportError:
   272         return False
   321         return False
   273 
   322 
       
   323 @check("msys", "Windows with MSYS")
   274 def has_msys():
   324 def has_msys():
   275     return os.getenv('MSYSTEM')
   325     return os.getenv('MSYSTEM')
   276 
   326 
       
   327 @check("aix", "AIX")
   277 def has_aix():
   328 def has_aix():
   278     return sys.platform.startswith("aix")
   329     return sys.platform.startswith("aix")
   279 
   330 
       
   331 @check("absimport", "absolute_import in __future__")
   280 def has_absimport():
   332 def has_absimport():
   281     import __future__
   333     import __future__
   282     from mercurial import util
   334     from mercurial import util
   283     return util.safehasattr(__future__, "absolute_import")
   335     return util.safehasattr(__future__, "absolute_import")
   284 
   336 
       
   337 @check("py3k", "running with Python 3.x")
   285 def has_py3k():
   338 def has_py3k():
   286     return 3 == sys.version_info[0]
   339     return 3 == sys.version_info[0]
   287 
       
   288 checks = {
       
   289     "true": (lambda: True, "yak shaving"),
       
   290     "false": (lambda: False, "nail clipper"),
       
   291     "baz": (has_baz, "GNU Arch baz client"),
       
   292     "bzr": (has_bzr, "Canonical's Bazaar client"),
       
   293     "bzr114": (has_bzr114, "Canonical's Bazaar client >= 1.14"),
       
   294     "cacheable": (has_cacheable_fs, "cacheable filesystem"),
       
   295     "cvs": (has_cvs, "cvs client/server"),
       
   296     "cvs112": (has_cvs112, "cvs client/server >= 1.12"),
       
   297     "darcs": (has_darcs, "darcs client"),
       
   298     "docutils": (has_docutils, "Docutils text processing library"),
       
   299     "eol-in-paths": (has_eol_in_paths, "end-of-lines in paths"),
       
   300     "execbit": (has_executablebit, "executable bit"),
       
   301     "fifo": (has_fifo, "named pipes"),
       
   302     "gettext": (has_gettext, "GNU Gettext (msgfmt)"),
       
   303     "git": (has_git, "git command line client"),
       
   304     "gpg": (has_gpg, "gpg client"),
       
   305     "hardlink": (has_hardlink, "hardlinks"),
       
   306     "icasefs": (has_icasefs, "case insensitive file system"),
       
   307     "killdaemons": (has_killdaemons, 'killdaemons.py support'),
       
   308     "lsprof": (has_lsprof, "python lsprof module"),
       
   309     "mtn": (has_mtn, "monotone client (>= 1.0)"),
       
   310     "outer-repo": (has_outer_repo, "outer repo"),
       
   311     "p4": (has_p4, "Perforce server and client"),
       
   312     "pyflakes": (has_pyflakes, "Pyflakes python linter"),
       
   313     "pygments": (has_pygments, "Pygments source highlighting library"),
       
   314     "python243": (has_python243, "python >= 2.4.3"),
       
   315     "root": (has_root, "root permissions"),
       
   316     "serve": (has_serve, "platform and python can manage 'hg serve -d'"),
       
   317     "ssl": (has_ssl, "python >= 2.6 ssl module and python OpenSSL"),
       
   318     "svn": (has_svn, "subversion client and admin tools"),
       
   319     "svn13": (has_svn13, "subversion client and admin tools >= 1.3"),
       
   320     "svn15": (has_svn15, "subversion client and admin tools >= 1.5"),
       
   321     "svn-bindings": (has_svn_bindings, "subversion python bindings"),
       
   322     "symlink": (has_symlink, "symbolic links"),
       
   323     "system-sh": (has_system_sh, "system() uses sh"),
       
   324     "test-repo": (has_test_repo, "running tests from repository"),
       
   325     "tic": (has_tic, "terminfo compiler and curses module"),
       
   326     "tla": (has_tla, "GNU Arch tla client"),
       
   327     "unix-permissions": (has_unix_permissions, "unix-style permissions"),
       
   328     "windows": (has_windows, "Windows"),
       
   329     "msys": (has_msys, "Windows with MSYS"),
       
   330     "aix": (has_aix, "AIX"),
       
   331     "absimport": (has_absimport, "absolute_import in __future__"),
       
   332     "py3k": (has_py3k, "running with Python 3.x"),
       
   333 }