Mercurial > hg
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 |