##// END OF EJS Templates
packaging: rename hgrc.d to defaultrc for Windows config files next to the exe...
Matt Harbison -
r44614:e4344e46 5.3rc1 stable
parent child Browse files
Show More
@@ -1,245 +1,245 b''
1 1 # py2exe.py - Functionality for performing py2exe builds.
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 subprocess
13 13
14 14 from .downloads import download_entry
15 15 from .util import (
16 16 extract_tar_to_directory,
17 17 extract_zip_to_directory,
18 18 process_install_rules,
19 19 python_exe_info,
20 20 )
21 21
22 22
23 23 STAGING_RULES = [
24 24 ('contrib/bash_completion', 'Contrib/'),
25 25 ('contrib/hgk', 'Contrib/hgk.tcl'),
26 26 ('contrib/hgweb.fcgi', 'Contrib/'),
27 27 ('contrib/hgweb.wsgi', 'Contrib/'),
28 28 ('contrib/logo-droplets.svg', 'Contrib/'),
29 29 ('contrib/mercurial.el', 'Contrib/'),
30 30 ('contrib/mq.el', 'Contrib/'),
31 31 ('contrib/tcsh_completion', 'Contrib/'),
32 32 ('contrib/tcsh_completion_build.sh', 'Contrib/'),
33 33 ('contrib/vim/*', 'Contrib/Vim/'),
34 34 ('contrib/win32/postinstall.txt', 'ReleaseNotes.txt'),
35 35 ('contrib/win32/ReadMe.html', 'ReadMe.html'),
36 36 ('contrib/xml.rnc', 'Contrib/'),
37 37 ('contrib/zsh_completion', 'Contrib/'),
38 38 ('dist/hg.exe', './'),
39 39 ('dist/lib/*.dll', 'lib/'),
40 40 ('dist/lib/*.pyd', 'lib/'),
41 41 ('dist/lib/library.zip', 'lib/'),
42 42 ('dist/Microsoft.VC*.CRT.manifest', './'),
43 43 ('dist/msvc*.dll', './'),
44 44 ('dist/python*.dll', './'),
45 45 ('doc/*.html', 'doc/'),
46 46 ('doc/style.css', 'doc/'),
47 47 ('mercurial/helptext/**/*.txt', 'helptext/'),
48 ('mercurial/defaultrc/*.rc', 'hgrc.d/'),
48 ('mercurial/defaultrc/*.rc', 'defaultrc/'),
49 49 ('mercurial/locale/**/*', 'locale/'),
50 50 ('mercurial/templates/**/*', 'Templates/'),
51 51 ('COPYING', 'Copying.txt'),
52 52 ]
53 53
54 54 # List of paths to exclude from the staging area.
55 55 STAGING_EXCLUDES = [
56 56 'doc/hg-ssh.8.html',
57 57 ]
58 58
59 59
60 60 def build_py2exe(
61 61 source_dir: pathlib.Path,
62 62 build_dir: pathlib.Path,
63 63 python_exe: pathlib.Path,
64 64 build_name: str,
65 65 venv_requirements_txt: pathlib.Path,
66 66 extra_packages=None,
67 67 extra_excludes=None,
68 68 extra_dll_excludes=None,
69 69 extra_packages_script=None,
70 70 ):
71 71 """Build Mercurial with py2exe.
72 72
73 73 Build files will be placed in ``build_dir``.
74 74
75 75 py2exe's setup.py doesn't use setuptools. It doesn't have modern logic
76 76 for finding the Python 2.7 toolchain. So, we require the environment
77 77 to already be configured with an active toolchain.
78 78 """
79 79 if 'VCINSTALLDIR' not in os.environ:
80 80 raise Exception(
81 81 'not running from a Visual C++ build environment; '
82 82 'execute the "Visual C++ <version> Command Prompt" '
83 83 'application shortcut or a vcsvarsall.bat file'
84 84 )
85 85
86 86 # Identity x86/x64 and validate the environment matches the Python
87 87 # architecture.
88 88 vc_x64 = r'\x64' in os.environ['LIB']
89 89
90 90 py_info = python_exe_info(python_exe)
91 91
92 92 if vc_x64:
93 93 if py_info['arch'] != '64bit':
94 94 raise Exception(
95 95 'architecture mismatch: Visual C++ environment '
96 96 'is configured for 64-bit but Python is 32-bit'
97 97 )
98 98 else:
99 99 if py_info['arch'] != '32bit':
100 100 raise Exception(
101 101 'architecture mismatch: Visual C++ environment '
102 102 'is configured for 32-bit but Python is 64-bit'
103 103 )
104 104
105 105 if py_info['py3']:
106 106 raise Exception('Only Python 2 is currently supported')
107 107
108 108 build_dir.mkdir(exist_ok=True)
109 109
110 110 gettext_pkg, gettext_entry = download_entry('gettext', build_dir)
111 111 gettext_dep_pkg = download_entry('gettext-dep', build_dir)[0]
112 112 virtualenv_pkg, virtualenv_entry = download_entry('virtualenv', build_dir)
113 113 py2exe_pkg, py2exe_entry = download_entry('py2exe', build_dir)
114 114
115 115 venv_path = build_dir / (
116 116 'venv-%s-%s' % (build_name, 'x64' if vc_x64 else 'x86')
117 117 )
118 118
119 119 gettext_root = build_dir / ('gettext-win-%s' % gettext_entry['version'])
120 120
121 121 if not gettext_root.exists():
122 122 extract_zip_to_directory(gettext_pkg, gettext_root)
123 123 extract_zip_to_directory(gettext_dep_pkg, gettext_root)
124 124
125 125 # This assumes Python 2. We don't need virtualenv on Python 3.
126 126 virtualenv_src_path = build_dir / (
127 127 'virtualenv-%s' % virtualenv_entry['version']
128 128 )
129 129 virtualenv_py = virtualenv_src_path / 'virtualenv.py'
130 130
131 131 if not virtualenv_src_path.exists():
132 132 extract_tar_to_directory(virtualenv_pkg, build_dir)
133 133
134 134 py2exe_source_path = build_dir / ('py2exe-%s' % py2exe_entry['version'])
135 135
136 136 if not py2exe_source_path.exists():
137 137 extract_zip_to_directory(py2exe_pkg, build_dir)
138 138
139 139 if not venv_path.exists():
140 140 print('creating virtualenv with dependencies')
141 141 subprocess.run(
142 142 [str(python_exe), str(virtualenv_py), str(venv_path)], check=True
143 143 )
144 144
145 145 venv_python = venv_path / 'Scripts' / 'python.exe'
146 146 venv_pip = venv_path / 'Scripts' / 'pip.exe'
147 147
148 148 subprocess.run(
149 149 [str(venv_pip), 'install', '-r', str(venv_requirements_txt)], check=True
150 150 )
151 151
152 152 # Force distutils to use VC++ settings from environment, which was
153 153 # validated above.
154 154 env = dict(os.environ)
155 155 env['DISTUTILS_USE_SDK'] = '1'
156 156 env['MSSdk'] = '1'
157 157
158 158 if extra_packages_script:
159 159 more_packages = set(
160 160 subprocess.check_output(extra_packages_script, cwd=build_dir)
161 161 .split(b'\0')[-1]
162 162 .strip()
163 163 .decode('utf-8')
164 164 .splitlines()
165 165 )
166 166 if more_packages:
167 167 if not extra_packages:
168 168 extra_packages = more_packages
169 169 else:
170 170 extra_packages |= more_packages
171 171
172 172 if extra_packages:
173 173 env['HG_PY2EXE_EXTRA_PACKAGES'] = ' '.join(sorted(extra_packages))
174 174 hgext3rd_extras = sorted(
175 175 e for e in extra_packages if e.startswith('hgext3rd.')
176 176 )
177 177 if hgext3rd_extras:
178 178 env['HG_PY2EXE_EXTRA_INSTALL_PACKAGES'] = ' '.join(hgext3rd_extras)
179 179 if extra_excludes:
180 180 env['HG_PY2EXE_EXTRA_EXCLUDES'] = ' '.join(sorted(extra_excludes))
181 181 if extra_dll_excludes:
182 182 env['HG_PY2EXE_EXTRA_DLL_EXCLUDES'] = ' '.join(
183 183 sorted(extra_dll_excludes)
184 184 )
185 185
186 186 py2exe_py_path = venv_path / 'Lib' / 'site-packages' / 'py2exe'
187 187 if not py2exe_py_path.exists():
188 188 print('building py2exe')
189 189 subprocess.run(
190 190 [str(venv_python), 'setup.py', 'install'],
191 191 cwd=py2exe_source_path,
192 192 env=env,
193 193 check=True,
194 194 )
195 195
196 196 # Register location of msgfmt and other binaries.
197 197 env['PATH'] = '%s%s%s' % (
198 198 env['PATH'],
199 199 os.pathsep,
200 200 str(gettext_root / 'bin'),
201 201 )
202 202
203 203 print('building Mercurial')
204 204 subprocess.run(
205 205 [str(venv_python), 'setup.py', 'py2exe', 'build_doc', '--html'],
206 206 cwd=str(source_dir),
207 207 env=env,
208 208 check=True,
209 209 )
210 210
211 211
212 212 def stage_install(
213 213 source_dir: pathlib.Path, staging_dir: pathlib.Path, lower_case=False
214 214 ):
215 215 """Copy all files to be installed to a directory.
216 216
217 217 This allows packaging to simply walk a directory tree to find source
218 218 files.
219 219 """
220 220 if lower_case:
221 221 rules = []
222 222 for source, dest in STAGING_RULES:
223 223 # Only lower directory names.
224 224 if '/' in dest:
225 225 parent, leaf = dest.rsplit('/', 1)
226 226 dest = '%s/%s' % (parent.lower(), leaf)
227 227 rules.append((source, dest))
228 228 else:
229 229 rules = STAGING_RULES
230 230
231 231 process_install_rules(rules, source_dir, staging_dir)
232 232
233 233 # Write out a default editor.rc file to configure notepad as the
234 234 # default editor.
235 with (staging_dir / 'hgrc.d' / 'editor.rc').open(
235 with (staging_dir / 'defaultrc' / 'editor.rc').open(
236 236 'w', encoding='utf-8'
237 237 ) as fh:
238 238 fh.write('[ui]\neditor = notepad\n')
239 239
240 240 # Purge any files we don't want to be there.
241 241 for f in STAGING_EXCLUDES:
242 242 p = staging_dir / f
243 243 if p.exists():
244 244 print('removing %s' % p)
245 245 p.unlink()
@@ -1,518 +1,518 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 collections
11 11 import os
12 12 import pathlib
13 13 import re
14 14 import shutil
15 15 import subprocess
16 16 import typing
17 17 import uuid
18 18 import xml.dom.minidom
19 19
20 20 from .downloads import download_entry
21 21 from .py2exe import (
22 22 build_py2exe,
23 23 stage_install,
24 24 )
25 25 from .util import (
26 26 extract_zip_to_directory,
27 27 process_install_rules,
28 28 sign_with_signtool,
29 29 )
30 30
31 31
32 32 EXTRA_PACKAGES = {
33 33 'distutils',
34 34 'pygments',
35 35 }
36 36
37 37
38 38 EXTRA_INSTALL_RULES = [
39 39 ('contrib/packaging/wix/COPYING.rtf', 'COPYING.rtf'),
40 ('contrib/win32/mercurial.ini', 'hgrc.d/mercurial.rc'),
40 ('contrib/win32/mercurial.ini', 'defaultrc/mercurial.rc'),
41 41 ]
42 42
43 43 STAGING_REMOVE_FILES = [
44 44 # We use the RTF variant.
45 45 'copying.txt',
46 46 ]
47 47
48 48 SHORTCUTS = {
49 49 # hg.1.html'
50 50 'hg.file.5d3e441c_28d9_5542_afd0_cdd4234f12d5': {
51 51 'Name': 'Mercurial Command Reference',
52 52 },
53 53 # hgignore.5.html
54 54 'hg.file.5757d8e0_f207_5e10_a2ec_3ba0a062f431': {
55 55 'Name': 'Mercurial Ignore Files',
56 56 },
57 57 # hgrc.5.html
58 58 'hg.file.92e605fd_1d1a_5dc6_9fc0_5d2998eb8f5e': {
59 59 'Name': 'Mercurial Configuration Files',
60 60 },
61 61 }
62 62
63 63
64 64 def find_version(source_dir: pathlib.Path):
65 65 version_py = source_dir / 'mercurial' / '__version__.py'
66 66
67 67 with version_py.open('r', encoding='utf-8') as fh:
68 68 source = fh.read().strip()
69 69
70 70 m = re.search('version = b"(.*)"', source)
71 71 return m.group(1)
72 72
73 73
74 74 def normalize_version(version):
75 75 """Normalize Mercurial version string so WiX accepts it.
76 76
77 77 Version strings have to be numeric X.Y.Z.
78 78 """
79 79
80 80 if '+' in version:
81 81 version, extra = version.split('+', 1)
82 82 else:
83 83 extra = None
84 84
85 85 # 4.9rc0
86 86 if version[:-1].endswith('rc'):
87 87 version = version[:-3]
88 88
89 89 versions = [int(v) for v in version.split('.')]
90 90 while len(versions) < 3:
91 91 versions.append(0)
92 92
93 93 major, minor, build = versions[:3]
94 94
95 95 if extra:
96 96 # <commit count>-<hash>+<date>
97 97 build = int(extra.split('-')[0])
98 98
99 99 return '.'.join('%d' % x for x in (major, minor, build))
100 100
101 101
102 102 def ensure_vc90_merge_modules(build_dir):
103 103 x86 = (
104 104 download_entry(
105 105 'vc9-crt-x86-msm',
106 106 build_dir,
107 107 local_name='microsoft.vcxx.crt.x86_msm.msm',
108 108 )[0],
109 109 download_entry(
110 110 'vc9-crt-x86-msm-policy',
111 111 build_dir,
112 112 local_name='policy.x.xx.microsoft.vcxx.crt.x86_msm.msm',
113 113 )[0],
114 114 )
115 115
116 116 x64 = (
117 117 download_entry(
118 118 'vc9-crt-x64-msm',
119 119 build_dir,
120 120 local_name='microsoft.vcxx.crt.x64_msm.msm',
121 121 )[0],
122 122 download_entry(
123 123 'vc9-crt-x64-msm-policy',
124 124 build_dir,
125 125 local_name='policy.x.xx.microsoft.vcxx.crt.x64_msm.msm',
126 126 )[0],
127 127 )
128 128 return {
129 129 'x86': x86,
130 130 'x64': x64,
131 131 }
132 132
133 133
134 134 def run_candle(wix, cwd, wxs, source_dir, defines=None):
135 135 args = [
136 136 str(wix / 'candle.exe'),
137 137 '-nologo',
138 138 str(wxs),
139 139 '-dSourceDir=%s' % source_dir,
140 140 ]
141 141
142 142 if defines:
143 143 args.extend('-d%s=%s' % define for define in sorted(defines.items()))
144 144
145 145 subprocess.run(args, cwd=str(cwd), check=True)
146 146
147 147
148 148 def make_post_build_signing_fn(
149 149 name,
150 150 subject_name=None,
151 151 cert_path=None,
152 152 cert_password=None,
153 153 timestamp_url=None,
154 154 ):
155 155 """Create a callable that will use signtool to sign hg.exe."""
156 156
157 157 def post_build_sign(source_dir, build_dir, dist_dir, version):
158 158 description = '%s %s' % (name, version)
159 159
160 160 sign_with_signtool(
161 161 dist_dir / 'hg.exe',
162 162 description,
163 163 subject_name=subject_name,
164 164 cert_path=cert_path,
165 165 cert_password=cert_password,
166 166 timestamp_url=timestamp_url,
167 167 )
168 168
169 169 return post_build_sign
170 170
171 171
172 172 def make_files_xml(staging_dir: pathlib.Path, is_x64) -> str:
173 173 """Create XML string listing every file to be installed."""
174 174
175 175 # We derive GUIDs from a deterministic file path identifier.
176 176 # We shoehorn the name into something that looks like a URL because
177 177 # the UUID namespaces are supposed to work that way (even though
178 178 # the input data probably is never validated).
179 179
180 180 doc = xml.dom.minidom.parseString(
181 181 '<?xml version="1.0" encoding="utf-8"?>'
182 182 '<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">'
183 183 '</Wix>'
184 184 )
185 185
186 186 # Assemble the install layout by directory. This makes it easier to
187 187 # emit XML, since each directory has separate entities.
188 188 manifest = collections.defaultdict(dict)
189 189
190 190 for root, dirs, files in os.walk(staging_dir):
191 191 dirs.sort()
192 192
193 193 root = pathlib.Path(root)
194 194 rel_dir = root.relative_to(staging_dir)
195 195
196 196 for i in range(len(rel_dir.parts)):
197 197 parent = '/'.join(rel_dir.parts[0 : i + 1])
198 198 manifest.setdefault(parent, {})
199 199
200 200 for f in sorted(files):
201 201 full = root / f
202 202 manifest[str(rel_dir).replace('\\', '/')][full.name] = full
203 203
204 204 component_groups = collections.defaultdict(list)
205 205
206 206 # Now emit a <Fragment> for each directory.
207 207 # Each directory is composed of a <DirectoryRef> pointing to its parent
208 208 # and defines child <Directory>'s and a <Component> with all the files.
209 209 for dir_name, entries in sorted(manifest.items()):
210 210 # The directory id is derived from the path. But the root directory
211 211 # is special.
212 212 if dir_name == '.':
213 213 parent_directory_id = 'INSTALLDIR'
214 214 else:
215 215 parent_directory_id = 'hg.dir.%s' % dir_name.replace('/', '.')
216 216
217 217 fragment = doc.createElement('Fragment')
218 218 directory_ref = doc.createElement('DirectoryRef')
219 219 directory_ref.setAttribute('Id', parent_directory_id)
220 220
221 221 # Add <Directory> entries for immediate children directories.
222 222 for possible_child in sorted(manifest.keys()):
223 223 if (
224 224 dir_name == '.'
225 225 and '/' not in possible_child
226 226 and possible_child != '.'
227 227 ):
228 228 child_directory_id = 'hg.dir.%s' % possible_child
229 229 name = possible_child
230 230 else:
231 231 if not possible_child.startswith('%s/' % dir_name):
232 232 continue
233 233 name = possible_child[len(dir_name) + 1 :]
234 234 if '/' in name:
235 235 continue
236 236
237 237 child_directory_id = 'hg.dir.%s' % possible_child.replace(
238 238 '/', '.'
239 239 )
240 240
241 241 directory = doc.createElement('Directory')
242 242 directory.setAttribute('Id', child_directory_id)
243 243 directory.setAttribute('Name', name)
244 244 directory_ref.appendChild(directory)
245 245
246 246 # Add <Component>s for files in this directory.
247 247 for rel, source_path in sorted(entries.items()):
248 248 if dir_name == '.':
249 249 full_rel = rel
250 250 else:
251 251 full_rel = '%s/%s' % (dir_name, rel)
252 252
253 253 component_unique_id = (
254 254 'https://www.mercurial-scm.org/wix-installer/0/component/%s'
255 255 % full_rel
256 256 )
257 257 component_guid = uuid.uuid5(uuid.NAMESPACE_URL, component_unique_id)
258 258 component_id = 'hg.component.%s' % str(component_guid).replace(
259 259 '-', '_'
260 260 )
261 261
262 262 component = doc.createElement('Component')
263 263
264 264 component.setAttribute('Id', component_id)
265 265 component.setAttribute('Guid', str(component_guid).upper())
266 266 component.setAttribute('Win64', 'yes' if is_x64 else 'no')
267 267
268 268 # Assign this component to a top-level group.
269 269 if dir_name == '.':
270 270 component_groups['ROOT'].append(component_id)
271 271 elif '/' in dir_name:
272 272 component_groups[dir_name[0 : dir_name.index('/')]].append(
273 273 component_id
274 274 )
275 275 else:
276 276 component_groups[dir_name].append(component_id)
277 277
278 278 unique_id = (
279 279 'https://www.mercurial-scm.org/wix-installer/0/%s' % full_rel
280 280 )
281 281 file_guid = uuid.uuid5(uuid.NAMESPACE_URL, unique_id)
282 282
283 283 # IDs have length limits. So use GUID to derive them.
284 284 file_guid_normalized = str(file_guid).replace('-', '_')
285 285 file_id = 'hg.file.%s' % file_guid_normalized
286 286
287 287 file_element = doc.createElement('File')
288 288 file_element.setAttribute('Id', file_id)
289 289 file_element.setAttribute('Source', str(source_path))
290 290 file_element.setAttribute('KeyPath', 'yes')
291 291 file_element.setAttribute('ReadOnly', 'yes')
292 292
293 293 component.appendChild(file_element)
294 294 directory_ref.appendChild(component)
295 295
296 296 fragment.appendChild(directory_ref)
297 297 doc.documentElement.appendChild(fragment)
298 298
299 299 for group, component_ids in sorted(component_groups.items()):
300 300 fragment = doc.createElement('Fragment')
301 301 component_group = doc.createElement('ComponentGroup')
302 302 component_group.setAttribute('Id', 'hg.group.%s' % group)
303 303
304 304 for component_id in component_ids:
305 305 component_ref = doc.createElement('ComponentRef')
306 306 component_ref.setAttribute('Id', component_id)
307 307 component_group.appendChild(component_ref)
308 308
309 309 fragment.appendChild(component_group)
310 310 doc.documentElement.appendChild(fragment)
311 311
312 312 # Add <Shortcut> to files that have it defined.
313 313 for file_id, metadata in sorted(SHORTCUTS.items()):
314 314 els = doc.getElementsByTagName('File')
315 315 els = [el for el in els if el.getAttribute('Id') == file_id]
316 316
317 317 if not els:
318 318 raise Exception('could not find File[Id=%s]' % file_id)
319 319
320 320 for el in els:
321 321 shortcut = doc.createElement('Shortcut')
322 322 shortcut.setAttribute('Id', 'hg.shortcut.%s' % file_id)
323 323 shortcut.setAttribute('Directory', 'ProgramMenuDir')
324 324 shortcut.setAttribute('Icon', 'hgIcon.ico')
325 325 shortcut.setAttribute('IconIndex', '0')
326 326 shortcut.setAttribute('Advertise', 'yes')
327 327 for k, v in sorted(metadata.items()):
328 328 shortcut.setAttribute(k, v)
329 329
330 330 el.appendChild(shortcut)
331 331
332 332 return doc.toprettyxml()
333 333
334 334
335 335 def build_installer(
336 336 source_dir: pathlib.Path,
337 337 python_exe: pathlib.Path,
338 338 msi_name='mercurial',
339 339 version=None,
340 340 post_build_fn=None,
341 341 extra_packages_script=None,
342 342 extra_wxs: typing.Optional[typing.Dict[str, str]] = None,
343 343 extra_features: typing.Optional[typing.List[str]] = None,
344 344 ):
345 345 """Build a WiX MSI installer.
346 346
347 347 ``source_dir`` is the path to the Mercurial source tree to use.
348 348 ``arch`` is the target architecture. either ``x86`` or ``x64``.
349 349 ``python_exe`` is the path to the Python executable to use/bundle.
350 350 ``version`` is the Mercurial version string. If not defined,
351 351 ``mercurial/__version__.py`` will be consulted.
352 352 ``post_build_fn`` is a callable that will be called after building
353 353 Mercurial but before invoking WiX. It can be used to e.g. facilitate
354 354 signing. It is passed the paths to the Mercurial source, build, and
355 355 dist directories and the resolved Mercurial version.
356 356 ``extra_packages_script`` is a command to be run to inject extra packages
357 357 into the py2exe binary. It should stage packages into the virtualenv and
358 358 print a null byte followed by a newline-separated list of packages that
359 359 should be included in the exe.
360 360 ``extra_wxs`` is a dict of {wxs_name: working_dir_for_wxs_build}.
361 361 ``extra_features`` is a list of additional named Features to include in
362 362 the build. These must match Feature names in one of the wxs scripts.
363 363 """
364 364 arch = 'x64' if r'\x64' in os.environ.get('LIB', '') else 'x86'
365 365
366 366 hg_build_dir = source_dir / 'build'
367 367 dist_dir = source_dir / 'dist'
368 368 wix_dir = source_dir / 'contrib' / 'packaging' / 'wix'
369 369
370 370 requirements_txt = wix_dir / 'requirements.txt'
371 371
372 372 build_py2exe(
373 373 source_dir,
374 374 hg_build_dir,
375 375 python_exe,
376 376 'wix',
377 377 requirements_txt,
378 378 extra_packages=EXTRA_PACKAGES,
379 379 extra_packages_script=extra_packages_script,
380 380 )
381 381
382 382 version = version or normalize_version(find_version(source_dir))
383 383 print('using version string: %s' % version)
384 384
385 385 if post_build_fn:
386 386 post_build_fn(source_dir, hg_build_dir, dist_dir, version)
387 387
388 388 build_dir = hg_build_dir / ('wix-%s' % arch)
389 389 staging_dir = build_dir / 'stage'
390 390
391 391 build_dir.mkdir(exist_ok=True)
392 392
393 393 # Purge the staging directory for every build so packaging is pristine.
394 394 if staging_dir.exists():
395 395 print('purging %s' % staging_dir)
396 396 shutil.rmtree(staging_dir)
397 397
398 398 stage_install(source_dir, staging_dir, lower_case=True)
399 399
400 400 # We also install some extra files.
401 401 process_install_rules(EXTRA_INSTALL_RULES, source_dir, staging_dir)
402 402
403 403 # And remove some files we don't want.
404 404 for f in STAGING_REMOVE_FILES:
405 405 p = staging_dir / f
406 406 if p.exists():
407 407 print('removing %s' % p)
408 408 p.unlink()
409 409
410 410 wix_pkg, wix_entry = download_entry('wix', hg_build_dir)
411 411 wix_path = hg_build_dir / ('wix-%s' % wix_entry['version'])
412 412
413 413 if not wix_path.exists():
414 414 extract_zip_to_directory(wix_pkg, wix_path)
415 415
416 416 ensure_vc90_merge_modules(hg_build_dir)
417 417
418 418 source_build_rel = pathlib.Path(os.path.relpath(source_dir, build_dir))
419 419
420 420 defines = {'Platform': arch}
421 421
422 422 # Derive a .wxs file with the staged files.
423 423 manifest_wxs = build_dir / 'stage.wxs'
424 424 with manifest_wxs.open('w', encoding='utf-8') as fh:
425 425 fh.write(make_files_xml(staging_dir, is_x64=arch == 'x64'))
426 426
427 427 run_candle(wix_path, build_dir, manifest_wxs, staging_dir, defines=defines)
428 428
429 429 for source, rel_path in sorted((extra_wxs or {}).items()):
430 430 run_candle(wix_path, build_dir, source, rel_path, defines=defines)
431 431
432 432 source = wix_dir / 'mercurial.wxs'
433 433 defines['Version'] = version
434 434 defines['Comments'] = 'Installs Mercurial version %s' % version
435 435 defines['VCRedistSrcDir'] = str(hg_build_dir)
436 436 if extra_features:
437 437 assert all(';' not in f for f in extra_features)
438 438 defines['MercurialExtraFeatures'] = ';'.join(extra_features)
439 439
440 440 run_candle(wix_path, build_dir, source, source_build_rel, defines=defines)
441 441
442 442 msi_path = (
443 443 source_dir / 'dist' / ('%s-%s-%s.msi' % (msi_name, version, arch))
444 444 )
445 445
446 446 args = [
447 447 str(wix_path / 'light.exe'),
448 448 '-nologo',
449 449 '-ext',
450 450 'WixUIExtension',
451 451 '-sw1076',
452 452 '-spdb',
453 453 '-o',
454 454 str(msi_path),
455 455 ]
456 456
457 457 for source, rel_path in sorted((extra_wxs or {}).items()):
458 458 assert source.endswith('.wxs')
459 459 source = os.path.basename(source)
460 460 args.append(str(build_dir / ('%s.wixobj' % source[:-4])))
461 461
462 462 args.extend(
463 463 [str(build_dir / 'stage.wixobj'), str(build_dir / 'mercurial.wixobj'),]
464 464 )
465 465
466 466 subprocess.run(args, cwd=str(source_dir), check=True)
467 467
468 468 print('%s created' % msi_path)
469 469
470 470 return {
471 471 'msi_path': msi_path,
472 472 }
473 473
474 474
475 475 def build_signed_installer(
476 476 source_dir: pathlib.Path,
477 477 python_exe: pathlib.Path,
478 478 name: str,
479 479 version=None,
480 480 subject_name=None,
481 481 cert_path=None,
482 482 cert_password=None,
483 483 timestamp_url=None,
484 484 extra_packages_script=None,
485 485 extra_wxs=None,
486 486 extra_features=None,
487 487 ):
488 488 """Build an installer with signed executables."""
489 489
490 490 post_build_fn = make_post_build_signing_fn(
491 491 name,
492 492 subject_name=subject_name,
493 493 cert_path=cert_path,
494 494 cert_password=cert_password,
495 495 timestamp_url=timestamp_url,
496 496 )
497 497
498 498 info = build_installer(
499 499 source_dir,
500 500 python_exe=python_exe,
501 501 msi_name=name.lower(),
502 502 version=version,
503 503 post_build_fn=post_build_fn,
504 504 extra_packages_script=extra_packages_script,
505 505 extra_wxs=extra_wxs,
506 506 extra_features=extra_features,
507 507 )
508 508
509 509 description = '%s %s' % (name, version)
510 510
511 511 sign_with_signtool(
512 512 info['msi_path'],
513 513 description,
514 514 subject_name=subject_name,
515 515 cert_path=cert_path,
516 516 cert_password=cert_password,
517 517 timestamp_url=timestamp_url,
518 518 )
@@ -1,82 +1,82 b''
1 1 ; Script generated by the Inno Setup Script Wizard.
2 2 ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
3 3
4 4 #ifndef ARCH
5 5 #define ARCH = "x86"
6 6 #endif
7 7
8 8 [Setup]
9 9 AppCopyright=Copyright 2005-2019 Matt Mackall and others
10 10 AppName=Mercurial
11 11 AppVersion={#VERSION}
12 12 #if ARCH == "x64"
13 13 AppVerName=Mercurial {#VERSION} (64-bit)
14 14 OutputBaseFilename=Mercurial-{#VERSION}-x64
15 15 ArchitecturesAllowed=x64
16 16 ArchitecturesInstallIn64BitMode=x64
17 17 #else
18 18 AppVerName=Mercurial {#VERSION}
19 19 OutputBaseFilename=Mercurial-{#VERSION}
20 20 #endif
21 21 InfoAfterFile=../postinstall.txt
22 22 LicenseFile=Copying.txt
23 23 ShowLanguageDialog=yes
24 24 AppPublisher=Matt Mackall and others
25 25 AppPublisherURL=https://mercurial-scm.org/
26 26 AppSupportURL=https://mercurial-scm.org/
27 27 AppUpdatesURL=https://mercurial-scm.org/
28 28 {{ 'AppID={{4B95A5F1-EF59-4B08-BED8-C891C46121B3}' }}
29 29 AppContact=mercurial@mercurial-scm.org
30 30 DefaultDirName={pf}\Mercurial
31 31 SourceDir=stage
32 32 VersionInfoDescription=Mercurial distributed SCM (version {#VERSION})
33 33 VersionInfoCopyright=Copyright 2005-2019 Matt Mackall and others
34 34 VersionInfoCompany=Matt Mackall and others
35 35 InternalCompressLevel=max
36 36 SolidCompression=true
37 37 SetupIconFile=../mercurial.ico
38 38 AllowNoIcons=true
39 39 DefaultGroupName=Mercurial
40 40 PrivilegesRequired=none
41 41 ChangesEnvironment=true
42 42
43 43 [Files]
44 44 {% for entry in package_files -%}
45 45 Source: {{ entry.source }}; DestDir: {{ entry.dest_dir }}
46 46 {%- if entry.metadata %}; {{ entry.metadata }}{% endif %}
47 47 {% endfor %}
48 48
49 49 [INI]
50 50 Filename: {app}\Mercurial.url; Section: InternetShortcut; Key: URL; String: https://mercurial-scm.org/
51 51
52 52 [UninstallDelete]
53 53 Type: files; Name: {app}\Mercurial.url
54 Type: filesandordirs; Name: {app}\hgrc.d
54 Type: filesandordirs; Name: {app}\defaultrc
55 55
56 56 [Icons]
57 57 Name: {group}\Uninstall Mercurial; Filename: {uninstallexe}
58 58 Name: {group}\Mercurial Command Reference; Filename: {app}\Docs\hg.1.html
59 59 Name: {group}\Mercurial Configuration Files; Filename: {app}\Docs\hgrc.5.html
60 60 Name: {group}\Mercurial Ignore Files; Filename: {app}\Docs\hgignore.5.html
61 61 Name: {group}\Mercurial Web Site; Filename: {app}\Mercurial.url
62 62
63 63 [Tasks]
64 64 Name: modifypath; Description: Add the installation path to the search path; Flags: unchecked
65 65
66 66 [Code]
67 67 procedure Touch(fn: String);
68 68 begin
69 69 SaveStringToFile(ExpandConstant(fn), '', False);
70 70 end;
71 71
72 72 const
73 73 ModPathName = 'modifypath';
74 74 ModPathType = 'user';
75 75
76 76 function ModPathDir(): TArrayOfString;
77 77 begin
78 78 setArrayLength(Result, 1)
79 79 Result[0] := ExpandConstant('{app}');
80 80 end;
81 81
82 82 {% include 'modpath.iss' %}
@@ -1,143 +1,143 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 <CreateFolder />
64 64 <Environment Id="Environment" Name="PATH" Part="last" System="yes"
65 65 Permanent="no" Value="[INSTALLDIR]" Action="set" />
66 66 </Component>
67 67 </Directory>
68 68 </Directory>
69 69
70 70 <Directory Id="ProgramMenuFolder" Name="Programs">
71 71 <Directory Id="ProgramMenuDir" Name="Mercurial $(var.Version)">
72 72 <Component Id="ProgramMenuDir" Guid="$(var.ProgramMenuDir.guid)" Win64='$(var.IsX64)'>
73 73 <RemoveFolder Id='ProgramMenuDir' On='uninstall' />
74 74 <RegistryValue Root='HKCU' Key='Software\Mercurial\InstallDir' Type='string'
75 75 Value='[INSTALLDIR]' KeyPath='yes' />
76 76 <Shortcut Id='UrlShortcut' Directory='ProgramMenuDir' Name='Mercurial Web Site'
77 77 Target='[ARPHELPLINK]' Icon="hgIcon.ico" IconIndex='0' />
78 78 </Component>
79 79 </Directory>
80 80 </Directory>
81 81
82 82 <?if $(var.Platform) = "x86" ?>
83 83 <Merge Id='VCRuntime' DiskId='1' Language='1033'
84 84 SourceFile='$(var.VCRedistSrcDir)\microsoft.vcxx.crt.x86_msm.msm' />
85 85 <Merge Id='VCRuntimePolicy' DiskId='1' Language='1033'
86 86 SourceFile='$(var.VCRedistSrcDir)\policy.x.xx.microsoft.vcxx.crt.x86_msm.msm' />
87 87 <?else?>
88 88 <Merge Id='VCRuntime' DiskId='1' Language='1033'
89 89 SourceFile='$(var.VCRedistSrcDir)\microsoft.vcxx.crt.x64_msm.msm' />
90 90 <Merge Id='VCRuntimePolicy' DiskId='1' Language='1033'
91 91 SourceFile='$(var.VCRedistSrcDir)\policy.x.xx.microsoft.vcxx.crt.x64_msm.msm' />
92 92 <?endif?>
93 93 </Directory>
94 94
95 95 <Feature Id='Complete' Title='Mercurial' Description='The complete package'
96 96 Display='expand' Level='1' ConfigurableDirectory='INSTALLDIR' >
97 97 <Feature Id='MainProgram' Title='Program' Description='Mercurial command line app'
98 98 Level='1' Absent='disallow' >
99 99 <ComponentRef Id='MainExecutable' />
100 100 <ComponentRef Id='ProgramMenuDir' />
101 101 <ComponentGroupRef Id="hg.group.ROOT" />
102 <ComponentGroupRef Id="hg.group.hgrc.d" />
102 <ComponentGroupRef Id="hg.group.defaultrc" />
103 103 <ComponentGroupRef Id="hg.group.helptext" />
104 104 <ComponentGroupRef Id="hg.group.lib" />
105 105 <ComponentGroupRef Id="hg.group.templates" />
106 106 <MergeRef Id='VCRuntime' />
107 107 <MergeRef Id='VCRuntimePolicy' />
108 108 </Feature>
109 109 <?ifdef MercurialExtraFeatures?>
110 110 <?foreach EXTRAFEAT in $(var.MercurialExtraFeatures)?>
111 111 <FeatureRef Id="$(var.EXTRAFEAT)" />
112 112 <?endforeach?>
113 113 <?endif?>
114 114 <Feature Id='Locales' Title='Translations' Description='Translations' Level='1'>
115 115 <ComponentGroupRef Id="hg.group.locale" />
116 116 </Feature>
117 117 <Feature Id='Documentation' Title='Documentation' Description='HTML man pages' Level='1'>
118 118 <ComponentGroupRef Id="hg.group.doc" />
119 119 </Feature>
120 120 <Feature Id='Misc' Title='Miscellaneous' Description='Contributed scripts' Level='1'>
121 121 <ComponentGroupRef Id="hg.group.contrib" />
122 122 </Feature>
123 123 </Feature>
124 124
125 125 <UIRef Id="WixUI_FeatureTree" />
126 126 <UIRef Id="WixUI_ErrorProgressText" />
127 127
128 128 <WixVariable Id="WixUILicenseRtf" Value="contrib\packaging\wix\COPYING.rtf" />
129 129
130 130 <Icon Id="hgIcon.ico" SourceFile="contrib/win32/mercurial.ico" />
131 131
132 132 <Upgrade Id='$(var.ProductUpgradeCode)'>
133 133 <UpgradeVersion
134 134 IncludeMinimum='yes' Minimum='0.0.0' IncludeMaximum='no' OnlyDetect='no'
135 135 Property='INSTALLEDMERCURIALPRODUCTS' />
136 136 </Upgrade>
137 137
138 138 <InstallExecuteSequence>
139 139 <RemoveExistingProducts After='InstallInitialize'/>
140 140 </InstallExecuteSequence>
141 141
142 142 </Product>
143 143 </Wix>
General Comments 0
You need to be logged in to leave comments. Login now