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