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