comparison contrib/packaging/hgpackaging/wix.py @ 41957:131d0b7c3940

wix: autogenerate wxs file for library files Currently, dist.wxs contains an enumeration of .pyd and .dll files staged to dist/lib by py2exe. Having a manual list of files is error prone, as things can easily get out of sync (as the previous commit demonstrates). This is especially an issue for TortoiseHG, which ships a number of custom modules, which may pull in additional dependencies. Let's prevent this problem from manifesting by dynamically generating a .wxs file containing the .pyd and .dll files staged by py2exe. Differential Revision: https://phab.mercurial-scm.org/D6139
author Gregory Szorc <gregory.szorc@gmail.com>
date Thu, 14 Mar 2019 18:11:22 -0700
parents 39f65c506899
children 715d3220ac4f
comparison
equal deleted inserted replaced
41956:39f65c506899 41957:131d0b7c3940
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
15 import xml.dom.minidom
14 16
15 from .downloads import ( 17 from .downloads import (
16 download_entry, 18 download_entry,
17 ) 19 )
18 from .py2exe import ( 20 from .py2exe import (
124 subject_name=subject_name, cert_path=cert_path, 126 subject_name=subject_name, cert_path=cert_path,
125 cert_password=cert_password, 127 cert_password=cert_password,
126 timestamp_url=timestamp_url) 128 timestamp_url=timestamp_url)
127 129
128 return post_build_sign 130 return post_build_sign
131
132
133 LIBRARIES_XML = '''
134 <?xml version="1.0" encoding="utf-8"?>
135 <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
136
137 <?include {wix_dir}/guids.wxi ?>
138 <?include {wix_dir}/defines.wxi ?>
139
140 <Fragment>
141 <DirectoryRef Id="INSTALLDIR" FileSource="$(var.SourceDir)">
142 <Directory Id="libdir" Name="lib" FileSource="$(var.SourceDir)/lib">
143 <Component Id="libOutput" Guid="$(var.lib.guid)" Win64='$(var.IsX64)'>
144 </Component>
145 </Directory>
146 </DirectoryRef>
147 </Fragment>
148 </Wix>
149 '''.lstrip()
150
151
152 def make_libraries_xml(wix_dir: pathlib.Path, dist_dir: pathlib.Path):
153 """Make XML data for library components WXS."""
154 # We can't use ElementTree because it doesn't handle the
155 # <?include ?> directives.
156 doc = xml.dom.minidom.parseString(
157 LIBRARIES_XML.format(wix_dir=str(wix_dir)))
158
159 component = doc.getElementsByTagName('Component')[0]
160
161 f = doc.createElement('File')
162 f.setAttribute('Name', 'library.zip')
163 f.setAttribute('KeyPath', 'yes')
164 component.appendChild(f)
165
166 lib_dir = dist_dir / 'lib'
167
168 for p in sorted(lib_dir.iterdir()):
169 if not p.name.endswith(('.dll', '.pyd')):
170 continue
171
172 f = doc.createElement('File')
173 f.setAttribute('Name', p.name)
174 component.appendChild(f)
175
176 return doc.toprettyxml()
129 177
130 178
131 def build_installer(source_dir: pathlib.Path, python_exe: pathlib.Path, 179 def build_installer(source_dir: pathlib.Path, python_exe: pathlib.Path,
132 msi_name='mercurial', version=None, post_build_fn=None): 180 msi_name='mercurial', version=None, post_build_fn=None):
133 """Build a WiX MSI installer. 181 """Build a WiX MSI installer.
179 for wxs, rel_path in SUPPORT_WXS: 227 for wxs, rel_path in SUPPORT_WXS:
180 wxs = wix_dir / wxs 228 wxs = wix_dir / wxs
181 wxs_source_dir = source_dir / rel_path 229 wxs_source_dir = source_dir / rel_path
182 run_candle(wix_path, build_dir, wxs, wxs_source_dir, defines=defines) 230 run_candle(wix_path, build_dir, wxs, wxs_source_dir, defines=defines)
183 231
232 # candle.exe doesn't like when we have an open handle on the file.
233 # So use TemporaryDirectory() instead of NamedTemporaryFile().
234 with tempfile.TemporaryDirectory() as td:
235 td = pathlib.Path(td)
236
237 tf = td / 'library.wxs'
238 with tf.open('w') as fh:
239 fh.write(make_libraries_xml(wix_dir, dist_dir))
240
241 run_candle(wix_path, build_dir, tf, dist_dir, defines=defines)
242
184 source = wix_dir / 'mercurial.wxs' 243 source = wix_dir / 'mercurial.wxs'
185 defines['Version'] = version 244 defines['Version'] = version
186 defines['Comments'] = 'Installs Mercurial version %s' % version 245 defines['Comments'] = 'Installs Mercurial version %s' % version
187 defines['VCRedistSrcDir'] = str(hg_build_dir) 246 defines['VCRedistSrcDir'] = str(hg_build_dir)
188 247
202 261
203 for source, rel_path in SUPPORT_WXS: 262 for source, rel_path in SUPPORT_WXS:
204 assert source.endswith('.wxs') 263 assert source.endswith('.wxs')
205 args.append(str(build_dir / ('%s.wixobj' % source[:-4]))) 264 args.append(str(build_dir / ('%s.wixobj' % source[:-4])))
206 265
207 args.append(str(build_dir / 'mercurial.wixobj')) 266 args.extend([
267 str(build_dir / 'library.wixobj'),
268 str(build_dir / 'mercurial.wixobj'),
269 ])
208 270
209 subprocess.run(args, cwd=str(source_dir), check=True) 271 subprocess.run(args, cwd=str(source_dir), check=True)
210 272
211 print('%s created' % msi_path) 273 print('%s created' % msi_path)
212 274