##// END OF EJS Templates
wix: add functionality to inject additional Features into installer...
Augie Fackler -
r42216:1711f581 default
parent child Browse files
Show More
@@ -1,321 +1,327 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 import tempfile
14 import tempfile
15 import typing
15 import typing
16 import xml.dom.minidom
16 import xml.dom.minidom
17
17
18 from .downloads import (
18 from .downloads import (
19 download_entry,
19 download_entry,
20 )
20 )
21 from .py2exe import (
21 from .py2exe import (
22 build_py2exe,
22 build_py2exe,
23 )
23 )
24 from .util import (
24 from .util import (
25 extract_zip_to_directory,
25 extract_zip_to_directory,
26 sign_with_signtool,
26 sign_with_signtool,
27 )
27 )
28
28
29
29
30 SUPPORT_WXS = [
30 SUPPORT_WXS = [
31 ('contrib.wxs', r'contrib'),
31 ('contrib.wxs', r'contrib'),
32 ('dist.wxs', r'dist'),
32 ('dist.wxs', r'dist'),
33 ('doc.wxs', r'doc'),
33 ('doc.wxs', r'doc'),
34 ('help.wxs', r'mercurial\help'),
34 ('help.wxs', r'mercurial\help'),
35 ('i18n.wxs', r'i18n'),
35 ('i18n.wxs', r'i18n'),
36 ('locale.wxs', r'mercurial\locale'),
36 ('locale.wxs', r'mercurial\locale'),
37 ('templates.wxs', r'mercurial\templates'),
37 ('templates.wxs', r'mercurial\templates'),
38 ]
38 ]
39
39
40
40
41 EXTRA_PACKAGES = {
41 EXTRA_PACKAGES = {
42 'distutils',
42 'distutils',
43 'pygments',
43 'pygments',
44 }
44 }
45
45
46
46
47 def find_version(source_dir: pathlib.Path):
47 def find_version(source_dir: pathlib.Path):
48 version_py = source_dir / 'mercurial' / '__version__.py'
48 version_py = source_dir / 'mercurial' / '__version__.py'
49
49
50 with version_py.open('r', encoding='utf-8') as fh:
50 with version_py.open('r', encoding='utf-8') as fh:
51 source = fh.read().strip()
51 source = fh.read().strip()
52
52
53 m = re.search('version = b"(.*)"', source)
53 m = re.search('version = b"(.*)"', source)
54 return m.group(1)
54 return m.group(1)
55
55
56
56
57 def normalize_version(version):
57 def normalize_version(version):
58 """Normalize Mercurial version string so WiX accepts it.
58 """Normalize Mercurial version string so WiX accepts it.
59
59
60 Version strings have to be numeric X.Y.Z.
60 Version strings have to be numeric X.Y.Z.
61 """
61 """
62
62
63 if '+' in version:
63 if '+' in version:
64 version, extra = version.split('+', 1)
64 version, extra = version.split('+', 1)
65 else:
65 else:
66 extra = None
66 extra = None
67
67
68 # 4.9rc0
68 # 4.9rc0
69 if version[:-1].endswith('rc'):
69 if version[:-1].endswith('rc'):
70 version = version[:-3]
70 version = version[:-3]
71
71
72 versions = [int(v) for v in version.split('.')]
72 versions = [int(v) for v in version.split('.')]
73 while len(versions) < 3:
73 while len(versions) < 3:
74 versions.append(0)
74 versions.append(0)
75
75
76 major, minor, build = versions[:3]
76 major, minor, build = versions[:3]
77
77
78 if extra:
78 if extra:
79 # <commit count>-<hash>+<date>
79 # <commit count>-<hash>+<date>
80 build = int(extra.split('-')[0])
80 build = int(extra.split('-')[0])
81
81
82 return '.'.join('%d' % x for x in (major, minor, build))
82 return '.'.join('%d' % x for x in (major, minor, build))
83
83
84
84
85 def ensure_vc90_merge_modules(build_dir):
85 def ensure_vc90_merge_modules(build_dir):
86 x86 = (
86 x86 = (
87 download_entry('vc9-crt-x86-msm', build_dir,
87 download_entry('vc9-crt-x86-msm', build_dir,
88 local_name='microsoft.vcxx.crt.x86_msm.msm')[0],
88 local_name='microsoft.vcxx.crt.x86_msm.msm')[0],
89 download_entry('vc9-crt-x86-msm-policy', build_dir,
89 download_entry('vc9-crt-x86-msm-policy', build_dir,
90 local_name='policy.x.xx.microsoft.vcxx.crt.x86_msm.msm')[0]
90 local_name='policy.x.xx.microsoft.vcxx.crt.x86_msm.msm')[0]
91 )
91 )
92
92
93 x64 = (
93 x64 = (
94 download_entry('vc9-crt-x64-msm', build_dir,
94 download_entry('vc9-crt-x64-msm', build_dir,
95 local_name='microsoft.vcxx.crt.x64_msm.msm')[0],
95 local_name='microsoft.vcxx.crt.x64_msm.msm')[0],
96 download_entry('vc9-crt-x64-msm-policy', build_dir,
96 download_entry('vc9-crt-x64-msm-policy', build_dir,
97 local_name='policy.x.xx.microsoft.vcxx.crt.x64_msm.msm')[0]
97 local_name='policy.x.xx.microsoft.vcxx.crt.x64_msm.msm')[0]
98 )
98 )
99 return {
99 return {
100 'x86': x86,
100 'x86': x86,
101 'x64': x64,
101 'x64': x64,
102 }
102 }
103
103
104
104
105 def run_candle(wix, cwd, wxs, source_dir, defines=None):
105 def run_candle(wix, cwd, wxs, source_dir, defines=None):
106 args = [
106 args = [
107 str(wix / 'candle.exe'),
107 str(wix / 'candle.exe'),
108 '-nologo',
108 '-nologo',
109 str(wxs),
109 str(wxs),
110 '-dSourceDir=%s' % source_dir,
110 '-dSourceDir=%s' % source_dir,
111 ]
111 ]
112
112
113 if defines:
113 if defines:
114 args.extend('-d%s=%s' % define for define in sorted(defines.items()))
114 args.extend('-d%s=%s' % define for define in sorted(defines.items()))
115
115
116 subprocess.run(args, cwd=str(cwd), check=True)
116 subprocess.run(args, cwd=str(cwd), check=True)
117
117
118
118
119 def make_post_build_signing_fn(name, subject_name=None, cert_path=None,
119 def make_post_build_signing_fn(name, subject_name=None, cert_path=None,
120 cert_password=None, timestamp_url=None):
120 cert_password=None, timestamp_url=None):
121 """Create a callable that will use signtool to sign hg.exe."""
121 """Create a callable that will use signtool to sign hg.exe."""
122
122
123 def post_build_sign(source_dir, build_dir, dist_dir, version):
123 def post_build_sign(source_dir, build_dir, dist_dir, version):
124 description = '%s %s' % (name, version)
124 description = '%s %s' % (name, version)
125
125
126 sign_with_signtool(dist_dir / 'hg.exe', description,
126 sign_with_signtool(dist_dir / 'hg.exe', description,
127 subject_name=subject_name, cert_path=cert_path,
127 subject_name=subject_name, cert_path=cert_path,
128 cert_password=cert_password,
128 cert_password=cert_password,
129 timestamp_url=timestamp_url)
129 timestamp_url=timestamp_url)
130
130
131 return post_build_sign
131 return post_build_sign
132
132
133
133
134 LIBRARIES_XML = '''
134 LIBRARIES_XML = '''
135 <?xml version="1.0" encoding="utf-8"?>
135 <?xml version="1.0" encoding="utf-8"?>
136 <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
136 <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
137
137
138 <?include {wix_dir}/guids.wxi ?>
138 <?include {wix_dir}/guids.wxi ?>
139 <?include {wix_dir}/defines.wxi ?>
139 <?include {wix_dir}/defines.wxi ?>
140
140
141 <Fragment>
141 <Fragment>
142 <DirectoryRef Id="INSTALLDIR" FileSource="$(var.SourceDir)">
142 <DirectoryRef Id="INSTALLDIR" FileSource="$(var.SourceDir)">
143 <Directory Id="libdir" Name="lib" FileSource="$(var.SourceDir)/lib">
143 <Directory Id="libdir" Name="lib" FileSource="$(var.SourceDir)/lib">
144 <Component Id="libOutput" Guid="$(var.lib.guid)" Win64='$(var.IsX64)'>
144 <Component Id="libOutput" Guid="$(var.lib.guid)" Win64='$(var.IsX64)'>
145 </Component>
145 </Component>
146 </Directory>
146 </Directory>
147 </DirectoryRef>
147 </DirectoryRef>
148 </Fragment>
148 </Fragment>
149 </Wix>
149 </Wix>
150 '''.lstrip()
150 '''.lstrip()
151
151
152
152
153 def make_libraries_xml(wix_dir: pathlib.Path, dist_dir: pathlib.Path):
153 def make_libraries_xml(wix_dir: pathlib.Path, dist_dir: pathlib.Path):
154 """Make XML data for library components WXS."""
154 """Make XML data for library components WXS."""
155 # We can't use ElementTree because it doesn't handle the
155 # We can't use ElementTree because it doesn't handle the
156 # <?include ?> directives.
156 # <?include ?> directives.
157 doc = xml.dom.minidom.parseString(
157 doc = xml.dom.minidom.parseString(
158 LIBRARIES_XML.format(wix_dir=str(wix_dir)))
158 LIBRARIES_XML.format(wix_dir=str(wix_dir)))
159
159
160 component = doc.getElementsByTagName('Component')[0]
160 component = doc.getElementsByTagName('Component')[0]
161
161
162 f = doc.createElement('File')
162 f = doc.createElement('File')
163 f.setAttribute('Name', 'library.zip')
163 f.setAttribute('Name', 'library.zip')
164 f.setAttribute('KeyPath', 'yes')
164 f.setAttribute('KeyPath', 'yes')
165 component.appendChild(f)
165 component.appendChild(f)
166
166
167 lib_dir = dist_dir / 'lib'
167 lib_dir = dist_dir / 'lib'
168
168
169 for p in sorted(lib_dir.iterdir()):
169 for p in sorted(lib_dir.iterdir()):
170 if not p.name.endswith(('.dll', '.pyd')):
170 if not p.name.endswith(('.dll', '.pyd')):
171 continue
171 continue
172
172
173 f = doc.createElement('File')
173 f = doc.createElement('File')
174 f.setAttribute('Name', p.name)
174 f.setAttribute('Name', p.name)
175 component.appendChild(f)
175 component.appendChild(f)
176
176
177 return doc.toprettyxml()
177 return doc.toprettyxml()
178
178
179
179
180 def build_installer(source_dir: pathlib.Path, python_exe: pathlib.Path,
180 def build_installer(source_dir: pathlib.Path, python_exe: pathlib.Path,
181 msi_name='mercurial', version=None, post_build_fn=None,
181 msi_name='mercurial', version=None, post_build_fn=None,
182 extra_packages_script: typing.Optional[str]=None,
182 extra_packages_script=None,
183 extra_wxs:typing.Optional[typing.Dict[str,str]]=None):
183 extra_wxs:typing.Optional[typing.Dict[str,str]]=None,
184 extra_features:typing.Optional[typing.List[str]]=None):
184 """Build a WiX MSI installer.
185 """Build a WiX MSI installer.
185
186
186 ``source_dir`` is the path to the Mercurial source tree to use.
187 ``source_dir`` is the path to the Mercurial source tree to use.
187 ``arch`` is the target architecture. either ``x86`` or ``x64``.
188 ``arch`` is the target architecture. either ``x86`` or ``x64``.
188 ``python_exe`` is the path to the Python executable to use/bundle.
189 ``python_exe`` is the path to the Python executable to use/bundle.
189 ``version`` is the Mercurial version string. If not defined,
190 ``version`` is the Mercurial version string. If not defined,
190 ``mercurial/__version__.py`` will be consulted.
191 ``mercurial/__version__.py`` will be consulted.
191 ``post_build_fn`` is a callable that will be called after building
192 ``post_build_fn`` is a callable that will be called after building
192 Mercurial but before invoking WiX. It can be used to e.g. facilitate
193 Mercurial but before invoking WiX. It can be used to e.g. facilitate
193 signing. It is passed the paths to the Mercurial source, build, and
194 signing. It is passed the paths to the Mercurial source, build, and
194 dist directories and the resolved Mercurial version.
195 dist directories and the resolved Mercurial version.
195 ``extra_packages_script`` is a command to be run to inject extra packages
196 ``extra_packages_script`` is a command to be run to inject extra packages
196 into the py2exe binary. It should stage packages into the virtualenv and
197 into the py2exe binary. It should stage packages into the virtualenv and
197 print a null byte followed by a newline-separated list of packages that
198 print a null byte followed by a newline-separated list of packages that
198 should be included in the exe.
199 should be included in the exe.
199 ``extra_wxs`` is a dict of {wxs_name: working_dir_for_wxs_build}.
200 ``extra_wxs`` is a dict of {wxs_name: working_dir_for_wxs_build}.
201 ``extra_features`` is a list of additional named Features to include in
202 the build. These must match Feature names in one of the wxs scripts.
200 """
203 """
201 arch = 'x64' if r'\x64' in os.environ.get('LIB', '') else 'x86'
204 arch = 'x64' if r'\x64' in os.environ.get('LIB', '') else 'x86'
202
205
203 hg_build_dir = source_dir / 'build'
206 hg_build_dir = source_dir / 'build'
204 dist_dir = source_dir / 'dist'
207 dist_dir = source_dir / 'dist'
205 wix_dir = source_dir / 'contrib' / 'packaging' / 'wix'
208 wix_dir = source_dir / 'contrib' / 'packaging' / 'wix'
206
209
207 requirements_txt = wix_dir / 'requirements.txt'
210 requirements_txt = wix_dir / 'requirements.txt'
208
211
209 build_py2exe(source_dir, hg_build_dir,
212 build_py2exe(source_dir, hg_build_dir,
210 python_exe, 'wix', requirements_txt,
213 python_exe, 'wix', requirements_txt,
211 extra_packages=EXTRA_PACKAGES,
214 extra_packages=EXTRA_PACKAGES,
212 extra_packages_script=extra_packages_script)
215 extra_packages_script=extra_packages_script)
213
216
214 version = version or normalize_version(find_version(source_dir))
217 version = version or normalize_version(find_version(source_dir))
215 print('using version string: %s' % version)
218 print('using version string: %s' % version)
216
219
217 if post_build_fn:
220 if post_build_fn:
218 post_build_fn(source_dir, hg_build_dir, dist_dir, version)
221 post_build_fn(source_dir, hg_build_dir, dist_dir, version)
219
222
220 build_dir = hg_build_dir / ('wix-%s' % arch)
223 build_dir = hg_build_dir / ('wix-%s' % arch)
221
224
222 build_dir.mkdir(exist_ok=True)
225 build_dir.mkdir(exist_ok=True)
223
226
224 wix_pkg, wix_entry = download_entry('wix', hg_build_dir)
227 wix_pkg, wix_entry = download_entry('wix', hg_build_dir)
225 wix_path = hg_build_dir / ('wix-%s' % wix_entry['version'])
228 wix_path = hg_build_dir / ('wix-%s' % wix_entry['version'])
226
229
227 if not wix_path.exists():
230 if not wix_path.exists():
228 extract_zip_to_directory(wix_pkg, wix_path)
231 extract_zip_to_directory(wix_pkg, wix_path)
229
232
230 ensure_vc90_merge_modules(hg_build_dir)
233 ensure_vc90_merge_modules(hg_build_dir)
231
234
232 source_build_rel = pathlib.Path(os.path.relpath(source_dir, build_dir))
235 source_build_rel = pathlib.Path(os.path.relpath(source_dir, build_dir))
233
236
234 defines = {'Platform': arch}
237 defines = {'Platform': arch}
235
238
236 for wxs, rel_path in SUPPORT_WXS:
239 for wxs, rel_path in SUPPORT_WXS:
237 wxs = wix_dir / wxs
240 wxs = wix_dir / wxs
238 wxs_source_dir = source_dir / rel_path
241 wxs_source_dir = source_dir / rel_path
239 run_candle(wix_path, build_dir, wxs, wxs_source_dir, defines=defines)
242 run_candle(wix_path, build_dir, wxs, wxs_source_dir, defines=defines)
240
243
241 for source, rel_path in sorted((extra_wxs or {}).items()):
244 for source, rel_path in sorted((extra_wxs or {}).items()):
242 run_candle(wix_path, build_dir, source, rel_path, defines=defines)
245 run_candle(wix_path, build_dir, source, rel_path, defines=defines)
243
246
244 # candle.exe doesn't like when we have an open handle on the file.
247 # candle.exe doesn't like when we have an open handle on the file.
245 # So use TemporaryDirectory() instead of NamedTemporaryFile().
248 # So use TemporaryDirectory() instead of NamedTemporaryFile().
246 with tempfile.TemporaryDirectory() as td:
249 with tempfile.TemporaryDirectory() as td:
247 td = pathlib.Path(td)
250 td = pathlib.Path(td)
248
251
249 tf = td / 'library.wxs'
252 tf = td / 'library.wxs'
250 with tf.open('w') as fh:
253 with tf.open('w') as fh:
251 fh.write(make_libraries_xml(wix_dir, dist_dir))
254 fh.write(make_libraries_xml(wix_dir, dist_dir))
252
255
253 run_candle(wix_path, build_dir, tf, dist_dir, defines=defines)
256 run_candle(wix_path, build_dir, tf, dist_dir, defines=defines)
254
257
255 source = wix_dir / 'mercurial.wxs'
258 source = wix_dir / 'mercurial.wxs'
256 defines['Version'] = version
259 defines['Version'] = version
257 defines['Comments'] = 'Installs Mercurial version %s' % version
260 defines['Comments'] = 'Installs Mercurial version %s' % version
258 defines['VCRedistSrcDir'] = str(hg_build_dir)
261 defines['VCRedistSrcDir'] = str(hg_build_dir)
262 if extra_features:
263 assert all(';' not in f for f in extra_features)
264 defines['MercurialExtraFeatures'] = ';'.join(extra_features)
259
265
260 run_candle(wix_path, build_dir, source, source_build_rel, defines=defines)
266 run_candle(wix_path, build_dir, source, source_build_rel, defines=defines)
261
267
262 msi_path = source_dir / 'dist' / (
268 msi_path = source_dir / 'dist' / (
263 '%s-%s-%s.msi' % (msi_name, version, arch))
269 '%s-%s-%s.msi' % (msi_name, version, arch))
264
270
265 args = [
271 args = [
266 str(wix_path / 'light.exe'),
272 str(wix_path / 'light.exe'),
267 '-nologo',
273 '-nologo',
268 '-ext', 'WixUIExtension',
274 '-ext', 'WixUIExtension',
269 '-sw1076',
275 '-sw1076',
270 '-spdb',
276 '-spdb',
271 '-o', str(msi_path),
277 '-o', str(msi_path),
272 ]
278 ]
273
279
274 for source, rel_path in SUPPORT_WXS:
280 for source, rel_path in SUPPORT_WXS:
275 assert source.endswith('.wxs')
281 assert source.endswith('.wxs')
276 args.append(str(build_dir / ('%s.wixobj' % source[:-4])))
282 args.append(str(build_dir / ('%s.wixobj' % source[:-4])))
277
283
278 for source, rel_path in sorted((extra_wxs or {}).items()):
284 for source, rel_path in sorted((extra_wxs or {}).items()):
279 assert source.endswith('.wxs')
285 assert source.endswith('.wxs')
280 source = os.path.basename(source)
286 source = os.path.basename(source)
281 args.append(str(build_dir / ('%s.wixobj' % source[:-4])))
287 args.append(str(build_dir / ('%s.wixobj' % source[:-4])))
282
288
283 args.extend([
289 args.extend([
284 str(build_dir / 'library.wixobj'),
290 str(build_dir / 'library.wixobj'),
285 str(build_dir / 'mercurial.wixobj'),
291 str(build_dir / 'mercurial.wixobj'),
286 ])
292 ])
287
293
288 subprocess.run(args, cwd=str(source_dir), check=True)
294 subprocess.run(args, cwd=str(source_dir), check=True)
289
295
290 print('%s created' % msi_path)
296 print('%s created' % msi_path)
291
297
292 return {
298 return {
293 'msi_path': msi_path,
299 'msi_path': msi_path,
294 }
300 }
295
301
296
302
297 def build_signed_installer(source_dir: pathlib.Path, python_exe: pathlib.Path,
303 def build_signed_installer(source_dir: pathlib.Path, python_exe: pathlib.Path,
298 name: str, version=None, subject_name=None,
304 name: str, version=None, subject_name=None,
299 cert_path=None, cert_password=None,
305 cert_path=None, cert_password=None,
300 timestamp_url=None, extra_packages_script=None,
306 timestamp_url=None, extra_packages_script=None,
301 extra_wxs=None):
307 extra_wxs=None, extra_features=None):
302 """Build an installer with signed executables."""
308 """Build an installer with signed executables."""
303
309
304 post_build_fn = make_post_build_signing_fn(
310 post_build_fn = make_post_build_signing_fn(
305 name,
311 name,
306 subject_name=subject_name,
312 subject_name=subject_name,
307 cert_path=cert_path,
313 cert_path=cert_path,
308 cert_password=cert_password,
314 cert_password=cert_password,
309 timestamp_url=timestamp_url)
315 timestamp_url=timestamp_url)
310
316
311 info = build_installer(source_dir, python_exe=python_exe,
317 info = build_installer(source_dir, python_exe=python_exe,
312 msi_name=name.lower(), version=version,
318 msi_name=name.lower(), version=version,
313 post_build_fn=post_build_fn,
319 post_build_fn=post_build_fn,
314 extra_packages_script=extra_packages_script,
320 extra_packages_script=extra_packages_script,
315 extra_wxs=extra_wxs)
321 extra_wxs=extra_wxs, extra_features=extra_features)
316
322
317 description = '%s %s' % (name, version)
323 description = '%s %s' % (name, version)
318
324
319 sign_with_signtool(info['msi_path'], description,
325 sign_with_signtool(info['msi_path'], description,
320 subject_name=subject_name, cert_path=cert_path,
326 subject_name=subject_name, cert_path=cert_path,
321 cert_password=cert_password, timestamp_url=timestamp_url)
327 cert_password=cert_password, timestamp_url=timestamp_url)
@@ -1,76 +1,81 b''
1 #!/usr/bin/env python3
1 #!/usr/bin/env python3
2 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
2 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
3 #
3 #
4 # This software may be used and distributed according to the terms of the
4 # This software may be used and distributed according to the terms of the
5 # GNU General Public License version 2 or any later version.
5 # GNU General Public License version 2 or any later version.
6
6
7 # no-check-code because Python 3 native.
7 # no-check-code because Python 3 native.
8
8
9 """Code to build Mercurial WiX installer."""
9 """Code to build Mercurial WiX installer."""
10
10
11 import argparse
11 import argparse
12 import os
12 import os
13 import pathlib
13 import pathlib
14 import sys
14 import sys
15
15
16
16
17 if __name__ == '__main__':
17 if __name__ == '__main__':
18 parser = argparse.ArgumentParser()
18 parser = argparse.ArgumentParser()
19
19
20 parser.add_argument('--name',
20 parser.add_argument('--name',
21 help='Application name',
21 help='Application name',
22 default='Mercurial')
22 default='Mercurial')
23 parser.add_argument('--python',
23 parser.add_argument('--python',
24 help='Path to Python executable to use',
24 help='Path to Python executable to use',
25 required=True)
25 required=True)
26 parser.add_argument('--sign-sn',
26 parser.add_argument('--sign-sn',
27 help='Subject name (or fragment thereof) of certificate '
27 help='Subject name (or fragment thereof) of certificate '
28 'to use for signing')
28 'to use for signing')
29 parser.add_argument('--sign-cert',
29 parser.add_argument('--sign-cert',
30 help='Path to certificate to use for signing')
30 help='Path to certificate to use for signing')
31 parser.add_argument('--sign-password',
31 parser.add_argument('--sign-password',
32 help='Password for signing certificate')
32 help='Password for signing certificate')
33 parser.add_argument('--sign-timestamp-url',
33 parser.add_argument('--sign-timestamp-url',
34 help='URL of timestamp server to use for signing')
34 help='URL of timestamp server to use for signing')
35 parser.add_argument('--version',
35 parser.add_argument('--version',
36 help='Version string to use')
36 help='Version string to use')
37 parser.add_argument('--extra-packages-script',
37 parser.add_argument('--extra-packages-script',
38 help=('Script to execute to include extra packages in '
38 help=('Script to execute to include extra packages in '
39 'py2exe binary.'))
39 'py2exe binary.'))
40 parser.add_argument('--extra-wxs',
40 parser.add_argument('--extra-wxs',
41 help='CSV of path_to_wxs_file=working_dir_for_wxs_file')
41 help='CSV of path_to_wxs_file=working_dir_for_wxs_file')
42 parser.add_argument('--extra-features',
43 help=('CSV of extra feature names to include '
44 'in the installer from the extra wxs files'))
42
45
43 args = parser.parse_args()
46 args = parser.parse_args()
44
47
45 here = pathlib.Path(os.path.abspath(os.path.dirname(__file__)))
48 here = pathlib.Path(os.path.abspath(os.path.dirname(__file__)))
46 source_dir = here.parent.parent.parent
49 source_dir = here.parent.parent.parent
47
50
48 sys.path.insert(0, str(source_dir / 'contrib' / 'packaging'))
51 sys.path.insert(0, str(source_dir / 'contrib' / 'packaging'))
49
52
50 from hgpackaging.wix import (
53 from hgpackaging.wix import (
51 build_installer,
54 build_installer,
52 build_signed_installer,
55 build_signed_installer,
53 )
56 )
54
57
55 fn = build_installer
58 fn = build_installer
56 kwargs = {
59 kwargs = {
57 'source_dir': source_dir,
60 'source_dir': source_dir,
58 'python_exe': pathlib.Path(args.python),
61 'python_exe': pathlib.Path(args.python),
59 'version': args.version,
62 'version': args.version,
60 }
63 }
61
64
62 if args.extra_packages_script:
65 if args.extra_packages_script:
63 kwargs['extra_packages_script'] = args.extra_packages_script
66 kwargs['extra_packages_script'] = args.extra_packages_script
64 if args.extra_wxs:
67 if args.extra_wxs:
65 kwargs['extra_wxs'] = dict(
68 kwargs['extra_wxs'] = dict(
66 thing.split("=") for thing in args.extra_wxs.split(','))
69 thing.split("=") for thing in args.extra_wxs.split(','))
70 if args.extra_features:
71 kwargs['extra_features'] = args.extra_features.split(',')
67
72
68 if args.sign_sn or args.sign_cert:
73 if args.sign_sn or args.sign_cert:
69 fn = build_signed_installer
74 fn = build_signed_installer
70 kwargs['name'] = args.name
75 kwargs['name'] = args.name
71 kwargs['subject_name'] = args.sign_sn
76 kwargs['subject_name'] = args.sign_sn
72 kwargs['cert_path'] = args.sign_cert
77 kwargs['cert_path'] = args.sign_cert
73 kwargs['cert_password'] = args.sign_password
78 kwargs['cert_password'] = args.sign_password
74 kwargs['timestamp_url'] = args.sign_timestamp_url
79 kwargs['timestamp_url'] = args.sign_timestamp_url
75
80
76 fn(**kwargs)
81 fn(**kwargs)
@@ -1,162 +1,167 b''
1 <?xml version='1.0' encoding='windows-1252'?>
1 <?xml version='1.0' encoding='windows-1252'?>
2 <Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
2 <Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
3
3
4 <!-- Copyright 2010 Steve Borho <steve@borho.org>
4 <!-- Copyright 2010 Steve Borho <steve@borho.org>
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 <?include guids.wxi ?>
9 <?include guids.wxi ?>
10 <?include defines.wxi ?>
10 <?include defines.wxi ?>
11
11
12 <?if $(var.Platform) = "x64" ?>
12 <?if $(var.Platform) = "x64" ?>
13 <?define PFolder = ProgramFiles64Folder ?>
13 <?define PFolder = ProgramFiles64Folder ?>
14 <?else?>
14 <?else?>
15 <?define PFolder = ProgramFilesFolder ?>
15 <?define PFolder = ProgramFilesFolder ?>
16 <?endif?>
16 <?endif?>
17
17
18 <Product Id='*'
18 <Product Id='*'
19 Name='Mercurial $(var.Version) ($(var.Platform))'
19 Name='Mercurial $(var.Version) ($(var.Platform))'
20 UpgradeCode='$(var.ProductUpgradeCode)'
20 UpgradeCode='$(var.ProductUpgradeCode)'
21 Language='1033' Codepage='1252' Version='$(var.Version)'
21 Language='1033' Codepage='1252' Version='$(var.Version)'
22 Manufacturer='Matt Mackall and others'>
22 Manufacturer='Matt Mackall and others'>
23
23
24 <Package Id='*'
24 <Package Id='*'
25 Keywords='Installer'
25 Keywords='Installer'
26 Description="Mercurial distributed SCM (version $(var.Version))"
26 Description="Mercurial distributed SCM (version $(var.Version))"
27 Comments='$(var.Comments)'
27 Comments='$(var.Comments)'
28 Platform='$(var.Platform)'
28 Platform='$(var.Platform)'
29 Manufacturer='Matt Mackall and others'
29 Manufacturer='Matt Mackall and others'
30 InstallerVersion='300' Languages='1033' Compressed='yes' SummaryCodepage='1252' />
30 InstallerVersion='300' Languages='1033' Compressed='yes' SummaryCodepage='1252' />
31
31
32 <Media Id='1' Cabinet='mercurial.cab' EmbedCab='yes' DiskPrompt='CD-ROM #1'
32 <Media Id='1' Cabinet='mercurial.cab' EmbedCab='yes' DiskPrompt='CD-ROM #1'
33 CompressionLevel='high' />
33 CompressionLevel='high' />
34 <Property Id='DiskPrompt' Value="Mercurial $(var.Version) Installation [1]" />
34 <Property Id='DiskPrompt' Value="Mercurial $(var.Version) Installation [1]" />
35
35
36 <Condition Message='Mercurial MSI installers require Windows XP or higher'>
36 <Condition Message='Mercurial MSI installers require Windows XP or higher'>
37 VersionNT >= 501
37 VersionNT >= 501
38 </Condition>
38 </Condition>
39
39
40 <Property Id="INSTALLDIR">
40 <Property Id="INSTALLDIR">
41 <ComponentSearch Id='SearchForMainExecutableComponent'
41 <ComponentSearch Id='SearchForMainExecutableComponent'
42 Guid='$(var.ComponentMainExecutableGUID)' />
42 Guid='$(var.ComponentMainExecutableGUID)' />
43 </Property>
43 </Property>
44
44
45 <!--Property Id='ARPCOMMENTS'>any comments</Property-->
45 <!--Property Id='ARPCOMMENTS'>any comments</Property-->
46 <Property Id='ARPCONTACT'>mercurial@mercurial-scm.org</Property>
46 <Property Id='ARPCONTACT'>mercurial@mercurial-scm.org</Property>
47 <Property Id='ARPHELPLINK'>https://mercurial-scm.org/wiki/</Property>
47 <Property Id='ARPHELPLINK'>https://mercurial-scm.org/wiki/</Property>
48 <Property Id='ARPURLINFOABOUT'>https://mercurial-scm.org/about/</Property>
48 <Property Id='ARPURLINFOABOUT'>https://mercurial-scm.org/about/</Property>
49 <Property Id='ARPURLUPDATEINFO'>https://mercurial-scm.org/downloads/</Property>
49 <Property Id='ARPURLUPDATEINFO'>https://mercurial-scm.org/downloads/</Property>
50 <Property Id='ARPHELPTELEPHONE'>https://mercurial-scm.org/wiki/Support</Property>
50 <Property Id='ARPHELPTELEPHONE'>https://mercurial-scm.org/wiki/Support</Property>
51 <Property Id='ARPPRODUCTICON'>hgIcon.ico</Property>
51 <Property Id='ARPPRODUCTICON'>hgIcon.ico</Property>
52
52
53 <Property Id='INSTALLEDMERCURIALPRODUCTS' Secure='yes'></Property>
53 <Property Id='INSTALLEDMERCURIALPRODUCTS' Secure='yes'></Property>
54 <Property Id='REINSTALLMODE'>amus</Property>
54 <Property Id='REINSTALLMODE'>amus</Property>
55
55
56 <!--Auto-accept the license page-->
56 <!--Auto-accept the license page-->
57 <Property Id='LicenseAccepted'>1</Property>
57 <Property Id='LicenseAccepted'>1</Property>
58
58
59 <Directory Id='TARGETDIR' Name='SourceDir'>
59 <Directory Id='TARGETDIR' Name='SourceDir'>
60 <Directory Id='$(var.PFolder)' Name='PFiles'>
60 <Directory Id='$(var.PFolder)' Name='PFiles'>
61 <Directory Id='INSTALLDIR' Name='Mercurial'>
61 <Directory Id='INSTALLDIR' Name='Mercurial'>
62 <Component Id='MainExecutable' Guid='$(var.ComponentMainExecutableGUID)' Win64='$(var.IsX64)'>
62 <Component Id='MainExecutable' Guid='$(var.ComponentMainExecutableGUID)' Win64='$(var.IsX64)'>
63 <File Id='hgEXE' Name='hg.exe' Source='dist\hg.exe' KeyPath='yes' />
63 <File Id='hgEXE' Name='hg.exe' Source='dist\hg.exe' KeyPath='yes' />
64 <Environment Id="Environment" Name="PATH" Part="last" System="yes"
64 <Environment Id="Environment" Name="PATH" Part="last" System="yes"
65 Permanent="no" Value="[INSTALLDIR]" Action="set" />
65 Permanent="no" Value="[INSTALLDIR]" Action="set" />
66 </Component>
66 </Component>
67 <Component Id='ReadMe' Guid='$(var.ReadMe.guid)' Win64='$(var.IsX64)'>
67 <Component Id='ReadMe' Guid='$(var.ReadMe.guid)' Win64='$(var.IsX64)'>
68 <File Id='ReadMe' Name='ReadMe.html' Source='contrib\win32\ReadMe.html'
68 <File Id='ReadMe' Name='ReadMe.html' Source='contrib\win32\ReadMe.html'
69 KeyPath='yes'/>
69 KeyPath='yes'/>
70 </Component>
70 </Component>
71 <Component Id='COPYING' Guid='$(var.COPYING.guid)' Win64='$(var.IsX64)'>
71 <Component Id='COPYING' Guid='$(var.COPYING.guid)' Win64='$(var.IsX64)'>
72 <File Id='COPYING' Name='COPYING.rtf' Source='contrib\packaging\wix\COPYING.rtf'
72 <File Id='COPYING' Name='COPYING.rtf' Source='contrib\packaging\wix\COPYING.rtf'
73 KeyPath='yes'/>
73 KeyPath='yes'/>
74 </Component>
74 </Component>
75
75
76 <Directory Id='HGRCD' Name='hgrc.d'>
76 <Directory Id='HGRCD' Name='hgrc.d'>
77 <Component Id='mercurial.rc' Guid='$(var.mercurial.rc.guid)' Win64='$(var.IsX64)'>
77 <Component Id='mercurial.rc' Guid='$(var.mercurial.rc.guid)' Win64='$(var.IsX64)'>
78 <File Id='mercurial.rc' Name='Mercurial.rc' Source='contrib\win32\mercurial.ini'
78 <File Id='mercurial.rc' Name='Mercurial.rc' Source='contrib\win32\mercurial.ini'
79 ReadOnly='yes' KeyPath='yes'/>
79 ReadOnly='yes' KeyPath='yes'/>
80 </Component>
80 </Component>
81 <Component Id='mergetools.rc' Guid='$(var.mergetools.rc.guid)' Win64='$(var.IsX64)'>
81 <Component Id='mergetools.rc' Guid='$(var.mergetools.rc.guid)' Win64='$(var.IsX64)'>
82 <File Id='mergetools.rc' Name='MergeTools.rc' Source='mercurial\default.d\mergetools.rc'
82 <File Id='mergetools.rc' Name='MergeTools.rc' Source='mercurial\default.d\mergetools.rc'
83 ReadOnly='yes' KeyPath='yes'/>
83 ReadOnly='yes' KeyPath='yes'/>
84 </Component>
84 </Component>
85 </Directory>
85 </Directory>
86
86
87 </Directory>
87 </Directory>
88 </Directory>
88 </Directory>
89
89
90 <Directory Id="ProgramMenuFolder" Name="Programs">
90 <Directory Id="ProgramMenuFolder" Name="Programs">
91 <Directory Id="ProgramMenuDir" Name="Mercurial $(var.Version)">
91 <Directory Id="ProgramMenuDir" Name="Mercurial $(var.Version)">
92 <Component Id="ProgramMenuDir" Guid="$(var.ProgramMenuDir.guid)" Win64='$(var.IsX64)'>
92 <Component Id="ProgramMenuDir" Guid="$(var.ProgramMenuDir.guid)" Win64='$(var.IsX64)'>
93 <RemoveFolder Id='ProgramMenuDir' On='uninstall' />
93 <RemoveFolder Id='ProgramMenuDir' On='uninstall' />
94 <RegistryValue Root='HKCU' Key='Software\Mercurial\InstallDir' Type='string'
94 <RegistryValue Root='HKCU' Key='Software\Mercurial\InstallDir' Type='string'
95 Value='[INSTALLDIR]' KeyPath='yes' />
95 Value='[INSTALLDIR]' KeyPath='yes' />
96 <Shortcut Id='UrlShortcut' Directory='ProgramMenuDir' Name='Mercurial Web Site'
96 <Shortcut Id='UrlShortcut' Directory='ProgramMenuDir' Name='Mercurial Web Site'
97 Target='[ARPHELPLINK]' Icon="hgIcon.ico" IconIndex='0' />
97 Target='[ARPHELPLINK]' Icon="hgIcon.ico" IconIndex='0' />
98 </Component>
98 </Component>
99 </Directory>
99 </Directory>
100 </Directory>
100 </Directory>
101
101
102 <?if $(var.Platform) = "x86" ?>
102 <?if $(var.Platform) = "x86" ?>
103 <Merge Id='VCRuntime' DiskId='1' Language='1033'
103 <Merge Id='VCRuntime' DiskId='1' Language='1033'
104 SourceFile='$(var.VCRedistSrcDir)\microsoft.vcxx.crt.x86_msm.msm' />
104 SourceFile='$(var.VCRedistSrcDir)\microsoft.vcxx.crt.x86_msm.msm' />
105 <Merge Id='VCRuntimePolicy' DiskId='1' Language='1033'
105 <Merge Id='VCRuntimePolicy' DiskId='1' Language='1033'
106 SourceFile='$(var.VCRedistSrcDir)\policy.x.xx.microsoft.vcxx.crt.x86_msm.msm' />
106 SourceFile='$(var.VCRedistSrcDir)\policy.x.xx.microsoft.vcxx.crt.x86_msm.msm' />
107 <?else?>
107 <?else?>
108 <Merge Id='VCRuntime' DiskId='1' Language='1033'
108 <Merge Id='VCRuntime' DiskId='1' Language='1033'
109 SourceFile='$(var.VCRedistSrcDir)\microsoft.vcxx.crt.x64_msm.msm' />
109 SourceFile='$(var.VCRedistSrcDir)\microsoft.vcxx.crt.x64_msm.msm' />
110 <Merge Id='VCRuntimePolicy' DiskId='1' Language='1033'
110 <Merge Id='VCRuntimePolicy' DiskId='1' Language='1033'
111 SourceFile='$(var.VCRedistSrcDir)\policy.x.xx.microsoft.vcxx.crt.x64_msm.msm' />
111 SourceFile='$(var.VCRedistSrcDir)\policy.x.xx.microsoft.vcxx.crt.x64_msm.msm' />
112 <?endif?>
112 <?endif?>
113 </Directory>
113 </Directory>
114
114
115 <Feature Id='Complete' Title='Mercurial' Description='The complete package'
115 <Feature Id='Complete' Title='Mercurial' Description='The complete package'
116 Display='expand' Level='1' ConfigurableDirectory='INSTALLDIR' >
116 Display='expand' Level='1' ConfigurableDirectory='INSTALLDIR' >
117 <Feature Id='MainProgram' Title='Program' Description='Mercurial command line app'
117 <Feature Id='MainProgram' Title='Program' Description='Mercurial command line app'
118 Level='1' Absent='disallow' >
118 Level='1' Absent='disallow' >
119 <ComponentRef Id='MainExecutable' />
119 <ComponentRef Id='MainExecutable' />
120 <ComponentRef Id='distOutput' />
120 <ComponentRef Id='distOutput' />
121 <ComponentRef Id='libOutput' />
121 <ComponentRef Id='libOutput' />
122 <ComponentRef Id='ProgramMenuDir' />
122 <ComponentRef Id='ProgramMenuDir' />
123 <ComponentRef Id='ReadMe' />
123 <ComponentRef Id='ReadMe' />
124 <ComponentRef Id='COPYING' />
124 <ComponentRef Id='COPYING' />
125 <ComponentRef Id='mercurial.rc' />
125 <ComponentRef Id='mercurial.rc' />
126 <ComponentRef Id='mergetools.rc' />
126 <ComponentRef Id='mergetools.rc' />
127 <ComponentGroupRef Id='helpFolder' />
127 <ComponentGroupRef Id='helpFolder' />
128 <ComponentGroupRef Id='templatesFolder' />
128 <ComponentGroupRef Id='templatesFolder' />
129 <MergeRef Id='VCRuntime' />
129 <MergeRef Id='VCRuntime' />
130 <MergeRef Id='VCRuntimePolicy' />
130 <MergeRef Id='VCRuntimePolicy' />
131 </Feature>
131 </Feature>
132 <?if $(var.MercurialExtraFeatures)?>
133 <?foreach EXTRAFEAT in $(var.MercurialExtraFeatures)?>
134 <FeatureRef Id="$(var.EXTRAFEAT)" />
135 <?endforeach?>
136 <?endif?>
132 <Feature Id='Locales' Title='Translations' Description='Translations' Level='1'>
137 <Feature Id='Locales' Title='Translations' Description='Translations' Level='1'>
133 <ComponentGroupRef Id='localeFolder' />
138 <ComponentGroupRef Id='localeFolder' />
134 <ComponentRef Id='i18nFolder' />
139 <ComponentRef Id='i18nFolder' />
135 </Feature>
140 </Feature>
136 <Feature Id='Documentation' Title='Documentation' Description='HTML man pages' Level='1'>
141 <Feature Id='Documentation' Title='Documentation' Description='HTML man pages' Level='1'>
137 <ComponentGroupRef Id='docFolder' />
142 <ComponentGroupRef Id='docFolder' />
138 </Feature>
143 </Feature>
139 <Feature Id='Misc' Title='Miscellaneous' Description='Contributed scripts' Level='1'>
144 <Feature Id='Misc' Title='Miscellaneous' Description='Contributed scripts' Level='1'>
140 <ComponentGroupRef Id='contribFolder' />
145 <ComponentGroupRef Id='contribFolder' />
141 </Feature>
146 </Feature>
142 </Feature>
147 </Feature>
143
148
144 <UIRef Id="WixUI_FeatureTree" />
149 <UIRef Id="WixUI_FeatureTree" />
145 <UIRef Id="WixUI_ErrorProgressText" />
150 <UIRef Id="WixUI_ErrorProgressText" />
146
151
147 <WixVariable Id="WixUILicenseRtf" Value="contrib\packaging\wix\COPYING.rtf" />
152 <WixVariable Id="WixUILicenseRtf" Value="contrib\packaging\wix\COPYING.rtf" />
148
153
149 <Icon Id="hgIcon.ico" SourceFile="contrib/win32/mercurial.ico" />
154 <Icon Id="hgIcon.ico" SourceFile="contrib/win32/mercurial.ico" />
150
155
151 <Upgrade Id='$(var.ProductUpgradeCode)'>
156 <Upgrade Id='$(var.ProductUpgradeCode)'>
152 <UpgradeVersion
157 <UpgradeVersion
153 IncludeMinimum='yes' Minimum='0.0.0' IncludeMaximum='no' OnlyDetect='no'
158 IncludeMinimum='yes' Minimum='0.0.0' IncludeMaximum='no' OnlyDetect='no'
154 Property='INSTALLEDMERCURIALPRODUCTS' />
159 Property='INSTALLEDMERCURIALPRODUCTS' />
155 </Upgrade>
160 </Upgrade>
156
161
157 <InstallExecuteSequence>
162 <InstallExecuteSequence>
158 <RemoveExistingProducts After='InstallInitialize'/>
163 <RemoveExistingProducts After='InstallInitialize'/>
159 </InstallExecuteSequence>
164 </InstallExecuteSequence>
160
165
161 </Product>
166 </Product>
162 </Wix>
167 </Wix>
General Comments 0
You need to be logged in to leave comments. Login now