##// END OF EJS Templates
packaging: move Inno Setup core logic into a module...
Gregory Szorc -
r42077:dc7827a9 default
parent child Browse files
Show More
@@ -1,204 +1,169 b''
1 #!/usr/bin/env python3
2 # build.py - Inno installer build script.
1 # inno.py - Inno Setup functionality.
3 2 #
4 3 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
5 4 #
6 5 # This software may be used and distributed according to the terms of the
7 6 # GNU General Public License version 2 or any later version.
8 7
9 # This script automates the building of the Inno MSI installer for Mercurial.
10
11 8 # no-check-code because Python 3 native.
12 9
13 import argparse
14 10 import os
15 11 import pathlib
16 12 import shutil
17 13 import subprocess
18 import sys
19 14 import tempfile
20 15
16 from .downloads import (
17 download_entry,
18 )
19 from .util import (
20 extract_tar_to_directory,
21 extract_zip_to_directory,
22 find_vc_runtime_files,
23 )
24
21 25
22 26 PRINT_PYTHON_INFO = '''
23 27 import platform, sys; print("%s:%d" % (platform.architecture()[0], sys.version_info[0]))
24 28 '''.strip()
25 29
26 30
27 31 def build(source_dir: pathlib.Path, build_dir: pathlib.Path,
28 32 python_exe: pathlib.Path, iscc_exe: pathlib.Path,
29 33 version=None):
30 34 """Build the Inno installer.
31 35
32 36 Build files will be placed in ``build_dir``.
33 37
34 38 py2exe's setup.py doesn't use setuptools. It doesn't have modern logic
35 39 for finding the Python 2.7 toolchain. So, we require the environment
36 40 to already be configured with an active toolchain.
37 41 """
38 from hgpackaging.downloads import (
39 download_entry,
40 )
41 from hgpackaging.util import (
42 extract_tar_to_directory,
43 extract_zip_to_directory,
44 find_vc_runtime_files,
45 )
46
47 if not iscc.exists():
48 raise Exception('%s does not exist' % iscc)
42 if not iscc_exe.exists():
43 raise Exception('%s does not exist' % iscc_exe)
49 44
50 45 if 'VCINSTALLDIR' not in os.environ:
51 46 raise Exception('not running from a Visual C++ build environment; '
52 47 'execute the "Visual C++ <version> Command Prompt" '
53 48 'application shortcut or a vcsvarsall.bat file')
54 49
55 50 # Identity x86/x64 and validate the environment matches the Python
56 51 # architecture.
57 52 vc_x64 = r'\x64' in os.environ['LIB']
58 53
59 54 res = subprocess.run(
60 55 [str(python_exe), '-c', PRINT_PYTHON_INFO],
61 56 capture_output=True, check=True)
62 57
63 58 py_arch, py_version = res.stdout.decode('utf-8').split(':')
64 59 py_version = int(py_version)
65 60
66 61 if vc_x64:
67 62 if py_arch != '64bit':
68 63 raise Exception('architecture mismatch: Visual C++ environment '
69 64 'is configured for 64-bit but Python is 32-bit')
70 65 else:
71 66 if py_arch != '32bit':
72 67 raise Exception('architecture mismatch: Visual C++ environment '
73 68 'is configured for 32-bit but Python is 64-bit')
74 69
75 70 if py_version != 2:
76 71 raise Exception('Only Python 2 is currently supported')
77 72
78 73 build_dir.mkdir(exist_ok=True)
79 74
80 75 gettext_pkg, gettext_entry = download_entry('gettext', build_dir)
81 76 gettext_dep_pkg = download_entry('gettext-dep', build_dir)[0]
82 77 virtualenv_pkg, virtualenv_entry = download_entry('virtualenv', build_dir)
83 78 py2exe_pkg, py2exe_entry = download_entry('py2exe', build_dir)
84 79
85 80 venv_path = build_dir / ('venv-inno-%s' % ('x64' if vc_x64 else 'x86'))
86 81
87 82 gettext_root = build_dir / (
88 83 'gettext-win-%s' % gettext_entry['version'])
89 84
90 85 if not gettext_root.exists():
91 86 extract_zip_to_directory(gettext_pkg, gettext_root)
92 87 extract_zip_to_directory(gettext_dep_pkg, gettext_root)
93 88
94 89 with tempfile.TemporaryDirectory() as td:
95 90 td = pathlib.Path(td)
96 91
97 92 # This assumes Python 2.
98 93 extract_tar_to_directory(virtualenv_pkg, td)
99 94 extract_zip_to_directory(py2exe_pkg, td)
100 95
101 96 virtualenv_src_path = td / ('virtualenv-%s' %
102 97 virtualenv_entry['version'])
103 98 py2exe_source_path = td / ('py2exe-%s' %
104 99 py2exe_entry['version'])
105 100
106 101 virtualenv_py = virtualenv_src_path / 'virtualenv.py'
107 102
108 103 if not venv_path.exists():
109 104 print('creating virtualenv with dependencies')
110 105 subprocess.run(
111 106 [str(python_exe), str(virtualenv_py), str(venv_path)],
112 107 check=True)
113 108
114 109 venv_python = venv_path / 'Scripts' / 'python.exe'
115 110 venv_pip = venv_path / 'Scripts' / 'pip.exe'
116 111
117 112 requirements_txt = (source_dir / 'contrib' / 'packaging' /
118 113 'inno' / 'requirements.txt')
119 114 subprocess.run([str(venv_pip), 'install', '-r', str(requirements_txt)],
120 115 check=True)
121 116
122 117 # Force distutils to use VC++ settings from environment, which was
123 118 # validated above.
124 119 env = dict(os.environ)
125 120 env['DISTUTILS_USE_SDK'] = '1'
126 121 env['MSSdk'] = '1'
127 122
128 123 py2exe_py_path = venv_path / 'Lib' / 'site-packages' / 'py2exe'
129 124 if not py2exe_py_path.exists():
130 125 print('building py2exe')
131 126 subprocess.run([str(venv_python), 'setup.py', 'install'],
132 127 cwd=py2exe_source_path,
133 128 env=env,
134 129 check=True)
135 130
136 131 # Register location of msgfmt and other binaries.
137 132 env['PATH'] = '%s%s%s' % (
138 133 env['PATH'], os.pathsep, str(gettext_root / 'bin'))
139 134
140 135 print('building Mercurial')
141 136 subprocess.run(
142 137 [str(venv_python), 'setup.py',
143 138 'py2exe', '-b', '3' if vc_x64 else '2',
144 139 'build_doc', '--html'],
145 140 cwd=str(source_dir),
146 141 env=env,
147 142 check=True)
148 143
149 144 # hg.exe depends on VC9 runtime DLLs. Copy those into place.
150 145 for f in find_vc_runtime_files(vc_x64):
151 146 if f.name.endswith('.manifest'):
152 147 basename = 'Microsoft.VC90.CRT.manifest'
153 148 else:
154 149 basename = f.name
155 150
156 151 dest_path = source_dir / 'dist' / basename
157 152
158 153 print('copying %s to %s' % (f, dest_path))
159 154 shutil.copyfile(f, dest_path)
160 155
161 156 print('creating installer')
162 157
163 158 args = [str(iscc_exe)]
164 159
165 160 if vc_x64:
166 161 args.append('/dARCH=x64')
167 162
168 163 if version:
169 164 args.append('/dVERSION=%s' % version)
170 165
171 166 args.append('/Odist')
172 167 args.append('contrib/packaging/inno/mercurial.iss')
173 168
174 169 subprocess.run(args, cwd=str(source_dir), check=True)
175
176
177 if __name__ == '__main__':
178 parser = argparse.ArgumentParser()
179
180 parser.add_argument('--python',
181 required=True,
182 help='path to python.exe to use')
183 parser.add_argument('--iscc',
184 help='path to iscc.exe to use')
185 parser.add_argument('--version',
186 help='Mercurial version string to use '
187 '(detected from __version__.py if not defined')
188
189 args = parser.parse_args()
190
191 if args.iscc:
192 iscc = pathlib.Path(args.iscc)
193 else:
194 iscc = (pathlib.Path(os.environ['ProgramFiles(x86)']) / 'Inno Setup 5' /
195 'ISCC.exe')
196
197 here = pathlib.Path(os.path.abspath(os.path.dirname(__file__)))
198 source_dir = here.parent.parent.parent
199 build_dir = source_dir / 'build'
200
201 sys.path.insert(0, str(source_dir / 'contrib' / 'packaging'))
202
203 build(source_dir, build_dir, pathlib.Path(args.python), iscc,
204 version=args.version)
@@ -1,204 +1,48 b''
1 1 #!/usr/bin/env python3
2 2 # build.py - Inno installer build script.
3 3 #
4 4 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 # This script automates the building of the Inno MSI installer for Mercurial.
10 10
11 11 # no-check-code because Python 3 native.
12 12
13 13 import argparse
14 14 import os
15 15 import pathlib
16 import shutil
17 import subprocess
18 16 import sys
19 import tempfile
20
21
22 PRINT_PYTHON_INFO = '''
23 import platform, sys; print("%s:%d" % (platform.architecture()[0], sys.version_info[0]))
24 '''.strip()
25
26
27 def build(source_dir: pathlib.Path, build_dir: pathlib.Path,
28 python_exe: pathlib.Path, iscc_exe: pathlib.Path,
29 version=None):
30 """Build the Inno installer.
31
32 Build files will be placed in ``build_dir``.
33
34 py2exe's setup.py doesn't use setuptools. It doesn't have modern logic
35 for finding the Python 2.7 toolchain. So, we require the environment
36 to already be configured with an active toolchain.
37 """
38 from hgpackaging.downloads import (
39 download_entry,
40 )
41 from hgpackaging.util import (
42 extract_tar_to_directory,
43 extract_zip_to_directory,
44 find_vc_runtime_files,
45 )
46
47 if not iscc.exists():
48 raise Exception('%s does not exist' % iscc)
49
50 if 'VCINSTALLDIR' not in os.environ:
51 raise Exception('not running from a Visual C++ build environment; '
52 'execute the "Visual C++ <version> Command Prompt" '
53 'application shortcut or a vcsvarsall.bat file')
54
55 # Identity x86/x64 and validate the environment matches the Python
56 # architecture.
57 vc_x64 = r'\x64' in os.environ['LIB']
58
59 res = subprocess.run(
60 [str(python_exe), '-c', PRINT_PYTHON_INFO],
61 capture_output=True, check=True)
62
63 py_arch, py_version = res.stdout.decode('utf-8').split(':')
64 py_version = int(py_version)
65
66 if vc_x64:
67 if py_arch != '64bit':
68 raise Exception('architecture mismatch: Visual C++ environment '
69 'is configured for 64-bit but Python is 32-bit')
70 else:
71 if py_arch != '32bit':
72 raise Exception('architecture mismatch: Visual C++ environment '
73 'is configured for 32-bit but Python is 64-bit')
74
75 if py_version != 2:
76 raise Exception('Only Python 2 is currently supported')
77
78 build_dir.mkdir(exist_ok=True)
79
80 gettext_pkg, gettext_entry = download_entry('gettext', build_dir)
81 gettext_dep_pkg = download_entry('gettext-dep', build_dir)[0]
82 virtualenv_pkg, virtualenv_entry = download_entry('virtualenv', build_dir)
83 py2exe_pkg, py2exe_entry = download_entry('py2exe', build_dir)
84
85 venv_path = build_dir / ('venv-inno-%s' % ('x64' if vc_x64 else 'x86'))
86
87 gettext_root = build_dir / (
88 'gettext-win-%s' % gettext_entry['version'])
89
90 if not gettext_root.exists():
91 extract_zip_to_directory(gettext_pkg, gettext_root)
92 extract_zip_to_directory(gettext_dep_pkg, gettext_root)
93
94 with tempfile.TemporaryDirectory() as td:
95 td = pathlib.Path(td)
96
97 # This assumes Python 2.
98 extract_tar_to_directory(virtualenv_pkg, td)
99 extract_zip_to_directory(py2exe_pkg, td)
100
101 virtualenv_src_path = td / ('virtualenv-%s' %
102 virtualenv_entry['version'])
103 py2exe_source_path = td / ('py2exe-%s' %
104 py2exe_entry['version'])
105
106 virtualenv_py = virtualenv_src_path / 'virtualenv.py'
107
108 if not venv_path.exists():
109 print('creating virtualenv with dependencies')
110 subprocess.run(
111 [str(python_exe), str(virtualenv_py), str(venv_path)],
112 check=True)
113
114 venv_python = venv_path / 'Scripts' / 'python.exe'
115 venv_pip = venv_path / 'Scripts' / 'pip.exe'
116
117 requirements_txt = (source_dir / 'contrib' / 'packaging' /
118 'inno' / 'requirements.txt')
119 subprocess.run([str(venv_pip), 'install', '-r', str(requirements_txt)],
120 check=True)
121
122 # Force distutils to use VC++ settings from environment, which was
123 # validated above.
124 env = dict(os.environ)
125 env['DISTUTILS_USE_SDK'] = '1'
126 env['MSSdk'] = '1'
127
128 py2exe_py_path = venv_path / 'Lib' / 'site-packages' / 'py2exe'
129 if not py2exe_py_path.exists():
130 print('building py2exe')
131 subprocess.run([str(venv_python), 'setup.py', 'install'],
132 cwd=py2exe_source_path,
133 env=env,
134 check=True)
135
136 # Register location of msgfmt and other binaries.
137 env['PATH'] = '%s%s%s' % (
138 env['PATH'], os.pathsep, str(gettext_root / 'bin'))
139
140 print('building Mercurial')
141 subprocess.run(
142 [str(venv_python), 'setup.py',
143 'py2exe', '-b', '3' if vc_x64 else '2',
144 'build_doc', '--html'],
145 cwd=str(source_dir),
146 env=env,
147 check=True)
148
149 # hg.exe depends on VC9 runtime DLLs. Copy those into place.
150 for f in find_vc_runtime_files(vc_x64):
151 if f.name.endswith('.manifest'):
152 basename = 'Microsoft.VC90.CRT.manifest'
153 else:
154 basename = f.name
155
156 dest_path = source_dir / 'dist' / basename
157
158 print('copying %s to %s' % (f, dest_path))
159 shutil.copyfile(f, dest_path)
160
161 print('creating installer')
162
163 args = [str(iscc_exe)]
164
165 if vc_x64:
166 args.append('/dARCH=x64')
167
168 if version:
169 args.append('/dVERSION=%s' % version)
170
171 args.append('/Odist')
172 args.append('contrib/packaging/inno/mercurial.iss')
173
174 subprocess.run(args, cwd=str(source_dir), check=True)
175 17
176 18
177 19 if __name__ == '__main__':
178 20 parser = argparse.ArgumentParser()
179 21
180 22 parser.add_argument('--python',
181 23 required=True,
182 24 help='path to python.exe to use')
183 25 parser.add_argument('--iscc',
184 26 help='path to iscc.exe to use')
185 27 parser.add_argument('--version',
186 28 help='Mercurial version string to use '
187 29 '(detected from __version__.py if not defined')
188 30
189 31 args = parser.parse_args()
190 32
191 33 if args.iscc:
192 34 iscc = pathlib.Path(args.iscc)
193 35 else:
194 36 iscc = (pathlib.Path(os.environ['ProgramFiles(x86)']) / 'Inno Setup 5' /
195 37 'ISCC.exe')
196 38
197 39 here = pathlib.Path(os.path.abspath(os.path.dirname(__file__)))
198 40 source_dir = here.parent.parent.parent
199 41 build_dir = source_dir / 'build'
200 42
201 43 sys.path.insert(0, str(source_dir / 'contrib' / 'packaging'))
202 44
45 from hgpackaging.inno import build
46
203 47 build(source_dir, build_dir, pathlib.Path(args.python), iscc,
204 48 version=args.version)
@@ -1,72 +1,73 b''
1 1 #require test-repo
2 2
3 3 $ . "$TESTDIR/helpers-testrepo.sh"
4 4 $ check_code="$TESTDIR"/../contrib/check-code.py
5 5 $ cd "$TESTDIR"/..
6 6
7 7 New errors are not allowed. Warnings are strongly discouraged.
8 8 (The writing "no-che?k-code" is for not skipping this file when checking.)
9 9
10 10 $ testrepohg locate \
11 11 > -X contrib/python-zstandard \
12 12 > -X hgext/fsmonitor/pywatchman \
13 13 > -X mercurial/thirdparty \
14 14 > | sed 's-\\-/-g' | "$check_code" --warnings --per-file=0 - || false
15 15 Skipping contrib/packaging/hgpackaging/downloads.py it has no-che?k-code (glob)
16 Skipping contrib/packaging/hgpackaging/inno.py it has no-che?k-code (glob)
16 17 Skipping contrib/packaging/hgpackaging/util.py it has no-che?k-code (glob)
17 18 Skipping contrib/packaging/inno/build.py it has no-che?k-code (glob)
18 19 Skipping i18n/polib.py it has no-che?k-code (glob)
19 20 Skipping mercurial/statprof.py it has no-che?k-code (glob)
20 21 Skipping tests/badserverext.py it has no-che?k-code (glob)
21 22
22 23 @commands in debugcommands.py should be in alphabetical order.
23 24
24 25 >>> import re
25 26 >>> commands = []
26 27 >>> with open('mercurial/debugcommands.py', 'rb') as fh:
27 28 ... for line in fh:
28 29 ... m = re.match(br"^@command\('([a-z]+)", line)
29 30 ... if m:
30 31 ... commands.append(m.group(1))
31 32 >>> scommands = list(sorted(commands))
32 33 >>> for i, command in enumerate(scommands):
33 34 ... if command != commands[i]:
34 35 ... print('commands in debugcommands.py not sorted; first differing '
35 36 ... 'command is %s; expected %s' % (commands[i], command))
36 37 ... break
37 38
38 39 Prevent adding new files in the root directory accidentally.
39 40
40 41 $ testrepohg files 'glob:*'
41 42 .arcconfig
42 43 .clang-format
43 44 .editorconfig
44 45 .hgignore
45 46 .hgsigs
46 47 .hgtags
47 48 .jshintrc
48 49 CONTRIBUTING
49 50 CONTRIBUTORS
50 51 COPYING
51 52 Makefile
52 53 README.rst
53 54 hg
54 55 hgeditor
55 56 hgweb.cgi
56 57 setup.py
57 58
58 59 Prevent adding modules which could be shadowed by ancient .so/.dylib.
59 60
60 61 $ testrepohg files \
61 62 > mercurial/base85.py \
62 63 > mercurial/bdiff.py \
63 64 > mercurial/diffhelpers.py \
64 65 > mercurial/mpatch.py \
65 66 > mercurial/osutil.py \
66 67 > mercurial/parsers.py \
67 68 > mercurial/zstd.py
68 69 [1]
69 70
70 71 Keep python3 tests sorted:
71 72 $ sort < contrib/python3-whitelist > $TESTTMP/py3sorted
72 73 $ cmp contrib/python3-whitelist $TESTTMP/py3sorted || echo 'Please sort passing tests!'
General Comments 0
You need to be logged in to leave comments. Login now