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