##// END OF EJS Templates
wix: remove enum and future packages...
Gregory Szorc -
r42090:9d4ae504 default
parent child Browse files
Show More
@@ -1,240 +1,239 b''
1 # wix.py - WiX installer functionality
1 # wix.py - WiX installer functionality
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 os
10 import os
11 import pathlib
11 import pathlib
12 import re
12 import re
13 import subprocess
13 import subprocess
14
14
15 from .downloads import (
15 from .downloads import (
16 download_entry,
16 download_entry,
17 )
17 )
18 from .py2exe import (
18 from .py2exe import (
19 build_py2exe,
19 build_py2exe,
20 )
20 )
21 from .util import (
21 from .util import (
22 extract_zip_to_directory,
22 extract_zip_to_directory,
23 sign_with_signtool,
23 sign_with_signtool,
24 )
24 )
25
25
26
26
27 SUPPORT_WXS = [
27 SUPPORT_WXS = [
28 ('contrib.wxs', r'contrib'),
28 ('contrib.wxs', r'contrib'),
29 ('dist.wxs', r'dist'),
29 ('dist.wxs', r'dist'),
30 ('doc.wxs', r'doc'),
30 ('doc.wxs', r'doc'),
31 ('help.wxs', r'mercurial\help'),
31 ('help.wxs', r'mercurial\help'),
32 ('i18n.wxs', r'i18n'),
32 ('i18n.wxs', r'i18n'),
33 ('locale.wxs', r'mercurial\locale'),
33 ('locale.wxs', r'mercurial\locale'),
34 ('templates.wxs', r'mercurial\templates'),
34 ('templates.wxs', r'mercurial\templates'),
35 ]
35 ]
36
36
37
37
38 EXTRA_PACKAGES = {
38 EXTRA_PACKAGES = {
39 'distutils',
39 'distutils',
40 'enum',
41 'pygments',
40 'pygments',
42 }
41 }
43
42
44
43
45 def find_version(source_dir: pathlib.Path):
44 def find_version(source_dir: pathlib.Path):
46 version_py = source_dir / 'mercurial' / '__version__.py'
45 version_py = source_dir / 'mercurial' / '__version__.py'
47
46
48 with version_py.open('r', encoding='utf-8') as fh:
47 with version_py.open('r', encoding='utf-8') as fh:
49 source = fh.read().strip()
48 source = fh.read().strip()
50
49
51 m = re.search('version = b"(.*)"', source)
50 m = re.search('version = b"(.*)"', source)
52 return m.group(1)
51 return m.group(1)
53
52
54
53
55 def normalize_version(version):
54 def normalize_version(version):
56 """Normalize Mercurial version string so WiX accepts it.
55 """Normalize Mercurial version string so WiX accepts it.
57
56
58 Version strings have to be numeric X.Y.Z.
57 Version strings have to be numeric X.Y.Z.
59 """
58 """
60
59
61 if '+' in version:
60 if '+' in version:
62 version, extra = version.split('+', 1)
61 version, extra = version.split('+', 1)
63 else:
62 else:
64 extra = None
63 extra = None
65
64
66 # 4.9rc0
65 # 4.9rc0
67 if version[:-1].endswith('rc'):
66 if version[:-1].endswith('rc'):
68 version = version[:-3]
67 version = version[:-3]
69
68
70 versions = [int(v) for v in version.split('.')]
69 versions = [int(v) for v in version.split('.')]
71 while len(versions) < 3:
70 while len(versions) < 3:
72 versions.append(0)
71 versions.append(0)
73
72
74 major, minor, build = versions[:3]
73 major, minor, build = versions[:3]
75
74
76 if extra:
75 if extra:
77 # <commit count>-<hash>+<date>
76 # <commit count>-<hash>+<date>
78 build = int(extra.split('-')[0])
77 build = int(extra.split('-')[0])
79
78
80 return '.'.join('%d' % x for x in (major, minor, build))
79 return '.'.join('%d' % x for x in (major, minor, build))
81
80
82
81
83 def ensure_vc90_merge_modules(build_dir):
82 def ensure_vc90_merge_modules(build_dir):
84 x86 = (
83 x86 = (
85 download_entry('vc9-crt-x86-msm', build_dir,
84 download_entry('vc9-crt-x86-msm', build_dir,
86 local_name='microsoft.vcxx.crt.x86_msm.msm')[0],
85 local_name='microsoft.vcxx.crt.x86_msm.msm')[0],
87 download_entry('vc9-crt-x86-msm-policy', build_dir,
86 download_entry('vc9-crt-x86-msm-policy', build_dir,
88 local_name='policy.x.xx.microsoft.vcxx.crt.x86_msm.msm')[0]
87 local_name='policy.x.xx.microsoft.vcxx.crt.x86_msm.msm')[0]
89 )
88 )
90
89
91 x64 = (
90 x64 = (
92 download_entry('vc9-crt-x64-msm', build_dir,
91 download_entry('vc9-crt-x64-msm', build_dir,
93 local_name='microsoft.vcxx.crt.x64_msm.msm')[0],
92 local_name='microsoft.vcxx.crt.x64_msm.msm')[0],
94 download_entry('vc9-crt-x64-msm-policy', build_dir,
93 download_entry('vc9-crt-x64-msm-policy', build_dir,
95 local_name='policy.x.xx.microsoft.vcxx.crt.x64_msm.msm')[0]
94 local_name='policy.x.xx.microsoft.vcxx.crt.x64_msm.msm')[0]
96 )
95 )
97 return {
96 return {
98 'x86': x86,
97 'x86': x86,
99 'x64': x64,
98 'x64': x64,
100 }
99 }
101
100
102
101
103 def run_candle(wix, cwd, wxs, source_dir, defines=None):
102 def run_candle(wix, cwd, wxs, source_dir, defines=None):
104 args = [
103 args = [
105 str(wix / 'candle.exe'),
104 str(wix / 'candle.exe'),
106 '-nologo',
105 '-nologo',
107 str(wxs),
106 str(wxs),
108 '-dSourceDir=%s' % source_dir,
107 '-dSourceDir=%s' % source_dir,
109 ]
108 ]
110
109
111 if defines:
110 if defines:
112 args.extend('-d%s=%s' % define for define in sorted(defines.items()))
111 args.extend('-d%s=%s' % define for define in sorted(defines.items()))
113
112
114 subprocess.run(args, cwd=str(cwd), check=True)
113 subprocess.run(args, cwd=str(cwd), check=True)
115
114
116
115
117 def make_post_build_signing_fn(name, subject_name=None, cert_path=None,
116 def make_post_build_signing_fn(name, subject_name=None, cert_path=None,
118 cert_password=None, timestamp_url=None):
117 cert_password=None, timestamp_url=None):
119 """Create a callable that will use signtool to sign hg.exe."""
118 """Create a callable that will use signtool to sign hg.exe."""
120
119
121 def post_build_sign(source_dir, build_dir, dist_dir, version):
120 def post_build_sign(source_dir, build_dir, dist_dir, version):
122 description = '%s %s' % (name, version)
121 description = '%s %s' % (name, version)
123
122
124 sign_with_signtool(dist_dir / 'hg.exe', description,
123 sign_with_signtool(dist_dir / 'hg.exe', description,
125 subject_name=subject_name, cert_path=cert_path,
124 subject_name=subject_name, cert_path=cert_path,
126 cert_password=cert_password,
125 cert_password=cert_password,
127 timestamp_url=timestamp_url)
126 timestamp_url=timestamp_url)
128
127
129 return post_build_sign
128 return post_build_sign
130
129
131
130
132 def build_installer(source_dir: pathlib.Path, python_exe: pathlib.Path,
131 def build_installer(source_dir: pathlib.Path, python_exe: pathlib.Path,
133 msi_name='mercurial', version=None, post_build_fn=None):
132 msi_name='mercurial', version=None, post_build_fn=None):
134 """Build a WiX MSI installer.
133 """Build a WiX MSI installer.
135
134
136 ``source_dir`` is the path to the Mercurial source tree to use.
135 ``source_dir`` is the path to the Mercurial source tree to use.
137 ``arch`` is the target architecture. either ``x86`` or ``x64``.
136 ``arch`` is the target architecture. either ``x86`` or ``x64``.
138 ``python_exe`` is the path to the Python executable to use/bundle.
137 ``python_exe`` is the path to the Python executable to use/bundle.
139 ``version`` is the Mercurial version string. If not defined,
138 ``version`` is the Mercurial version string. If not defined,
140 ``mercurial/__version__.py`` will be consulted.
139 ``mercurial/__version__.py`` will be consulted.
141 ``post_build_fn`` is a callable that will be called after building
140 ``post_build_fn`` is a callable that will be called after building
142 Mercurial but before invoking WiX. It can be used to e.g. facilitate
141 Mercurial but before invoking WiX. It can be used to e.g. facilitate
143 signing. It is passed the paths to the Mercurial source, build, and
142 signing. It is passed the paths to the Mercurial source, build, and
144 dist directories and the resolved Mercurial version.
143 dist directories and the resolved Mercurial version.
145 """
144 """
146 arch = 'x64' if r'\x64' in os.environ.get('LIB', '') else 'x86'
145 arch = 'x64' if r'\x64' in os.environ.get('LIB', '') else 'x86'
147
146
148 hg_build_dir = source_dir / 'build'
147 hg_build_dir = source_dir / 'build'
149 dist_dir = source_dir / 'dist'
148 dist_dir = source_dir / 'dist'
150
149
151 requirements_txt = (source_dir / 'contrib' / 'packaging' /
150 requirements_txt = (source_dir / 'contrib' / 'packaging' /
152 'wix' / 'requirements.txt')
151 'wix' / 'requirements.txt')
153
152
154 build_py2exe(source_dir, hg_build_dir,
153 build_py2exe(source_dir, hg_build_dir,
155 python_exe, 'wix', requirements_txt,
154 python_exe, 'wix', requirements_txt,
156 extra_packages=EXTRA_PACKAGES)
155 extra_packages=EXTRA_PACKAGES)
157
156
158 version = version or normalize_version(find_version(source_dir))
157 version = version or normalize_version(find_version(source_dir))
159 print('using version string: %s' % version)
158 print('using version string: %s' % version)
160
159
161 if post_build_fn:
160 if post_build_fn:
162 post_build_fn(source_dir, hg_build_dir, dist_dir, version)
161 post_build_fn(source_dir, hg_build_dir, dist_dir, version)
163
162
164 build_dir = hg_build_dir / ('wix-%s' % arch)
163 build_dir = hg_build_dir / ('wix-%s' % arch)
165
164
166 build_dir.mkdir(exist_ok=True)
165 build_dir.mkdir(exist_ok=True)
167
166
168 wix_pkg, wix_entry = download_entry('wix', hg_build_dir)
167 wix_pkg, wix_entry = download_entry('wix', hg_build_dir)
169 wix_path = hg_build_dir / ('wix-%s' % wix_entry['version'])
168 wix_path = hg_build_dir / ('wix-%s' % wix_entry['version'])
170
169
171 if not wix_path.exists():
170 if not wix_path.exists():
172 extract_zip_to_directory(wix_pkg, wix_path)
171 extract_zip_to_directory(wix_pkg, wix_path)
173
172
174 ensure_vc90_merge_modules(hg_build_dir)
173 ensure_vc90_merge_modules(hg_build_dir)
175
174
176 source_build_rel = pathlib.Path(os.path.relpath(source_dir, build_dir))
175 source_build_rel = pathlib.Path(os.path.relpath(source_dir, build_dir))
177
176
178 defines = {'Platform': arch}
177 defines = {'Platform': arch}
179
178
180 for wxs, rel_path in SUPPORT_WXS:
179 for wxs, rel_path in SUPPORT_WXS:
181 wxs = source_dir / 'contrib' / 'packaging' / 'wix' / wxs
180 wxs = source_dir / 'contrib' / 'packaging' / 'wix' / wxs
182 wxs_source_dir = source_dir / rel_path
181 wxs_source_dir = source_dir / rel_path
183 run_candle(wix_path, build_dir, wxs, wxs_source_dir, defines=defines)
182 run_candle(wix_path, build_dir, wxs, wxs_source_dir, defines=defines)
184
183
185 source = source_dir / 'contrib' / 'packaging' / 'wix' / 'mercurial.wxs'
184 source = source_dir / 'contrib' / 'packaging' / 'wix' / 'mercurial.wxs'
186 defines['Version'] = version
185 defines['Version'] = version
187 defines['Comments'] = 'Installs Mercurial version %s' % version
186 defines['Comments'] = 'Installs Mercurial version %s' % version
188 defines['VCRedistSrcDir'] = str(hg_build_dir)
187 defines['VCRedistSrcDir'] = str(hg_build_dir)
189
188
190 run_candle(wix_path, build_dir, source, source_build_rel, defines=defines)
189 run_candle(wix_path, build_dir, source, source_build_rel, defines=defines)
191
190
192 msi_path = source_dir / 'dist' / (
191 msi_path = source_dir / 'dist' / (
193 '%s-%s-%s.msi' % (msi_name, version, arch))
192 '%s-%s-%s.msi' % (msi_name, version, arch))
194
193
195 args = [
194 args = [
196 str(wix_path / 'light.exe'),
195 str(wix_path / 'light.exe'),
197 '-nologo',
196 '-nologo',
198 '-ext', 'WixUIExtension',
197 '-ext', 'WixUIExtension',
199 '-sw1076',
198 '-sw1076',
200 '-spdb',
199 '-spdb',
201 '-o', str(msi_path),
200 '-o', str(msi_path),
202 ]
201 ]
203
202
204 for source, rel_path in SUPPORT_WXS:
203 for source, rel_path in SUPPORT_WXS:
205 assert source.endswith('.wxs')
204 assert source.endswith('.wxs')
206 args.append(str(build_dir / ('%s.wixobj' % source[:-4])))
205 args.append(str(build_dir / ('%s.wixobj' % source[:-4])))
207
206
208 args.append(str(build_dir / 'mercurial.wixobj'))
207 args.append(str(build_dir / 'mercurial.wixobj'))
209
208
210 subprocess.run(args, cwd=str(source_dir), check=True)
209 subprocess.run(args, cwd=str(source_dir), check=True)
211
210
212 print('%s created' % msi_path)
211 print('%s created' % msi_path)
213
212
214 return {
213 return {
215 'msi_path': msi_path,
214 'msi_path': msi_path,
216 }
215 }
217
216
218
217
219 def build_signed_installer(source_dir: pathlib.Path, python_exe: pathlib.Path,
218 def build_signed_installer(source_dir: pathlib.Path, python_exe: pathlib.Path,
220 name: str, version=None, subject_name=None,
219 name: str, version=None, subject_name=None,
221 cert_path=None, cert_password=None,
220 cert_path=None, cert_password=None,
222 timestamp_url=None):
221 timestamp_url=None):
223 """Build an installer with signed executables."""
222 """Build an installer with signed executables."""
224
223
225 post_build_fn = make_post_build_signing_fn(
224 post_build_fn = make_post_build_signing_fn(
226 name,
225 name,
227 subject_name=subject_name,
226 subject_name=subject_name,
228 cert_path=cert_path,
227 cert_path=cert_path,
229 cert_password=cert_password,
228 cert_password=cert_password,
230 timestamp_url=timestamp_url)
229 timestamp_url=timestamp_url)
231
230
232 info = build_installer(source_dir, python_exe=python_exe,
231 info = build_installer(source_dir, python_exe=python_exe,
233 msi_name=name.lower(), version=version,
232 msi_name=name.lower(), version=version,
234 post_build_fn=post_build_fn)
233 post_build_fn=post_build_fn)
235
234
236 description = '%s %s' % (name, version)
235 description = '%s %s' % (name, version)
237
236
238 sign_with_signtool(info['msi_path'], description,
237 sign_with_signtool(info['msi_path'], description,
239 subject_name=subject_name, cert_path=cert_path,
238 subject_name=subject_name, cert_path=cert_path,
240 cert_password=cert_password, timestamp_url=timestamp_url)
239 cert_password=cert_password, timestamp_url=timestamp_url)
@@ -1,17 +1,13 b''
1 #
1 #
2 # This file is autogenerated by pip-compile
2 # This file is autogenerated by pip-compile
3 # To update, run:
3 # To update, run:
4 #
4 #
5 # pip-compile --generate-hashes contrib/packaging/wix/requirements.txt.in -o contrib/packaging/wix/requirements.txt
5 # pip-compile --generate-hashes contrib/packaging/wix/requirements.txt.in -o contrib/packaging/wix/requirements.txt
6 #
6 #
7 docutils==0.14 \
7 docutils==0.14 \
8 --hash=sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6 \
8 --hash=sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6 \
9 --hash=sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274 \
9 --hash=sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274 \
10 --hash=sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6
10 --hash=sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6
11 enum==0.4.7 \
12 --hash=sha256:8c7cf3587eda51008bcc1eed99ea2c331ccd265c231dbaa95ec5258d3dc03100
13 future==0.17.1 \
14 --hash=sha256:67045236dcfd6816dc439556d009594abf643e5eb48992e36beac09c2ca659b8
15 pygments==2.3.1 \
11 pygments==2.3.1 \
16 --hash=sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a \
12 --hash=sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a \
17 --hash=sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d
13 --hash=sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d
@@ -1,4 +1,2 b''
1 docutils
1 docutils
2 enum
3 future
4 pygments
2 pygments
General Comments 0
You need to be logged in to leave comments. Login now