##// END OF EJS Templates
packaging: establish hgpackaging package...
Gregory Szorc -
r42073:9da97f49 default
parent child Browse files
Show More
1 NO CONTENT: file renamed from contrib/packaging/__init__.py to contrib/packaging/hgpackaging/__init__.py
NO CONTENT: file renamed from contrib/packaging/__init__.py to contrib/packaging/hgpackaging/__init__.py
@@ -1,128 +1,128
1 # packagingutil.py - Common packaging utility code.
1 # util.py - Common packaging utility code.
2 #
2 #
3 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
3 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 # no-check-code because Python 3 native.
8 # no-check-code because Python 3 native.
9
9
10 import gzip
10 import gzip
11 import hashlib
11 import hashlib
12 import pathlib
12 import pathlib
13 import tarfile
13 import tarfile
14 import urllib.request
14 import urllib.request
15 import zipfile
15 import zipfile
16
16
17
17
18 def hash_path(p: pathlib.Path):
18 def hash_path(p: pathlib.Path):
19 h = hashlib.sha256()
19 h = hashlib.sha256()
20
20
21 with p.open('rb') as fh:
21 with p.open('rb') as fh:
22 while True:
22 while True:
23 chunk = fh.read(65536)
23 chunk = fh.read(65536)
24 if not chunk:
24 if not chunk:
25 break
25 break
26
26
27 h.update(chunk)
27 h.update(chunk)
28
28
29 return h.hexdigest()
29 return h.hexdigest()
30
30
31
31
32 class IntegrityError(Exception):
32 class IntegrityError(Exception):
33 """Represents an integrity error when downloading a URL."""
33 """Represents an integrity error when downloading a URL."""
34
34
35
35
36 def secure_download_stream(url, size, sha256):
36 def secure_download_stream(url, size, sha256):
37 """Securely download a URL to a stream of chunks.
37 """Securely download a URL to a stream of chunks.
38
38
39 If the integrity of the download fails, an IntegrityError is
39 If the integrity of the download fails, an IntegrityError is
40 raised.
40 raised.
41 """
41 """
42 h = hashlib.sha256()
42 h = hashlib.sha256()
43 length = 0
43 length = 0
44
44
45 with urllib.request.urlopen(url) as fh:
45 with urllib.request.urlopen(url) as fh:
46 if not url.endswith('.gz') and fh.info().get('Content-Encoding') == 'gzip':
46 if not url.endswith('.gz') and fh.info().get('Content-Encoding') == 'gzip':
47 fh = gzip.GzipFile(fileobj=fh)
47 fh = gzip.GzipFile(fileobj=fh)
48
48
49 while True:
49 while True:
50 chunk = fh.read(65536)
50 chunk = fh.read(65536)
51 if not chunk:
51 if not chunk:
52 break
52 break
53
53
54 h.update(chunk)
54 h.update(chunk)
55 length += len(chunk)
55 length += len(chunk)
56
56
57 yield chunk
57 yield chunk
58
58
59 digest = h.hexdigest()
59 digest = h.hexdigest()
60
60
61 if length != size:
61 if length != size:
62 raise IntegrityError('size mismatch on %s: wanted %d; got %d' % (
62 raise IntegrityError('size mismatch on %s: wanted %d; got %d' % (
63 url, size, length))
63 url, size, length))
64
64
65 if digest != sha256:
65 if digest != sha256:
66 raise IntegrityError('sha256 mismatch on %s: wanted %s; got %s' % (
66 raise IntegrityError('sha256 mismatch on %s: wanted %s; got %s' % (
67 url, sha256, digest))
67 url, sha256, digest))
68
68
69
69
70 def download_to_path(url: str, path: pathlib.Path, size: int, sha256: str):
70 def download_to_path(url: str, path: pathlib.Path, size: int, sha256: str):
71 """Download a URL to a filesystem path, possibly with verification."""
71 """Download a URL to a filesystem path, possibly with verification."""
72
72
73 # We download to a temporary file and rename at the end so there's
73 # We download to a temporary file and rename at the end so there's
74 # no chance of the final file being partially written or containing
74 # no chance of the final file being partially written or containing
75 # bad data.
75 # bad data.
76 print('downloading %s to %s' % (url, path))
76 print('downloading %s to %s' % (url, path))
77
77
78 if path.exists():
78 if path.exists():
79 good = True
79 good = True
80
80
81 if path.stat().st_size != size:
81 if path.stat().st_size != size:
82 print('existing file size is wrong; removing')
82 print('existing file size is wrong; removing')
83 good = False
83 good = False
84
84
85 if good:
85 if good:
86 if hash_path(path) != sha256:
86 if hash_path(path) != sha256:
87 print('existing file hash is wrong; removing')
87 print('existing file hash is wrong; removing')
88 good = False
88 good = False
89
89
90 if good:
90 if good:
91 print('%s exists and passes integrity checks' % path)
91 print('%s exists and passes integrity checks' % path)
92 return
92 return
93
93
94 path.unlink()
94 path.unlink()
95
95
96 tmp = path.with_name('%s.tmp' % path.name)
96 tmp = path.with_name('%s.tmp' % path.name)
97
97
98 try:
98 try:
99 with tmp.open('wb') as fh:
99 with tmp.open('wb') as fh:
100 for chunk in secure_download_stream(url, size, sha256):
100 for chunk in secure_download_stream(url, size, sha256):
101 fh.write(chunk)
101 fh.write(chunk)
102 except IntegrityError:
102 except IntegrityError:
103 tmp.unlink()
103 tmp.unlink()
104 raise
104 raise
105
105
106 tmp.rename(path)
106 tmp.rename(path)
107 print('successfully downloaded %s' % url)
107 print('successfully downloaded %s' % url)
108
108
109
109
110 def download_entry(entry: dict, dest_path: pathlib.Path, local_name=None) -> pathlib.Path:
110 def download_entry(entry: dict, dest_path: pathlib.Path, local_name=None) -> pathlib.Path:
111 url = entry['url']
111 url = entry['url']
112
112
113 local_name = local_name or url[url.rindex('/') + 1:]
113 local_name = local_name or url[url.rindex('/') + 1:]
114
114
115 local_path = dest_path / local_name
115 local_path = dest_path / local_name
116 download_to_path(url, local_path, entry['size'], entry['sha256'])
116 download_to_path(url, local_path, entry['size'], entry['sha256'])
117
117
118 return local_path
118 return local_path
119
119
120
120
121 def extract_tar_to_directory(source: pathlib.Path, dest: pathlib.Path):
121 def extract_tar_to_directory(source: pathlib.Path, dest: pathlib.Path):
122 with tarfile.open(source, 'r') as tf:
122 with tarfile.open(source, 'r') as tf:
123 tf.extractall(dest)
123 tf.extractall(dest)
124
124
125
125
126 def extract_zip_to_directory(source: pathlib.Path, dest: pathlib.Path):
126 def extract_zip_to_directory(source: pathlib.Path, dest: pathlib.Path):
127 with zipfile.ZipFile(source, 'r') as zf:
127 with zipfile.ZipFile(source, 'r') as zf:
128 zf.extractall(dest)
128 zf.extractall(dest)
@@ -1,253 +1,253
1 #!/usr/bin/env python3
1 #!/usr/bin/env python3
2 # build.py - Inno installer build script.
2 # build.py - Inno installer build script.
3 #
3 #
4 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
4 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 # This script automates the building of the Inno MSI installer for Mercurial.
9 # This script automates the building of the Inno MSI installer for Mercurial.
10
10
11 # no-check-code because Python 3 native.
11 # no-check-code because Python 3 native.
12
12
13 import argparse
13 import argparse
14 import os
14 import os
15 import pathlib
15 import pathlib
16 import shutil
16 import shutil
17 import subprocess
17 import subprocess
18 import sys
18 import sys
19 import tempfile
19 import tempfile
20
20
21
21
22 DOWNLOADS = {
22 DOWNLOADS = {
23 'gettext': {
23 'gettext': {
24 'url': 'https://versaweb.dl.sourceforge.net/project/gnuwin32/gettext/0.14.4/gettext-0.14.4-bin.zip',
24 'url': 'https://versaweb.dl.sourceforge.net/project/gnuwin32/gettext/0.14.4/gettext-0.14.4-bin.zip',
25 'size': 1606131,
25 'size': 1606131,
26 'sha256': '60b9ef26bc5cceef036f0424e542106cf158352b2677f43a01affd6d82a1d641',
26 'sha256': '60b9ef26bc5cceef036f0424e542106cf158352b2677f43a01affd6d82a1d641',
27 'version': '0.14.4',
27 'version': '0.14.4',
28 },
28 },
29 'gettext-dep': {
29 'gettext-dep': {
30 'url': 'https://versaweb.dl.sourceforge.net/project/gnuwin32/gettext/0.14.4/gettext-0.14.4-dep.zip',
30 'url': 'https://versaweb.dl.sourceforge.net/project/gnuwin32/gettext/0.14.4/gettext-0.14.4-dep.zip',
31 'size': 715086,
31 'size': 715086,
32 'sha256': '411f94974492fd2ecf52590cb05b1023530aec67e64154a88b1e4ebcd9c28588',
32 'sha256': '411f94974492fd2ecf52590cb05b1023530aec67e64154a88b1e4ebcd9c28588',
33 },
33 },
34 'py2exe': {
34 'py2exe': {
35 'url': 'https://versaweb.dl.sourceforge.net/project/py2exe/py2exe/0.6.9/py2exe-0.6.9.zip',
35 'url': 'https://versaweb.dl.sourceforge.net/project/py2exe/py2exe/0.6.9/py2exe-0.6.9.zip',
36 'size': 149687,
36 'size': 149687,
37 'sha256': '6bd383312e7d33eef2e43a5f236f9445e4f3e0f6b16333c6f183ed445c44ddbd',
37 'sha256': '6bd383312e7d33eef2e43a5f236f9445e4f3e0f6b16333c6f183ed445c44ddbd',
38 'version': '0.6.9',
38 'version': '0.6.9',
39 },
39 },
40 'virtualenv': {
40 'virtualenv': {
41 'url': 'https://files.pythonhosted.org/packages/37/db/89d6b043b22052109da35416abc3c397655e4bd3cff031446ba02b9654fa/virtualenv-16.4.3.tar.gz',
41 'url': 'https://files.pythonhosted.org/packages/37/db/89d6b043b22052109da35416abc3c397655e4bd3cff031446ba02b9654fa/virtualenv-16.4.3.tar.gz',
42 'size': 3713208,
42 'size': 3713208,
43 'sha256': '984d7e607b0a5d1329425dd8845bd971b957424b5ba664729fab51ab8c11bc39',
43 'sha256': '984d7e607b0a5d1329425dd8845bd971b957424b5ba664729fab51ab8c11bc39',
44 'version': '16.4.3',
44 'version': '16.4.3',
45 },
45 },
46 }
46 }
47
47
48
48
49 PRINT_PYTHON_INFO = '''
49 PRINT_PYTHON_INFO = '''
50 import platform, sys; print("%s:%d" % (platform.architecture()[0], sys.version_info[0]))
50 import platform, sys; print("%s:%d" % (platform.architecture()[0], sys.version_info[0]))
51 '''.strip()
51 '''.strip()
52
52
53
53
54 def find_vc_runtime_files(x64=False):
54 def find_vc_runtime_files(x64=False):
55 """Finds Visual C++ Runtime DLLs to include in distribution."""
55 """Finds Visual C++ Runtime DLLs to include in distribution."""
56 winsxs = pathlib.Path(os.environ['SYSTEMROOT']) / 'WinSxS'
56 winsxs = pathlib.Path(os.environ['SYSTEMROOT']) / 'WinSxS'
57
57
58 prefix = 'amd64' if x64 else 'x86'
58 prefix = 'amd64' if x64 else 'x86'
59
59
60 candidates = sorted(p for p in os.listdir(winsxs)
60 candidates = sorted(p for p in os.listdir(winsxs)
61 if p.lower().startswith('%s_microsoft.vc90.crt_' % prefix))
61 if p.lower().startswith('%s_microsoft.vc90.crt_' % prefix))
62
62
63 for p in candidates:
63 for p in candidates:
64 print('found candidate VC runtime: %s' % p)
64 print('found candidate VC runtime: %s' % p)
65
65
66 # Take the newest version.
66 # Take the newest version.
67 version = candidates[-1]
67 version = candidates[-1]
68
68
69 d = winsxs / version
69 d = winsxs / version
70
70
71 return [
71 return [
72 d / 'msvcm90.dll',
72 d / 'msvcm90.dll',
73 d / 'msvcp90.dll',
73 d / 'msvcp90.dll',
74 d / 'msvcr90.dll',
74 d / 'msvcr90.dll',
75 winsxs / 'Manifests' / ('%s.manifest' % version),
75 winsxs / 'Manifests' / ('%s.manifest' % version),
76 ]
76 ]
77
77
78
78
79 def build(source_dir: pathlib.Path, build_dir: pathlib.Path,
79 def build(source_dir: pathlib.Path, build_dir: pathlib.Path,
80 python_exe: pathlib.Path, iscc_exe: pathlib.Path,
80 python_exe: pathlib.Path, iscc_exe: pathlib.Path,
81 version=None):
81 version=None):
82 """Build the Inno installer.
82 """Build the Inno installer.
83
83
84 Build files will be placed in ``build_dir``.
84 Build files will be placed in ``build_dir``.
85
85
86 py2exe's setup.py doesn't use setuptools. It doesn't have modern logic
86 py2exe's setup.py doesn't use setuptools. It doesn't have modern logic
87 for finding the Python 2.7 toolchain. So, we require the environment
87 for finding the Python 2.7 toolchain. So, we require the environment
88 to already be configured with an active toolchain.
88 to already be configured with an active toolchain.
89 """
89 """
90 from packagingutil import (
90 from hgpackaging.util import (
91 download_entry,
91 download_entry,
92 extract_tar_to_directory,
92 extract_tar_to_directory,
93 extract_zip_to_directory,
93 extract_zip_to_directory,
94 )
94 )
95
95
96 if not iscc.exists():
96 if not iscc.exists():
97 raise Exception('%s does not exist' % iscc)
97 raise Exception('%s does not exist' % iscc)
98
98
99 if 'VCINSTALLDIR' not in os.environ:
99 if 'VCINSTALLDIR' not in os.environ:
100 raise Exception('not running from a Visual C++ build environment; '
100 raise Exception('not running from a Visual C++ build environment; '
101 'execute the "Visual C++ <version> Command Prompt" '
101 'execute the "Visual C++ <version> Command Prompt" '
102 'application shortcut or a vcsvarsall.bat file')
102 'application shortcut or a vcsvarsall.bat file')
103
103
104 # Identity x86/x64 and validate the environment matches the Python
104 # Identity x86/x64 and validate the environment matches the Python
105 # architecture.
105 # architecture.
106 vc_x64 = r'\x64' in os.environ['LIB']
106 vc_x64 = r'\x64' in os.environ['LIB']
107
107
108 res = subprocess.run(
108 res = subprocess.run(
109 [str(python_exe), '-c', PRINT_PYTHON_INFO],
109 [str(python_exe), '-c', PRINT_PYTHON_INFO],
110 capture_output=True, check=True)
110 capture_output=True, check=True)
111
111
112 py_arch, py_version = res.stdout.decode('utf-8').split(':')
112 py_arch, py_version = res.stdout.decode('utf-8').split(':')
113 py_version = int(py_version)
113 py_version = int(py_version)
114
114
115 if vc_x64:
115 if vc_x64:
116 if py_arch != '64bit':
116 if py_arch != '64bit':
117 raise Exception('architecture mismatch: Visual C++ environment '
117 raise Exception('architecture mismatch: Visual C++ environment '
118 'is configured for 64-bit but Python is 32-bit')
118 'is configured for 64-bit but Python is 32-bit')
119 else:
119 else:
120 if py_arch != '32bit':
120 if py_arch != '32bit':
121 raise Exception('architecture mismatch: Visual C++ environment '
121 raise Exception('architecture mismatch: Visual C++ environment '
122 'is configured for 32-bit but Python is 64-bit')
122 'is configured for 32-bit but Python is 64-bit')
123
123
124 if py_version != 2:
124 if py_version != 2:
125 raise Exception('Only Python 2 is currently supported')
125 raise Exception('Only Python 2 is currently supported')
126
126
127 build_dir.mkdir(exist_ok=True)
127 build_dir.mkdir(exist_ok=True)
128
128
129 gettext_pkg = download_entry(DOWNLOADS['gettext'], build_dir)
129 gettext_pkg = download_entry(DOWNLOADS['gettext'], build_dir)
130 gettext_dep_pkg = download_entry(DOWNLOADS['gettext-dep'], build_dir)
130 gettext_dep_pkg = download_entry(DOWNLOADS['gettext-dep'], build_dir)
131 virtualenv_pkg = download_entry(DOWNLOADS['virtualenv'], build_dir)
131 virtualenv_pkg = download_entry(DOWNLOADS['virtualenv'], build_dir)
132 py2exe_pkg = download_entry(DOWNLOADS['py2exe'], build_dir)
132 py2exe_pkg = download_entry(DOWNLOADS['py2exe'], build_dir)
133
133
134 venv_path = build_dir / ('venv-inno-%s' % ('x64' if vc_x64 else 'x86'))
134 venv_path = build_dir / ('venv-inno-%s' % ('x64' if vc_x64 else 'x86'))
135
135
136 gettext_root = build_dir / (
136 gettext_root = build_dir / (
137 'gettext-win-%s' % DOWNLOADS['gettext']['version'])
137 'gettext-win-%s' % DOWNLOADS['gettext']['version'])
138
138
139 if not gettext_root.exists():
139 if not gettext_root.exists():
140 extract_zip_to_directory(gettext_pkg, gettext_root)
140 extract_zip_to_directory(gettext_pkg, gettext_root)
141 extract_zip_to_directory(gettext_dep_pkg, gettext_root)
141 extract_zip_to_directory(gettext_dep_pkg, gettext_root)
142
142
143 with tempfile.TemporaryDirectory() as td:
143 with tempfile.TemporaryDirectory() as td:
144 td = pathlib.Path(td)
144 td = pathlib.Path(td)
145
145
146 # This assumes Python 2.
146 # This assumes Python 2.
147 extract_tar_to_directory(virtualenv_pkg, td)
147 extract_tar_to_directory(virtualenv_pkg, td)
148 extract_zip_to_directory(py2exe_pkg, td)
148 extract_zip_to_directory(py2exe_pkg, td)
149
149
150 virtualenv_src_path = td / ('virtualenv-%s' %
150 virtualenv_src_path = td / ('virtualenv-%s' %
151 DOWNLOADS['virtualenv']['version'])
151 DOWNLOADS['virtualenv']['version'])
152 py2exe_source_path = td / ('py2exe-%s' %
152 py2exe_source_path = td / ('py2exe-%s' %
153 DOWNLOADS['py2exe']['version'])
153 DOWNLOADS['py2exe']['version'])
154
154
155 virtualenv_py = virtualenv_src_path / 'virtualenv.py'
155 virtualenv_py = virtualenv_src_path / 'virtualenv.py'
156
156
157 if not venv_path.exists():
157 if not venv_path.exists():
158 print('creating virtualenv with dependencies')
158 print('creating virtualenv with dependencies')
159 subprocess.run(
159 subprocess.run(
160 [str(python_exe), str(virtualenv_py), str(venv_path)],
160 [str(python_exe), str(virtualenv_py), str(venv_path)],
161 check=True)
161 check=True)
162
162
163 venv_python = venv_path / 'Scripts' / 'python.exe'
163 venv_python = venv_path / 'Scripts' / 'python.exe'
164 venv_pip = venv_path / 'Scripts' / 'pip.exe'
164 venv_pip = venv_path / 'Scripts' / 'pip.exe'
165
165
166 requirements_txt = (source_dir / 'contrib' / 'packaging' /
166 requirements_txt = (source_dir / 'contrib' / 'packaging' /
167 'inno' / 'requirements.txt')
167 'inno' / 'requirements.txt')
168 subprocess.run([str(venv_pip), 'install', '-r', str(requirements_txt)],
168 subprocess.run([str(venv_pip), 'install', '-r', str(requirements_txt)],
169 check=True)
169 check=True)
170
170
171 # Force distutils to use VC++ settings from environment, which was
171 # Force distutils to use VC++ settings from environment, which was
172 # validated above.
172 # validated above.
173 env = dict(os.environ)
173 env = dict(os.environ)
174 env['DISTUTILS_USE_SDK'] = '1'
174 env['DISTUTILS_USE_SDK'] = '1'
175 env['MSSdk'] = '1'
175 env['MSSdk'] = '1'
176
176
177 py2exe_py_path = venv_path / 'Lib' / 'site-packages' / 'py2exe'
177 py2exe_py_path = venv_path / 'Lib' / 'site-packages' / 'py2exe'
178 if not py2exe_py_path.exists():
178 if not py2exe_py_path.exists():
179 print('building py2exe')
179 print('building py2exe')
180 subprocess.run([str(venv_python), 'setup.py', 'install'],
180 subprocess.run([str(venv_python), 'setup.py', 'install'],
181 cwd=py2exe_source_path,
181 cwd=py2exe_source_path,
182 env=env,
182 env=env,
183 check=True)
183 check=True)
184
184
185 # Register location of msgfmt and other binaries.
185 # Register location of msgfmt and other binaries.
186 env['PATH'] = '%s%s%s' % (
186 env['PATH'] = '%s%s%s' % (
187 env['PATH'], os.pathsep, str(gettext_root / 'bin'))
187 env['PATH'], os.pathsep, str(gettext_root / 'bin'))
188
188
189 print('building Mercurial')
189 print('building Mercurial')
190 subprocess.run(
190 subprocess.run(
191 [str(venv_python), 'setup.py',
191 [str(venv_python), 'setup.py',
192 'py2exe', '-b', '3' if vc_x64 else '2',
192 'py2exe', '-b', '3' if vc_x64 else '2',
193 'build_doc', '--html'],
193 'build_doc', '--html'],
194 cwd=str(source_dir),
194 cwd=str(source_dir),
195 env=env,
195 env=env,
196 check=True)
196 check=True)
197
197
198 # hg.exe depends on VC9 runtime DLLs. Copy those into place.
198 # hg.exe depends on VC9 runtime DLLs. Copy those into place.
199 for f in find_vc_runtime_files(vc_x64):
199 for f in find_vc_runtime_files(vc_x64):
200 if f.name.endswith('.manifest'):
200 if f.name.endswith('.manifest'):
201 basename = 'Microsoft.VC90.CRT.manifest'
201 basename = 'Microsoft.VC90.CRT.manifest'
202 else:
202 else:
203 basename = f.name
203 basename = f.name
204
204
205 dest_path = source_dir / 'dist' / basename
205 dest_path = source_dir / 'dist' / basename
206
206
207 print('copying %s to %s' % (f, dest_path))
207 print('copying %s to %s' % (f, dest_path))
208 shutil.copyfile(f, dest_path)
208 shutil.copyfile(f, dest_path)
209
209
210 print('creating installer')
210 print('creating installer')
211
211
212 args = [str(iscc_exe)]
212 args = [str(iscc_exe)]
213
213
214 if vc_x64:
214 if vc_x64:
215 args.append('/dARCH=x64')
215 args.append('/dARCH=x64')
216
216
217 if version:
217 if version:
218 args.append('/dVERSION=%s' % version)
218 args.append('/dVERSION=%s' % version)
219
219
220 args.append('/Odist')
220 args.append('/Odist')
221 args.append('contrib/packaging/inno/mercurial.iss')
221 args.append('contrib/packaging/inno/mercurial.iss')
222
222
223 subprocess.run(args, cwd=str(source_dir), check=True)
223 subprocess.run(args, cwd=str(source_dir), check=True)
224
224
225
225
226 if __name__ == '__main__':
226 if __name__ == '__main__':
227 parser = argparse.ArgumentParser()
227 parser = argparse.ArgumentParser()
228
228
229 parser.add_argument('--python',
229 parser.add_argument('--python',
230 required=True,
230 required=True,
231 help='path to python.exe to use')
231 help='path to python.exe to use')
232 parser.add_argument('--iscc',
232 parser.add_argument('--iscc',
233 help='path to iscc.exe to use')
233 help='path to iscc.exe to use')
234 parser.add_argument('--version',
234 parser.add_argument('--version',
235 help='Mercurial version string to use '
235 help='Mercurial version string to use '
236 '(detected from __version__.py if not defined')
236 '(detected from __version__.py if not defined')
237
237
238 args = parser.parse_args()
238 args = parser.parse_args()
239
239
240 if args.iscc:
240 if args.iscc:
241 iscc = pathlib.Path(args.iscc)
241 iscc = pathlib.Path(args.iscc)
242 else:
242 else:
243 iscc = (pathlib.Path(os.environ['ProgramFiles(x86)']) / 'Inno Setup 5' /
243 iscc = (pathlib.Path(os.environ['ProgramFiles(x86)']) / 'Inno Setup 5' /
244 'ISCC.exe')
244 'ISCC.exe')
245
245
246 here = pathlib.Path(os.path.abspath(os.path.dirname(__file__)))
246 here = pathlib.Path(os.path.abspath(os.path.dirname(__file__)))
247 source_dir = here.parent.parent.parent
247 source_dir = here.parent.parent.parent
248 build_dir = source_dir / 'build'
248 build_dir = source_dir / 'build'
249
249
250 sys.path.insert(0, str(source_dir / 'contrib' / 'packaging'))
250 sys.path.insert(0, str(source_dir / 'contrib' / 'packaging'))
251
251
252 build(source_dir, build_dir, pathlib.Path(args.python), iscc,
252 build(source_dir, build_dir, pathlib.Path(args.python), iscc,
253 version=args.version)
253 version=args.version)
@@ -1,71 +1,71
1 #require test-repo
1 #require test-repo
2
2
3 $ . "$TESTDIR/helpers-testrepo.sh"
3 $ . "$TESTDIR/helpers-testrepo.sh"
4 $ check_code="$TESTDIR"/../contrib/check-code.py
4 $ check_code="$TESTDIR"/../contrib/check-code.py
5 $ cd "$TESTDIR"/..
5 $ cd "$TESTDIR"/..
6
6
7 New errors are not allowed. Warnings are strongly discouraged.
7 New errors are not allowed. Warnings are strongly discouraged.
8 (The writing "no-che?k-code" is for not skipping this file when checking.)
8 (The writing "no-che?k-code" is for not skipping this file when checking.)
9
9
10 $ testrepohg locate \
10 $ testrepohg locate \
11 > -X contrib/python-zstandard \
11 > -X contrib/python-zstandard \
12 > -X hgext/fsmonitor/pywatchman \
12 > -X hgext/fsmonitor/pywatchman \
13 > -X mercurial/thirdparty \
13 > -X mercurial/thirdparty \
14 > | sed 's-\\-/-g' | "$check_code" --warnings --per-file=0 - || false
14 > | sed 's-\\-/-g' | "$check_code" --warnings --per-file=0 - || false
15 Skipping contrib/packaging/hgpackaging/util.py it has no-che?k-code (glob)
15 Skipping contrib/packaging/inno/build.py it has no-che?k-code (glob)
16 Skipping contrib/packaging/inno/build.py it has no-che?k-code (glob)
16 Skipping contrib/packaging/packagingutil.py it has no-che?k-code (glob)
17 Skipping i18n/polib.py it has no-che?k-code (glob)
17 Skipping i18n/polib.py it has no-che?k-code (glob)
18 Skipping mercurial/statprof.py it has no-che?k-code (glob)
18 Skipping mercurial/statprof.py it has no-che?k-code (glob)
19 Skipping tests/badserverext.py it has no-che?k-code (glob)
19 Skipping tests/badserverext.py it has no-che?k-code (glob)
20
20
21 @commands in debugcommands.py should be in alphabetical order.
21 @commands in debugcommands.py should be in alphabetical order.
22
22
23 >>> import re
23 >>> import re
24 >>> commands = []
24 >>> commands = []
25 >>> with open('mercurial/debugcommands.py', 'rb') as fh:
25 >>> with open('mercurial/debugcommands.py', 'rb') as fh:
26 ... for line in fh:
26 ... for line in fh:
27 ... m = re.match(br"^@command\('([a-z]+)", line)
27 ... m = re.match(br"^@command\('([a-z]+)", line)
28 ... if m:
28 ... if m:
29 ... commands.append(m.group(1))
29 ... commands.append(m.group(1))
30 >>> scommands = list(sorted(commands))
30 >>> scommands = list(sorted(commands))
31 >>> for i, command in enumerate(scommands):
31 >>> for i, command in enumerate(scommands):
32 ... if command != commands[i]:
32 ... if command != commands[i]:
33 ... print('commands in debugcommands.py not sorted; first differing '
33 ... print('commands in debugcommands.py not sorted; first differing '
34 ... 'command is %s; expected %s' % (commands[i], command))
34 ... 'command is %s; expected %s' % (commands[i], command))
35 ... break
35 ... break
36
36
37 Prevent adding new files in the root directory accidentally.
37 Prevent adding new files in the root directory accidentally.
38
38
39 $ testrepohg files 'glob:*'
39 $ testrepohg files 'glob:*'
40 .arcconfig
40 .arcconfig
41 .clang-format
41 .clang-format
42 .editorconfig
42 .editorconfig
43 .hgignore
43 .hgignore
44 .hgsigs
44 .hgsigs
45 .hgtags
45 .hgtags
46 .jshintrc
46 .jshintrc
47 CONTRIBUTING
47 CONTRIBUTING
48 CONTRIBUTORS
48 CONTRIBUTORS
49 COPYING
49 COPYING
50 Makefile
50 Makefile
51 README.rst
51 README.rst
52 hg
52 hg
53 hgeditor
53 hgeditor
54 hgweb.cgi
54 hgweb.cgi
55 setup.py
55 setup.py
56
56
57 Prevent adding modules which could be shadowed by ancient .so/.dylib.
57 Prevent adding modules which could be shadowed by ancient .so/.dylib.
58
58
59 $ testrepohg files \
59 $ testrepohg files \
60 > mercurial/base85.py \
60 > mercurial/base85.py \
61 > mercurial/bdiff.py \
61 > mercurial/bdiff.py \
62 > mercurial/diffhelpers.py \
62 > mercurial/diffhelpers.py \
63 > mercurial/mpatch.py \
63 > mercurial/mpatch.py \
64 > mercurial/osutil.py \
64 > mercurial/osutil.py \
65 > mercurial/parsers.py \
65 > mercurial/parsers.py \
66 > mercurial/zstd.py
66 > mercurial/zstd.py
67 [1]
67 [1]
68
68
69 Keep python3 tests sorted:
69 Keep python3 tests sorted:
70 $ sort < contrib/python3-whitelist > $TESTTMP/py3sorted
70 $ sort < contrib/python3-whitelist > $TESTTMP/py3sorted
71 $ cmp contrib/python3-whitelist $TESTTMP/py3sorted || echo 'Please sort passing tests!'
71 $ cmp contrib/python3-whitelist $TESTTMP/py3sorted || echo 'Please sort passing tests!'
@@ -1,38 +1,38
1 #require test-repo
1 #require test-repo
2
2
3 $ . "$TESTDIR/helpers-testrepo.sh"
3 $ . "$TESTDIR/helpers-testrepo.sh"
4 $ import_checker="$TESTDIR"/../contrib/import-checker.py
4 $ import_checker="$TESTDIR"/../contrib/import-checker.py
5
5
6 $ cd "$TESTDIR"/..
6 $ cd "$TESTDIR"/..
7
7
8 There are a handful of cases here that require renaming a module so it
8 There are a handful of cases here that require renaming a module so it
9 doesn't overlap with a stdlib module name. There are also some cycles
9 doesn't overlap with a stdlib module name. There are also some cycles
10 here that we should still endeavor to fix, and some cycles will be
10 here that we should still endeavor to fix, and some cycles will be
11 hidden by deduplication algorithm in the cycle detector, so fixing
11 hidden by deduplication algorithm in the cycle detector, so fixing
12 these may expose other cycles.
12 these may expose other cycles.
13
13
14 Known-bad files are excluded by -X as some of them would produce unstable
14 Known-bad files are excluded by -X as some of them would produce unstable
15 outputs, which should be fixed later.
15 outputs, which should be fixed later.
16
16
17 $ testrepohg locate 'set:**.py or grep(r"^#!.*?python")' \
17 $ testrepohg locate 'set:**.py or grep(r"^#!.*?python")' \
18 > 'tests/**.t' \
18 > 'tests/**.t' \
19 > -X hgweb.cgi \
19 > -X hgweb.cgi \
20 > -X setup.py \
20 > -X setup.py \
21 > -X contrib/debugshell.py \
21 > -X contrib/debugshell.py \
22 > -X contrib/hgweb.fcgi \
22 > -X contrib/hgweb.fcgi \
23 > -X contrib/packaging/hg-docker \
23 > -X contrib/packaging/hg-docker \
24 > -X contrib/packaging/hgpackaging/ \
24 > -X contrib/packaging/inno/ \
25 > -X contrib/packaging/inno/ \
25 > -X contrib/packaging/packagingutil.py \
26 > -X contrib/python-zstandard/ \
26 > -X contrib/python-zstandard/ \
27 > -X contrib/win32/hgwebdir_wsgi.py \
27 > -X contrib/win32/hgwebdir_wsgi.py \
28 > -X contrib/perf-utils/perf-revlog-write-plot.py \
28 > -X contrib/perf-utils/perf-revlog-write-plot.py \
29 > -X doc/gendoc.py \
29 > -X doc/gendoc.py \
30 > -X doc/hgmanpage.py \
30 > -X doc/hgmanpage.py \
31 > -X i18n/posplit \
31 > -X i18n/posplit \
32 > -X mercurial/thirdparty \
32 > -X mercurial/thirdparty \
33 > -X tests/hypothesishelpers.py \
33 > -X tests/hypothesishelpers.py \
34 > -X tests/test-check-interfaces.py \
34 > -X tests/test-check-interfaces.py \
35 > -X tests/test-demandimport.py \
35 > -X tests/test-demandimport.py \
36 > -X tests/test-imports-checker.t \
36 > -X tests/test-imports-checker.t \
37 > -X tests/test-verify-repo-operations.py \
37 > -X tests/test-verify-repo-operations.py \
38 > | sed 's-\\-/-g' | "$PYTHON" "$import_checker" -
38 > | sed 's-\\-/-g' | "$PYTHON" "$import_checker" -
@@ -1,58 +1,58
1 #require test-repo
1 #require test-repo
2
2
3 $ . "$TESTDIR/helpers-testrepo.sh"
3 $ . "$TESTDIR/helpers-testrepo.sh"
4 $ cd "$TESTDIR"/..
4 $ cd "$TESTDIR"/..
5
5
6 #if no-py3
6 #if no-py3
7 $ testrepohg files 'set:(**.py)' \
7 $ testrepohg files 'set:(**.py)' \
8 > -X contrib/packaging/hgpackaging/ \
8 > -X contrib/packaging/inno/ \
9 > -X contrib/packaging/inno/ \
9 > -X contrib/packaging/packagingutil.py \
10 > -X hgdemandimport/demandimportpy2.py \
10 > -X hgdemandimport/demandimportpy2.py \
11 > -X mercurial/thirdparty/cbor \
11 > -X mercurial/thirdparty/cbor \
12 > | sed 's|\\|/|g' | xargs "$PYTHON" contrib/check-py3-compat.py
12 > | sed 's|\\|/|g' | xargs "$PYTHON" contrib/check-py3-compat.py
13 contrib/python-zstandard/setup.py not using absolute_import
13 contrib/python-zstandard/setup.py not using absolute_import
14 contrib/python-zstandard/setup_zstd.py not using absolute_import
14 contrib/python-zstandard/setup_zstd.py not using absolute_import
15 contrib/python-zstandard/tests/common.py not using absolute_import
15 contrib/python-zstandard/tests/common.py not using absolute_import
16 contrib/python-zstandard/tests/test_buffer_util.py not using absolute_import
16 contrib/python-zstandard/tests/test_buffer_util.py not using absolute_import
17 contrib/python-zstandard/tests/test_compressor.py not using absolute_import
17 contrib/python-zstandard/tests/test_compressor.py not using absolute_import
18 contrib/python-zstandard/tests/test_compressor_fuzzing.py not using absolute_import
18 contrib/python-zstandard/tests/test_compressor_fuzzing.py not using absolute_import
19 contrib/python-zstandard/tests/test_data_structures.py not using absolute_import
19 contrib/python-zstandard/tests/test_data_structures.py not using absolute_import
20 contrib/python-zstandard/tests/test_data_structures_fuzzing.py not using absolute_import
20 contrib/python-zstandard/tests/test_data_structures_fuzzing.py not using absolute_import
21 contrib/python-zstandard/tests/test_decompressor.py not using absolute_import
21 contrib/python-zstandard/tests/test_decompressor.py not using absolute_import
22 contrib/python-zstandard/tests/test_decompressor_fuzzing.py not using absolute_import
22 contrib/python-zstandard/tests/test_decompressor_fuzzing.py not using absolute_import
23 contrib/python-zstandard/tests/test_estimate_sizes.py not using absolute_import
23 contrib/python-zstandard/tests/test_estimate_sizes.py not using absolute_import
24 contrib/python-zstandard/tests/test_module_attributes.py not using absolute_import
24 contrib/python-zstandard/tests/test_module_attributes.py not using absolute_import
25 contrib/python-zstandard/tests/test_train_dictionary.py not using absolute_import
25 contrib/python-zstandard/tests/test_train_dictionary.py not using absolute_import
26 setup.py not using absolute_import
26 setup.py not using absolute_import
27 #endif
27 #endif
28
28
29 #if py3
29 #if py3
30 $ testrepohg files 'set:(**.py) - grep(pygments)' \
30 $ testrepohg files 'set:(**.py) - grep(pygments)' \
31 > -X hgdemandimport/demandimportpy2.py \
31 > -X hgdemandimport/demandimportpy2.py \
32 > -X hgext/fsmonitor/pywatchman \
32 > -X hgext/fsmonitor/pywatchman \
33 > -X mercurial/cffi \
33 > -X mercurial/cffi \
34 > -X mercurial/thirdparty \
34 > -X mercurial/thirdparty \
35 > | sed 's|\\|/|g' | xargs "$PYTHON" contrib/check-py3-compat.py \
35 > | sed 's|\\|/|g' | xargs "$PYTHON" contrib/check-py3-compat.py \
36 > | sed 's/[0-9][0-9]*)$/*)/'
36 > | sed 's/[0-9][0-9]*)$/*)/'
37 contrib/python-zstandard/tests/test_compressor.py:324: SyntaxWarning: invalid escape sequence \( (py38 !)
37 contrib/python-zstandard/tests/test_compressor.py:324: SyntaxWarning: invalid escape sequence \( (py38 !)
38 with self.assertRaisesRegexp(zstd.ZstdError, 'cannot call compress\(\) after compressor'): (py38 !)
38 with self.assertRaisesRegexp(zstd.ZstdError, 'cannot call compress\(\) after compressor'): (py38 !)
39 contrib/python-zstandard/tests/test_compressor.py:1329: SyntaxWarning: invalid escape sequence \( (py38 !)
39 contrib/python-zstandard/tests/test_compressor.py:1329: SyntaxWarning: invalid escape sequence \( (py38 !)
40 'cannot call compress\(\) after compression finished'): (py38 !)
40 'cannot call compress\(\) after compression finished'): (py38 !)
41 contrib/python-zstandard/tests/test_compressor.py:1341: SyntaxWarning: invalid escape sequence \( (py38 !)
41 contrib/python-zstandard/tests/test_compressor.py:1341: SyntaxWarning: invalid escape sequence \( (py38 !)
42 'cannot call flush\(\) after compression finished'): (py38 !)
42 'cannot call flush\(\) after compression finished'): (py38 !)
43 contrib/python-zstandard/tests/test_compressor.py:1353: SyntaxWarning: invalid escape sequence \( (py38 !)
43 contrib/python-zstandard/tests/test_compressor.py:1353: SyntaxWarning: invalid escape sequence \( (py38 !)
44 'cannot call finish\(\) after compression finished'): (py38 !)
44 'cannot call finish\(\) after compression finished'): (py38 !)
45 hgext/convert/transport.py: error importing: <*Error> No module named 'svn.client' (error at transport.py:*) (glob) (?)
45 hgext/convert/transport.py: error importing: <*Error> No module named 'svn.client' (error at transport.py:*) (glob) (?)
46 hgext/infinitepush/sqlindexapi.py: error importing: <*Error> No module named 'mysql' (error at sqlindexapi.py:*) (glob) (?)
46 hgext/infinitepush/sqlindexapi.py: error importing: <*Error> No module named 'mysql' (error at sqlindexapi.py:*) (glob) (?)
47 mercurial/scmwindows.py: error importing: <ValueError> _type_ 'v' not supported (error at win32.py:*) (no-windows !)
47 mercurial/scmwindows.py: error importing: <ValueError> _type_ 'v' not supported (error at win32.py:*) (no-windows !)
48 mercurial/win32.py: error importing: <ValueError> _type_ 'v' not supported (error at win32.py:*) (no-windows !)
48 mercurial/win32.py: error importing: <ValueError> _type_ 'v' not supported (error at win32.py:*) (no-windows !)
49 mercurial/windows.py: error importing: <ModuleNotFoundError> No module named 'msvcrt' (error at windows.py:*) (no-windows !)
49 mercurial/windows.py: error importing: <ModuleNotFoundError> No module named 'msvcrt' (error at windows.py:*) (no-windows !)
50 mercurial/posix.py: error importing: <ModuleNotFoundError> No module named 'fcntl' (error at posix.py:*) (windows !)
50 mercurial/posix.py: error importing: <ModuleNotFoundError> No module named 'fcntl' (error at posix.py:*) (windows !)
51 mercurial/scmposix.py: error importing: <ModuleNotFoundError> No module named 'fcntl' (error at scmposix.py:*) (windows !)
51 mercurial/scmposix.py: error importing: <ModuleNotFoundError> No module named 'fcntl' (error at scmposix.py:*) (windows !)
52 #endif
52 #endif
53
53
54 #if py3 pygments
54 #if py3 pygments
55 $ testrepohg files 'set:(**.py) and grep(pygments)' | sed 's|\\|/|g' \
55 $ testrepohg files 'set:(**.py) and grep(pygments)' | sed 's|\\|/|g' \
56 > | xargs "$PYTHON" contrib/check-py3-compat.py \
56 > | xargs "$PYTHON" contrib/check-py3-compat.py \
57 > | sed 's/[0-9][0-9]*)$/*)/'
57 > | sed 's/[0-9][0-9]*)$/*)/'
58 #endif
58 #endif
General Comments 0
You need to be logged in to leave comments. Login now