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