##// END OF EJS Templates
merge with stable
Martin von Zweigbergk -
r45280:16cba0ad merge default
parent child Browse files
Show More
@@ -0,0 +1,145 b''
1 # pyoxidizer.py - Packaging support for PyOxidizer
2 #
3 # Copyright 2020 Gregory Szorc <gregory.szorc@gmail.com>
4 #
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.
7
8 # no-check-code because Python 3 native.
9
10 import os
11 import pathlib
12 import shutil
13 import subprocess
14 import sys
15
16 from .downloads import download_entry
17 from .util import (
18 extract_zip_to_directory,
19 process_install_rules,
20 find_vc_runtime_dll,
21 )
22
23
24 STAGING_RULES_WINDOWS = [
25 ('contrib/bash_completion', 'contrib/'),
26 ('contrib/hgk', 'contrib/hgk.tcl'),
27 ('contrib/hgweb.fcgi', 'contrib/'),
28 ('contrib/hgweb.wsgi', 'contrib/'),
29 ('contrib/logo-droplets.svg', 'contrib/'),
30 ('contrib/mercurial.el', 'contrib/'),
31 ('contrib/mq.el', 'contrib/'),
32 ('contrib/tcsh_completion', 'contrib/'),
33 ('contrib/tcsh_completion_build.sh', 'contrib/'),
34 ('contrib/vim/*', 'contrib/vim/'),
35 ('contrib/win32/postinstall.txt', 'ReleaseNotes.txt'),
36 ('contrib/win32/ReadMe.html', 'ReadMe.html'),
37 ('contrib/xml.rnc', 'contrib/'),
38 ('contrib/zsh_completion', 'contrib/'),
39 ('doc/*.html', 'doc/'),
40 ('doc/style.css', 'doc/'),
41 ('COPYING', 'Copying.txt'),
42 ]
43
44 STAGING_RULES_APP = [
45 ('mercurial/helptext/**/*.txt', 'helptext/'),
46 ('mercurial/defaultrc/*.rc', 'defaultrc/'),
47 ('mercurial/locale/**/*', 'locale/'),
48 ('mercurial/templates/**/*', 'templates/'),
49 ]
50
51 STAGING_EXCLUDES_WINDOWS = [
52 "doc/hg-ssh.8.html",
53 ]
54
55
56 def run_pyoxidizer(
57 source_dir: pathlib.Path,
58 build_dir: pathlib.Path,
59 out_dir: pathlib.Path,
60 target_triple: str,
61 ):
62 """Build Mercurial with PyOxidizer and copy additional files into place.
63
64 After successful completion, ``out_dir`` contains files constituting a
65 Mercurial install.
66 """
67 # We need to make gettext binaries available for compiling i18n files.
68 gettext_pkg, gettext_entry = download_entry('gettext', build_dir)
69 gettext_dep_pkg = download_entry('gettext-dep', build_dir)[0]
70
71 gettext_root = build_dir / ('gettext-win-%s' % gettext_entry['version'])
72
73 if not gettext_root.exists():
74 extract_zip_to_directory(gettext_pkg, gettext_root)
75 extract_zip_to_directory(gettext_dep_pkg, gettext_root)
76
77 env = dict(os.environ)
78 env["PATH"] = "%s%s%s" % (
79 env["PATH"],
80 os.pathsep,
81 str(gettext_root / "bin"),
82 )
83
84 args = [
85 "pyoxidizer",
86 "build",
87 "--path",
88 str(source_dir / "rust" / "hgcli"),
89 "--release",
90 "--target-triple",
91 target_triple,
92 ]
93
94 subprocess.run(args, env=env, check=True)
95
96 if "windows" in target_triple:
97 target = "app_windows"
98 else:
99 target = "app_posix"
100
101 build_dir = (
102 source_dir / "build" / "pyoxidizer" / target_triple / "release" / target
103 )
104
105 if out_dir.exists():
106 print("purging %s" % out_dir)
107 shutil.rmtree(out_dir)
108
109 # Now assemble all the files from PyOxidizer into the staging directory.
110 shutil.copytree(build_dir, out_dir)
111
112 # Move some of those files around.
113 process_install_rules(STAGING_RULES_APP, build_dir, out_dir)
114 # Nuke the mercurial/* directory, as we copied resources
115 # to an appropriate location just above.
116 shutil.rmtree(out_dir / "mercurial")
117
118 # We also need to run setup.py build_doc to produce html files,
119 # as they aren't built as part of ``pip install``.
120 # This will fail if docutils isn't installed.
121 subprocess.run(
122 [sys.executable, str(source_dir / "setup.py"), "build_doc", "--html"],
123 cwd=str(source_dir),
124 check=True,
125 )
126
127 if "windows" in target_triple:
128 process_install_rules(STAGING_RULES_WINDOWS, source_dir, out_dir)
129
130 # Write out a default editor.rc file to configure notepad as the
131 # default editor.
132 with (out_dir / "defaultrc" / "editor.rc").open(
133 "w", encoding="utf-8"
134 ) as fh:
135 fh.write("[ui]\neditor = notepad\n")
136
137 for f in STAGING_EXCLUDES_WINDOWS:
138 p = out_dir / f
139 if p.exists():
140 print("removing %s" % p)
141 p.unlink()
142
143 # Add vcruntimeXXX.dll next to executable.
144 vc_runtime_dll = find_vc_runtime_dll(x64="x86_64" in target_triple)
145 shutil.copy(vc_runtime_dll, out_dir / vc_runtime_dll.name)
@@ -63,7 +63,13 b' def bootstrap_windows_dev(hga: HGAutomat'
63
63
64
64
65 def build_inno(
65 def build_inno(
66 hga: HGAutomation, aws_region, arch, revision, version, base_image_name
66 hga: HGAutomation,
67 aws_region,
68 python_version,
69 arch,
70 revision,
71 version,
72 base_image_name,
67 ):
73 ):
68 c = hga.aws_connection(aws_region)
74 c = hga.aws_connection(aws_region)
69 image = aws.ensure_windows_dev_ami(c, base_image_name=base_image_name)
75 image = aws.ensure_windows_dev_ami(c, base_image_name=base_image_name)
@@ -74,14 +80,25 b' def build_inno('
74
80
75 windows.synchronize_hg(SOURCE_ROOT, revision, instance)
81 windows.synchronize_hg(SOURCE_ROOT, revision, instance)
76
82
77 for a in arch:
83 for py_version in python_version:
78 windows.build_inno_installer(
84 for a in arch:
79 instance.winrm_client, a, DIST_PATH, version=version
85 windows.build_inno_installer(
80 )
86 instance.winrm_client,
87 py_version,
88 a,
89 DIST_PATH,
90 version=version,
91 )
81
92
82
93
83 def build_wix(
94 def build_wix(
84 hga: HGAutomation, aws_region, arch, revision, version, base_image_name
95 hga: HGAutomation,
96 aws_region,
97 python_version,
98 arch,
99 revision,
100 version,
101 base_image_name,
85 ):
102 ):
86 c = hga.aws_connection(aws_region)
103 c = hga.aws_connection(aws_region)
87 image = aws.ensure_windows_dev_ami(c, base_image_name=base_image_name)
104 image = aws.ensure_windows_dev_ami(c, base_image_name=base_image_name)
@@ -92,14 +109,24 b' def build_wix('
92
109
93 windows.synchronize_hg(SOURCE_ROOT, revision, instance)
110 windows.synchronize_hg(SOURCE_ROOT, revision, instance)
94
111
95 for a in arch:
112 for py_version in python_version:
96 windows.build_wix_installer(
113 for a in arch:
97 instance.winrm_client, a, DIST_PATH, version=version
114 windows.build_wix_installer(
98 )
115 instance.winrm_client,
116 py_version,
117 a,
118 DIST_PATH,
119 version=version,
120 )
99
121
100
122
101 def build_windows_wheel(
123 def build_windows_wheel(
102 hga: HGAutomation, aws_region, arch, revision, base_image_name
124 hga: HGAutomation,
125 aws_region,
126 python_version,
127 arch,
128 revision,
129 base_image_name,
103 ):
130 ):
104 c = hga.aws_connection(aws_region)
131 c = hga.aws_connection(aws_region)
105 image = aws.ensure_windows_dev_ami(c, base_image_name=base_image_name)
132 image = aws.ensure_windows_dev_ami(c, base_image_name=base_image_name)
@@ -110,8 +137,11 b' def build_windows_wheel('
110
137
111 windows.synchronize_hg(SOURCE_ROOT, revision, instance)
138 windows.synchronize_hg(SOURCE_ROOT, revision, instance)
112
139
113 for a in arch:
140 for py_version in python_version:
114 windows.build_wheel(instance.winrm_client, a, DIST_PATH)
141 for a in arch:
142 windows.build_wheel(
143 instance.winrm_client, py_version, a, DIST_PATH
144 )
115
145
116
146
117 def build_all_windows_packages(
147 def build_all_windows_packages(
@@ -128,17 +158,25 b' def build_all_windows_packages('
128
158
129 windows.synchronize_hg(SOURCE_ROOT, revision, instance)
159 windows.synchronize_hg(SOURCE_ROOT, revision, instance)
130
160
131 for arch in ('x86', 'x64'):
161 for py_version in ("2.7", "3.7", "3.8"):
132 windows.purge_hg(winrm_client)
162 for arch in ("x86", "x64"):
133 windows.build_wheel(winrm_client, arch, DIST_PATH)
163 windows.purge_hg(winrm_client)
134 windows.purge_hg(winrm_client)
164 windows.build_wheel(
135 windows.build_inno_installer(
165 winrm_client,
136 winrm_client, arch, DIST_PATH, version=version
166 python_version=py_version,
137 )
167 arch=arch,
138 windows.purge_hg(winrm_client)
168 dest_path=DIST_PATH,
139 windows.build_wix_installer(
169 )
140 winrm_client, arch, DIST_PATH, version=version
170
141 )
171 for py_version in (2, 3):
172 for arch in ('x86', 'x64'):
173 windows.purge_hg(winrm_client)
174 windows.build_inno_installer(
175 winrm_client, py_version, arch, DIST_PATH, version=version
176 )
177 windows.build_wix_installer(
178 winrm_client, py_version, arch, DIST_PATH, version=version
179 )
142
180
143
181
144 def terminate_ec2_instances(hga: HGAutomation, aws_region):
182 def terminate_ec2_instances(hga: HGAutomation, aws_region):
@@ -293,6 +331,14 b' def get_parser():'
293 'build-inno', help='Build Inno Setup installer(s)',
331 'build-inno', help='Build Inno Setup installer(s)',
294 )
332 )
295 sp.add_argument(
333 sp.add_argument(
334 '--python-version',
335 help='Which version of Python to target',
336 choices={2, 3},
337 type=int,
338 nargs='*',
339 default=[3],
340 )
341 sp.add_argument(
296 '--arch',
342 '--arch',
297 help='Architecture to build for',
343 help='Architecture to build for',
298 choices={'x86', 'x64'},
344 choices={'x86', 'x64'},
@@ -316,6 +362,13 b' def get_parser():'
316 'build-windows-wheel', help='Build Windows wheel(s)',
362 'build-windows-wheel', help='Build Windows wheel(s)',
317 )
363 )
318 sp.add_argument(
364 sp.add_argument(
365 '--python-version',
366 help='Python version to build for',
367 choices={'2.7', '3.7', '3.8'},
368 nargs='*',
369 default=['3.8'],
370 )
371 sp.add_argument(
319 '--arch',
372 '--arch',
320 help='Architecture to build for',
373 help='Architecture to build for',
321 choices={'x86', 'x64'},
374 choices={'x86', 'x64'},
@@ -334,6 +387,14 b' def get_parser():'
334
387
335 sp = subparsers.add_parser('build-wix', help='Build WiX installer(s)')
388 sp = subparsers.add_parser('build-wix', help='Build WiX installer(s)')
336 sp.add_argument(
389 sp.add_argument(
390 '--python-version',
391 help='Which version of Python to target',
392 choices={2, 3},
393 type=int,
394 nargs='*',
395 default=[3],
396 )
397 sp.add_argument(
337 '--arch',
398 '--arch',
338 help='Architecture to build for',
399 help='Architecture to build for',
339 choices={'x86', 'x64'},
400 choices={'x86', 'x64'},
@@ -72,8 +72,10 b' echo "${RUSTUP_INIT_SHA256} rustup-init"'
72
72
73 chmod +x rustup-init
73 chmod +x rustup-init
74 sudo -H -u hg -g hg ./rustup-init -y
74 sudo -H -u hg -g hg ./rustup-init -y
75 sudo -H -u hg -g hg /home/hg/.cargo/bin/rustup install 1.31.1 1.34.2
75 sudo -H -u hg -g hg /home/hg/.cargo/bin/rustup install 1.31.1 1.42.0
76 sudo -H -u hg -g hg /home/hg/.cargo/bin/rustup component add clippy
76 sudo -H -u hg -g hg /home/hg/.cargo/bin/rustup component add clippy
77
78 sudo -H -u hg -g hg /home/hg/.cargo/bin/cargo install --version 0.7.0 pyoxidizer
77 '''
79 '''
78
80
79
81
@@ -68,10 +68,20 b' hg.exe log -r .'
68 Write-Output "updated Mercurial working directory to {revision}"
68 Write-Output "updated Mercurial working directory to {revision}"
69 '''.lstrip()
69 '''.lstrip()
70
70
71 BUILD_INNO = r'''
71 BUILD_INNO_PYTHON3 = r'''
72 $Env:RUSTUP_HOME = "C:\hgdev\rustup"
73 $Env:CARGO_HOME = "C:\hgdev\cargo"
74 Set-Location C:\hgdev\src
75 C:\hgdev\python37-x64\python.exe contrib\packaging\packaging.py inno --pyoxidizer-target {pyoxidizer_target} --version {version}
76 if ($LASTEXITCODE -ne 0) {{
77 throw "process exited non-0: $LASTEXITCODE"
78 }}
79 '''
80
81 BUILD_INNO_PYTHON2 = r'''
72 Set-Location C:\hgdev\src
82 Set-Location C:\hgdev\src
73 $python = "C:\hgdev\python27-{arch}\python.exe"
83 $python = "C:\hgdev\python27-{arch}\python.exe"
74 C:\hgdev\python37-x64\python.exe contrib\packaging\packaging.py inno --python $python
84 C:\hgdev\python37-x64\python.exe contrib\packaging\packaging.py inno --python $python {extra_args}
75 if ($LASTEXITCODE -ne 0) {{
85 if ($LASTEXITCODE -ne 0) {{
76 throw "process exited non-0: $LASTEXITCODE"
86 throw "process exited non-0: $LASTEXITCODE"
77 }}
87 }}
@@ -79,13 +89,23 b' if ($LASTEXITCODE -ne 0) {{'
79
89
80 BUILD_WHEEL = r'''
90 BUILD_WHEEL = r'''
81 Set-Location C:\hgdev\src
91 Set-Location C:\hgdev\src
82 C:\hgdev\python27-{arch}\Scripts\pip.exe wheel --wheel-dir dist .
92 C:\hgdev\python{python_version}-{arch}\python.exe -m pip wheel --wheel-dir dist .
83 if ($LASTEXITCODE -ne 0) {{
93 if ($LASTEXITCODE -ne 0) {{
84 throw "process exited non-0: $LASTEXITCODE"
94 throw "process exited non-0: $LASTEXITCODE"
85 }}
95 }}
86 '''
96 '''
87
97
88 BUILD_WIX = r'''
98 BUILD_WIX_PYTHON3 = r'''
99 $Env:RUSTUP_HOME = "C:\hgdev\rustup"
100 $Env:CARGO_HOME = "C:\hgdev\cargo"
101 Set-Location C:\hgdev\src
102 C:\hgdev\python37-x64\python.exe contrib\packaging\packaging.py wix --pyoxidizer-target {pyoxidizer_target} --version {version}
103 if ($LASTEXITCODE -ne 0) {{
104 throw "process exited non-0: $LASTEXITCODE"
105 }}
106 '''
107
108 BUILD_WIX_PYTHON2 = r'''
89 Set-Location C:\hgdev\src
109 Set-Location C:\hgdev\src
90 $python = "C:\hgdev\python27-{arch}\python.exe"
110 $python = "C:\hgdev\python27-{arch}\python.exe"
91 C:\hgdev\python37-x64\python.exe contrib\packaging\packaging.py wix --python $python {extra_args}
111 C:\hgdev\python37-x64\python.exe contrib\packaging\packaging.py wix --python $python {extra_args}
@@ -101,31 +121,60 b' if ($LASTEXITCODE -ne 0) {{'
101 }}
121 }}
102 '''
122 '''
103
123
104 X86_WHEEL_FILENAME = 'mercurial-{version}-cp27-cp27m-win32.whl'
124 WHEEL_FILENAME_PYTHON27_X86 = 'mercurial-{version}-cp27-cp27m-win32.whl'
105 X64_WHEEL_FILENAME = 'mercurial-{version}-cp27-cp27m-win_amd64.whl'
125 WHEEL_FILENAME_PYTHON27_X64 = 'mercurial-{version}-cp27-cp27m-win_amd64.whl'
106 X86_EXE_FILENAME = 'Mercurial-{version}.exe'
126 WHEEL_FILENAME_PYTHON37_X86 = 'mercurial-{version}-cp37-cp37m-win32.whl'
107 X64_EXE_FILENAME = 'Mercurial-{version}-x64.exe'
127 WHEEL_FILENAME_PYTHON37_X64 = 'mercurial-{version}-cp37-cp37m-win_amd64.whl'
108 X86_MSI_FILENAME = 'mercurial-{version}-x86.msi'
128 WHEEL_FILENAME_PYTHON38_X86 = 'mercurial-{version}-cp38-cp38-win32.whl'
109 X64_MSI_FILENAME = 'mercurial-{version}-x64.msi'
129 WHEEL_FILENAME_PYTHON38_X64 = 'mercurial-{version}-cp38-cp38-win_amd64.whl'
130
131 EXE_FILENAME_PYTHON2_X86 = 'Mercurial-{version}-x86-python2.exe'
132 EXE_FILENAME_PYTHON2_X64 = 'Mercurial-{version}-x64-python2.exe'
133 EXE_FILENAME_PYTHON3_X86 = 'Mercurial-{version}-x86.exe'
134 EXE_FILENAME_PYTHON3_X64 = 'Mercurial-{version}-x64.exe'
135
136 MSI_FILENAME_PYTHON2_X86 = 'mercurial-{version}-x86-python2.msi'
137 MSI_FILENAME_PYTHON2_X64 = 'mercurial-{version}-x64-python2.msi'
138 MSI_FILENAME_PYTHON3_X86 = 'mercurial-{version}-x86.msi'
139 MSI_FILENAME_PYTHON3_X64 = 'mercurial-{version}-x64.msi'
110
140
111 MERCURIAL_SCM_BASE_URL = 'https://mercurial-scm.org/release/windows'
141 MERCURIAL_SCM_BASE_URL = 'https://mercurial-scm.org/release/windows'
112
142
113 X86_USER_AGENT_PATTERN = '.*Windows.*'
143 X86_USER_AGENT_PATTERN = '.*Windows.*'
114 X64_USER_AGENT_PATTERN = '.*Windows.*(WOW|x)64.*'
144 X64_USER_AGENT_PATTERN = '.*Windows.*(WOW|x)64.*'
115
145
116 X86_EXE_DESCRIPTION = (
146 EXE_PYTHON2_X86_DESCRIPTION = (
117 'Mercurial {version} Inno Setup installer - x86 Windows '
147 'Mercurial {version} Inno Setup installer - x86 Windows (Python 2) '
148 '- does not require admin rights'
149 )
150 EXE_PYTHON2_X64_DESCRIPTION = (
151 'Mercurial {version} Inno Setup installer - x64 Windows (Python 2) '
152 '- does not require admin rights'
153 )
154 # TODO remove Python version once Python 2 is dropped.
155 EXE_PYTHON3_X86_DESCRIPTION = (
156 'Mercurial {version} Inno Setup installer - x86 Windows (Python 3) '
118 '- does not require admin rights'
157 '- does not require admin rights'
119 )
158 )
120 X64_EXE_DESCRIPTION = (
159 EXE_PYTHON3_X64_DESCRIPTION = (
121 'Mercurial {version} Inno Setup installer - x64 Windows '
160 'Mercurial {version} Inno Setup installer - x64 Windows (Python 3) '
122 '- does not require admin rights'
161 '- does not require admin rights'
123 )
162 )
124 X86_MSI_DESCRIPTION = (
163 MSI_PYTHON2_X86_DESCRIPTION = (
125 'Mercurial {version} MSI installer - x86 Windows ' '- requires admin rights'
164 'Mercurial {version} MSI installer - x86 Windows (Python 2) '
165 '- requires admin rights'
166 )
167 MSI_PYTHON2_X64_DESCRIPTION = (
168 'Mercurial {version} MSI installer - x64 Windows (Python 2) '
169 '- requires admin rights'
126 )
170 )
127 X64_MSI_DESCRIPTION = (
171 MSI_PYTHON3_X86_DESCRIPTION = (
128 'Mercurial {version} MSI installer - x64 Windows ' '- requires admin rights'
172 'Mercurial {version} MSI installer - x86 Windows (Python 3) '
173 '- requires admin rights'
174 )
175 MSI_PYTHON3_X64_DESCRIPTION = (
176 'Mercurial {version} MSI installer - x64 Windows (Python 3) '
177 '- requires admin rights'
129 )
178 )
130
179
131
180
@@ -280,53 +329,113 b' def copy_latest_dist(winrm_client, patte'
280
329
281
330
282 def build_inno_installer(
331 def build_inno_installer(
283 winrm_client, arch: str, dest_path: pathlib.Path, version=None
332 winrm_client,
333 python_version: int,
334 arch: str,
335 dest_path: pathlib.Path,
336 version=None,
284 ):
337 ):
285 """Build the Inno Setup installer on a remote machine.
338 """Build the Inno Setup installer on a remote machine.
286
339
287 Using a WinRM client, remote commands are executed to build
340 Using a WinRM client, remote commands are executed to build
288 a Mercurial Inno Setup installer.
341 a Mercurial Inno Setup installer.
289 """
342 """
290 print('building Inno Setup installer for %s' % arch)
343 print(
344 'building Inno Setup installer for Python %d %s'
345 % (python_version, arch)
346 )
347
348 if python_version == 3:
349 # TODO fix this limitation in packaging code
350 if not version:
351 raise Exception(
352 "version string is required when building for Python 3"
353 )
291
354
292 extra_args = []
355 if arch == "x86":
293 if version:
356 target_triple = "i686-pc-windows-msvc"
294 extra_args.extend(['--version', version])
357 elif arch == "x64":
358 target_triple = "x86_64-pc-windows-msvc"
359 else:
360 raise Exception("unhandled arch: %s" % arch)
295
361
296 ps = get_vc_prefix(arch) + BUILD_INNO.format(
362 ps = BUILD_INNO_PYTHON3.format(
297 arch=arch, extra_args=' '.join(extra_args)
363 pyoxidizer_target=target_triple, version=version,
298 )
364 )
365 else:
366 extra_args = []
367 if version:
368 extra_args.extend(['--version', version])
369
370 ps = get_vc_prefix(arch) + BUILD_INNO_PYTHON2.format(
371 arch=arch, extra_args=' '.join(extra_args)
372 )
373
299 run_powershell(winrm_client, ps)
374 run_powershell(winrm_client, ps)
300 copy_latest_dist(winrm_client, '*.exe', dest_path)
375 copy_latest_dist(winrm_client, '*.exe', dest_path)
301
376
302
377
303 def build_wheel(winrm_client, arch: str, dest_path: pathlib.Path):
378 def build_wheel(
379 winrm_client, python_version: str, arch: str, dest_path: pathlib.Path
380 ):
304 """Build Python wheels on a remote machine.
381 """Build Python wheels on a remote machine.
305
382
306 Using a WinRM client, remote commands are executed to build a Python wheel
383 Using a WinRM client, remote commands are executed to build a Python wheel
307 for Mercurial.
384 for Mercurial.
308 """
385 """
309 print('Building Windows wheel for %s' % arch)
386 print('Building Windows wheel for Python %s %s' % (python_version, arch))
310 ps = get_vc_prefix(arch) + BUILD_WHEEL.format(arch=arch)
387
388 ps = BUILD_WHEEL.format(
389 python_version=python_version.replace(".", ""), arch=arch
390 )
391
392 # Python 2.7 requires an activated environment.
393 if python_version == "2.7":
394 ps = get_vc_prefix(arch) + ps
395
311 run_powershell(winrm_client, ps)
396 run_powershell(winrm_client, ps)
312 copy_latest_dist(winrm_client, '*.whl', dest_path)
397 copy_latest_dist(winrm_client, '*.whl', dest_path)
313
398
314
399
315 def build_wix_installer(
400 def build_wix_installer(
316 winrm_client, arch: str, dest_path: pathlib.Path, version=None
401 winrm_client,
402 python_version: int,
403 arch: str,
404 dest_path: pathlib.Path,
405 version=None,
317 ):
406 ):
318 """Build the WiX installer on a remote machine.
407 """Build the WiX installer on a remote machine.
319
408
320 Using a WinRM client, remote commands are executed to build a WiX installer.
409 Using a WinRM client, remote commands are executed to build a WiX installer.
321 """
410 """
322 print('Building WiX installer for %s' % arch)
411 print('Building WiX installer for Python %d %s' % (python_version, arch))
323 extra_args = []
412
324 if version:
413 if python_version == 3:
325 extra_args.extend(['--version', version])
414 # TODO fix this limitation in packaging code
415 if not version:
416 raise Exception(
417 "version string is required when building for Python 3"
418 )
326
419
327 ps = get_vc_prefix(arch) + BUILD_WIX.format(
420 if arch == "x86":
328 arch=arch, extra_args=' '.join(extra_args)
421 target_triple = "i686-pc-windows-msvc"
329 )
422 elif arch == "x64":
423 target_triple = "x86_64-pc-windows-msvc"
424 else:
425 raise Exception("unhandled arch: %s" % arch)
426
427 ps = BUILD_WIX_PYTHON3.format(
428 pyoxidizer_target=target_triple, version=version,
429 )
430 else:
431 extra_args = []
432 if version:
433 extra_args.extend(['--version', version])
434
435 ps = get_vc_prefix(arch) + BUILD_WIX_PYTHON2.format(
436 arch=arch, extra_args=' '.join(extra_args)
437 )
438
330 run_powershell(winrm_client, ps)
439 run_powershell(winrm_client, ps)
331 copy_latest_dist(winrm_client, '*.msi', dest_path)
440 copy_latest_dist(winrm_client, '*.msi', dest_path)
332
441
@@ -356,56 +465,100 b' def run_tests(winrm_client, python_versi'
356
465
357 def resolve_wheel_artifacts(dist_path: pathlib.Path, version: str):
466 def resolve_wheel_artifacts(dist_path: pathlib.Path, version: str):
358 return (
467 return (
359 dist_path / X86_WHEEL_FILENAME.format(version=version),
468 dist_path / WHEEL_FILENAME_PYTHON27_X86.format(version=version),
360 dist_path / X64_WHEEL_FILENAME.format(version=version),
469 dist_path / WHEEL_FILENAME_PYTHON27_X64.format(version=version),
470 dist_path / WHEEL_FILENAME_PYTHON37_X86.format(version=version),
471 dist_path / WHEEL_FILENAME_PYTHON37_X64.format(version=version),
472 dist_path / WHEEL_FILENAME_PYTHON38_X86.format(version=version),
473 dist_path / WHEEL_FILENAME_PYTHON38_X64.format(version=version),
361 )
474 )
362
475
363
476
364 def resolve_all_artifacts(dist_path: pathlib.Path, version: str):
477 def resolve_all_artifacts(dist_path: pathlib.Path, version: str):
365 return (
478 return (
366 dist_path / X86_WHEEL_FILENAME.format(version=version),
479 dist_path / WHEEL_FILENAME_PYTHON27_X86.format(version=version),
367 dist_path / X64_WHEEL_FILENAME.format(version=version),
480 dist_path / WHEEL_FILENAME_PYTHON27_X64.format(version=version),
368 dist_path / X86_EXE_FILENAME.format(version=version),
481 dist_path / WHEEL_FILENAME_PYTHON37_X86.format(version=version),
369 dist_path / X64_EXE_FILENAME.format(version=version),
482 dist_path / WHEEL_FILENAME_PYTHON37_X64.format(version=version),
370 dist_path / X86_MSI_FILENAME.format(version=version),
483 dist_path / WHEEL_FILENAME_PYTHON38_X86.format(version=version),
371 dist_path / X64_MSI_FILENAME.format(version=version),
484 dist_path / WHEEL_FILENAME_PYTHON38_X64.format(version=version),
485 dist_path / EXE_FILENAME_PYTHON2_X86.format(version=version),
486 dist_path / EXE_FILENAME_PYTHON2_X64.format(version=version),
487 dist_path / EXE_FILENAME_PYTHON3_X86.format(version=version),
488 dist_path / EXE_FILENAME_PYTHON3_X64.format(version=version),
489 dist_path / MSI_FILENAME_PYTHON2_X86.format(version=version),
490 dist_path / MSI_FILENAME_PYTHON2_X64.format(version=version),
491 dist_path / MSI_FILENAME_PYTHON3_X86.format(version=version),
492 dist_path / MSI_FILENAME_PYTHON3_X64.format(version=version),
372 )
493 )
373
494
374
495
375 def generate_latest_dat(version: str):
496 def generate_latest_dat(version: str):
376 x86_exe_filename = X86_EXE_FILENAME.format(version=version)
497 python2_x86_exe_filename = EXE_FILENAME_PYTHON2_X86.format(version=version)
377 x64_exe_filename = X64_EXE_FILENAME.format(version=version)
498 python2_x64_exe_filename = EXE_FILENAME_PYTHON2_X64.format(version=version)
378 x86_msi_filename = X86_MSI_FILENAME.format(version=version)
499 python3_x86_exe_filename = EXE_FILENAME_PYTHON3_X86.format(version=version)
379 x64_msi_filename = X64_MSI_FILENAME.format(version=version)
500 python3_x64_exe_filename = EXE_FILENAME_PYTHON3_X64.format(version=version)
501 python2_x86_msi_filename = MSI_FILENAME_PYTHON2_X86.format(version=version)
502 python2_x64_msi_filename = MSI_FILENAME_PYTHON2_X64.format(version=version)
503 python3_x86_msi_filename = MSI_FILENAME_PYTHON3_X86.format(version=version)
504 python3_x64_msi_filename = MSI_FILENAME_PYTHON3_X64.format(version=version)
380
505
381 entries = (
506 entries = (
382 (
507 (
383 '10',
508 '10',
384 version,
509 version,
385 X86_USER_AGENT_PATTERN,
510 X86_USER_AGENT_PATTERN,
386 '%s/%s' % (MERCURIAL_SCM_BASE_URL, x86_exe_filename),
511 '%s/%s' % (MERCURIAL_SCM_BASE_URL, python3_x86_exe_filename),
387 X86_EXE_DESCRIPTION.format(version=version),
512 EXE_PYTHON3_X86_DESCRIPTION.format(version=version),
388 ),
513 ),
389 (
514 (
390 '10',
515 '10',
391 version,
516 version,
392 X64_USER_AGENT_PATTERN,
517 X64_USER_AGENT_PATTERN,
393 '%s/%s' % (MERCURIAL_SCM_BASE_URL, x64_exe_filename),
518 '%s/%s' % (MERCURIAL_SCM_BASE_URL, python3_x64_exe_filename),
394 X64_EXE_DESCRIPTION.format(version=version),
519 EXE_PYTHON3_X64_DESCRIPTION.format(version=version),
520 ),
521 (
522 '9',
523 version,
524 X86_USER_AGENT_PATTERN,
525 '%s/%s' % (MERCURIAL_SCM_BASE_URL, python2_x86_exe_filename),
526 EXE_PYTHON2_X86_DESCRIPTION.format(version=version),
527 ),
528 (
529 '9',
530 version,
531 X64_USER_AGENT_PATTERN,
532 '%s/%s' % (MERCURIAL_SCM_BASE_URL, python2_x64_exe_filename),
533 EXE_PYTHON2_X64_DESCRIPTION.format(version=version),
395 ),
534 ),
396 (
535 (
397 '10',
536 '10',
398 version,
537 version,
399 X86_USER_AGENT_PATTERN,
538 X86_USER_AGENT_PATTERN,
400 '%s/%s' % (MERCURIAL_SCM_BASE_URL, x86_msi_filename),
539 '%s/%s' % (MERCURIAL_SCM_BASE_URL, python3_x86_msi_filename),
401 X86_MSI_DESCRIPTION.format(version=version),
540 MSI_PYTHON3_X86_DESCRIPTION.format(version=version),
402 ),
541 ),
403 (
542 (
404 '10',
543 '10',
405 version,
544 version,
406 X64_USER_AGENT_PATTERN,
545 X64_USER_AGENT_PATTERN,
407 '%s/%s' % (MERCURIAL_SCM_BASE_URL, x64_msi_filename),
546 '%s/%s' % (MERCURIAL_SCM_BASE_URL, python3_x64_msi_filename),
408 X64_MSI_DESCRIPTION.format(version=version),
547 MSI_PYTHON3_X64_DESCRIPTION.format(version=version),
548 ),
549 (
550 '9',
551 version,
552 X86_USER_AGENT_PATTERN,
553 '%s/%s' % (MERCURIAL_SCM_BASE_URL, python2_x86_msi_filename),
554 MSI_PYTHON2_X86_DESCRIPTION.format(version=version),
555 ),
556 (
557 '9',
558 version,
559 X64_USER_AGENT_PATTERN,
560 '%s/%s' % (MERCURIAL_SCM_BASE_URL, python2_x64_msi_filename),
561 MSI_PYTHON2_X64_DESCRIPTION.format(version=version),
409 ),
562 ),
410 )
563 )
411
564
@@ -64,6 +64,9 b''
64 $MERCURIAL_WHEEL_URL = "https://files.pythonhosted.org/packages/6d/47/e031e47f7fe9b16e4e3383da47e2b0a7eae6e603996bc67a03ec4fa1b3f4/$MERCURIAL_WHEEL_FILENAME"
64 $MERCURIAL_WHEEL_URL = "https://files.pythonhosted.org/packages/6d/47/e031e47f7fe9b16e4e3383da47e2b0a7eae6e603996bc67a03ec4fa1b3f4/$MERCURIAL_WHEEL_FILENAME"
65 $MERCURIAL_WHEEL_SHA256 = "1d18c7f6ca1456f0f62ee65c9a50c14cbba48ce6e924930cdb10537f5c9eaf5f"
65 $MERCURIAL_WHEEL_SHA256 = "1d18c7f6ca1456f0f62ee65c9a50c14cbba48ce6e924930cdb10537f5c9eaf5f"
66
66
67 $RUSTUP_INIT_URL = "https://static.rust-lang.org/rustup/archive/1.21.1/x86_64-pc-windows-gnu/rustup-init.exe"
68 $RUSTUP_INIT_SHA256 = "d17df34ba974b9b19cf5c75883a95475aa22ddc364591d75d174090d55711c72"
69
67 # Writing progress slows down downloads substantially. So disable it.
70 # Writing progress slows down downloads substantially. So disable it.
68 $progressPreference = 'silentlyContinue'
71 $progressPreference = 'silentlyContinue'
69
72
@@ -116,6 +119,20 b' function Install-Python3($name, $install'
116 Invoke-Process ${dest}\python.exe $pip
119 Invoke-Process ${dest}\python.exe $pip
117 }
120 }
118
121
122 function Install-Rust($prefix) {
123 Write-Output "installing Rust"
124 $Env:RUSTUP_HOME = "${prefix}\rustup"
125 $Env:CARGO_HOME = "${prefix}\cargo"
126
127 Invoke-Process "${prefix}\assets\rustup-init.exe" "-y --default-host x86_64-pc-windows-msvc"
128 Invoke-Process "${prefix}\cargo\bin\rustup.exe" "target add i686-pc-windows-msvc"
129 Invoke-Process "${prefix}\cargo\bin\rustup.exe" "install 1.42.0"
130 Invoke-Process "${prefix}\cargo\bin\rustup.exe" "component add clippy"
131
132 # Install PyOxidizer for packaging.
133 Invoke-Process "${prefix}\cargo\bin\cargo.exe" "install --version 0.7.0 pyoxidizer"
134 }
135
119 function Install-Dependencies($prefix) {
136 function Install-Dependencies($prefix) {
120 if (!(Test-Path -Path $prefix\assets)) {
137 if (!(Test-Path -Path $prefix\assets)) {
121 New-Item -Path $prefix\assets -ItemType Directory
138 New-Item -Path $prefix\assets -ItemType Directory
@@ -140,6 +157,7 b' function Install-Dependencies($prefix) {'
140 Secure-Download $INNO_SETUP_URL ${prefix}\assets\InnoSetup.exe $INNO_SETUP_SHA256
157 Secure-Download $INNO_SETUP_URL ${prefix}\assets\InnoSetup.exe $INNO_SETUP_SHA256
141 Secure-Download $MINGW_BIN_URL ${prefix}\assets\mingw-get-bin.zip $MINGW_BIN_SHA256
158 Secure-Download $MINGW_BIN_URL ${prefix}\assets\mingw-get-bin.zip $MINGW_BIN_SHA256
142 Secure-Download $MERCURIAL_WHEEL_URL ${prefix}\assets\${MERCURIAL_WHEEL_FILENAME} $MERCURIAL_WHEEL_SHA256
159 Secure-Download $MERCURIAL_WHEEL_URL ${prefix}\assets\${MERCURIAL_WHEEL_FILENAME} $MERCURIAL_WHEEL_SHA256
160 Secure-Download $RUSTUP_INIT_URL ${prefix}\assets\rustup-init.exe $RUSTUP_INIT_SHA256
143
161
144 Write-Output "installing Python 2.7 32-bit"
162 Write-Output "installing Python 2.7 32-bit"
145 Invoke-Process msiexec.exe "/i ${prefix}\assets\python27-x86.msi /l* ${prefix}\assets\python27-x86.log /q TARGETDIR=${prefix}\python27-x86 ALLUSERS="
163 Invoke-Process msiexec.exe "/i ${prefix}\assets\python27-x86.msi /l* ${prefix}\assets\python27-x86.log /q TARGETDIR=${prefix}\python27-x86 ALLUSERS="
@@ -163,6 +181,8 b' function Install-Dependencies($prefix) {'
163 Write-Output "installing Visual Studio 2017 Build Tools and SDKs"
181 Write-Output "installing Visual Studio 2017 Build Tools and SDKs"
164 Invoke-Process ${prefix}\assets\vs_buildtools.exe "--quiet --wait --norestart --nocache --channelUri https://aka.ms/vs/15/release/channel --add Microsoft.VisualStudio.Workload.MSBuildTools --add Microsoft.VisualStudio.Component.Windows10SDK.17763 --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.Windows10SDK --add Microsoft.VisualStudio.Component.VC.140"
182 Invoke-Process ${prefix}\assets\vs_buildtools.exe "--quiet --wait --norestart --nocache --channelUri https://aka.ms/vs/15/release/channel --add Microsoft.VisualStudio.Workload.MSBuildTools --add Microsoft.VisualStudio.Component.Windows10SDK.17763 --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.Windows10SDK --add Microsoft.VisualStudio.Component.VC.140"
165
183
184 Install-Rust ${prefix}
185
166 Write-Output "installing Visual C++ 9.0 for Python 2.7"
186 Write-Output "installing Visual C++ 9.0 for Python 2.7"
167 Invoke-Process msiexec.exe "/i ${prefix}\assets\VCForPython27.msi /l* ${prefix}\assets\VCForPython27.log /q"
187 Invoke-Process msiexec.exe "/i ${prefix}\assets\VCForPython27.msi /l* ${prefix}\assets\VCForPython27.log /q"
168
188
@@ -20,8 +20,11 b' HERE = pathlib.Path(os.path.abspath(os.p'
20 SOURCE_DIR = HERE.parent.parent.parent
20 SOURCE_DIR = HERE.parent.parent.parent
21
21
22
22
23 def build_inno(python=None, iscc=None, version=None):
23 def build_inno(pyoxidizer_target=None, python=None, iscc=None, version=None):
24 if not os.path.isabs(python):
24 if not pyoxidizer_target and not python:
25 raise Exception("--python required unless building with PyOxidizer")
26
27 if python and not os.path.isabs(python):
25 raise Exception("--python arg must be an absolute path")
28 raise Exception("--python arg must be an absolute path")
26
29
27 if iscc:
30 if iscc:
@@ -35,13 +38,19 b' def build_inno(python=None, iscc=None, v'
35
38
36 build_dir = SOURCE_DIR / "build"
39 build_dir = SOURCE_DIR / "build"
37
40
38 inno.build(
41 if pyoxidizer_target:
39 SOURCE_DIR, build_dir, pathlib.Path(python), iscc, version=version,
42 inno.build_with_pyoxidizer(
40 )
43 SOURCE_DIR, build_dir, pyoxidizer_target, iscc, version=version
44 )
45 else:
46 inno.build_with_py2exe(
47 SOURCE_DIR, build_dir, pathlib.Path(python), iscc, version=version,
48 )
41
49
42
50
43 def build_wix(
51 def build_wix(
44 name=None,
52 name=None,
53 pyoxidizer_target=None,
45 python=None,
54 python=None,
46 version=None,
55 version=None,
47 sign_sn=None,
56 sign_sn=None,
@@ -52,17 +61,29 b' def build_wix('
52 extra_wxs=None,
61 extra_wxs=None,
53 extra_features=None,
62 extra_features=None,
54 ):
63 ):
55 fn = wix.build_installer
64 if not pyoxidizer_target and not python:
65 raise Exception("--python required unless building with PyOxidizer")
66
67 if python and not os.path.isabs(python):
68 raise Exception("--python arg must be an absolute path")
69
56 kwargs = {
70 kwargs = {
57 "source_dir": SOURCE_DIR,
71 "source_dir": SOURCE_DIR,
58 "python_exe": pathlib.Path(python),
59 "version": version,
72 "version": version,
60 }
73 }
61
74
62 if not os.path.isabs(python):
75 if pyoxidizer_target:
63 raise Exception("--python arg must be an absolute path")
76 fn = wix.build_installer_pyoxidizer
77 kwargs["target_triple"] = pyoxidizer_target
78 else:
79 fn = wix.build_installer_py2exe
80 kwargs["python_exe"] = pathlib.Path(python)
64
81
65 if extra_packages_script:
82 if extra_packages_script:
83 if pyoxidizer_target:
84 raise Exception(
85 "pyoxidizer does not support --extra-packages-script"
86 )
66 kwargs["extra_packages_script"] = extra_packages_script
87 kwargs["extra_packages_script"] = extra_packages_script
67 if extra_wxs:
88 if extra_wxs:
68 kwargs["extra_wxs"] = dict(
89 kwargs["extra_wxs"] = dict(
@@ -72,12 +93,13 b' def build_wix('
72 kwargs["extra_features"] = extra_features.split(",")
93 kwargs["extra_features"] = extra_features.split(",")
73
94
74 if sign_sn or sign_cert:
95 if sign_sn or sign_cert:
75 fn = wix.build_signed_installer
96 kwargs["signing_info"] = {
76 kwargs["name"] = name
97 "name": name,
77 kwargs["subject_name"] = sign_sn
98 "subject_name": sign_sn,
78 kwargs["cert_path"] = sign_cert
99 "cert_path": sign_cert,
79 kwargs["cert_password"] = sign_password
100 "cert_password": sign_password,
80 kwargs["timestamp_url"] = sign_timestamp_url
101 "timestamp_url": sign_timestamp_url,
102 }
81
103
82 fn(**kwargs)
104 fn(**kwargs)
83
105
@@ -88,7 +110,12 b' def get_parser():'
88 subparsers = parser.add_subparsers()
110 subparsers = parser.add_subparsers()
89
111
90 sp = subparsers.add_parser("inno", help="Build Inno Setup installer")
112 sp = subparsers.add_parser("inno", help="Build Inno Setup installer")
91 sp.add_argument("--python", required=True, help="path to python.exe to use")
113 sp.add_argument(
114 "--pyoxidizer-target",
115 choices={"i686-pc-windows-msvc", "x86_64-pc-windows-msvc"},
116 help="Build with PyOxidizer targeting this host triple",
117 )
118 sp.add_argument("--python", help="path to python.exe to use")
92 sp.add_argument("--iscc", help="path to iscc.exe to use")
119 sp.add_argument("--iscc", help="path to iscc.exe to use")
93 sp.add_argument(
120 sp.add_argument(
94 "--version",
121 "--version",
@@ -102,8 +129,11 b' def get_parser():'
102 )
129 )
103 sp.add_argument("--name", help="Application name", default="Mercurial")
130 sp.add_argument("--name", help="Application name", default="Mercurial")
104 sp.add_argument(
131 sp.add_argument(
105 "--python", help="Path to Python executable to use", required=True
132 "--pyoxidizer-target",
133 choices={"i686-pc-windows-msvc", "x86_64-pc-windows-msvc"},
134 help="Build with PyOxidizer targeting this host triple",
106 )
135 )
136 sp.add_argument("--python", help="Path to Python executable to use")
107 sp.add_argument(
137 sp.add_argument(
108 "--sign-sn",
138 "--sign-sn",
109 help="Subject name (or fragment thereof) of certificate "
139 help="Subject name (or fragment thereof) of certificate "
@@ -18,8 +18,9 b' from .py2exe import ('
18 build_py2exe,
18 build_py2exe,
19 stage_install,
19 stage_install,
20 )
20 )
21 from .pyoxidizer import run_pyoxidizer
21 from .util import (
22 from .util import (
22 find_vc_runtime_files,
23 find_legacy_vc_runtime_files,
23 normalize_windows_version,
24 normalize_windows_version,
24 process_install_rules,
25 process_install_rules,
25 read_version_py,
26 read_version_py,
@@ -41,14 +42,14 b' PACKAGE_FILES_METADATA = {'
41 }
42 }
42
43
43
44
44 def build(
45 def build_with_py2exe(
45 source_dir: pathlib.Path,
46 source_dir: pathlib.Path,
46 build_dir: pathlib.Path,
47 build_dir: pathlib.Path,
47 python_exe: pathlib.Path,
48 python_exe: pathlib.Path,
48 iscc_exe: pathlib.Path,
49 iscc_exe: pathlib.Path,
49 version=None,
50 version=None,
50 ):
51 ):
51 """Build the Inno installer.
52 """Build the Inno installer using py2exe.
52
53
53 Build files will be placed in ``build_dir``.
54 Build files will be placed in ``build_dir``.
54
55
@@ -61,8 +62,7 b' def build('
61
62
62 vc_x64 = r'\x64' in os.environ.get('LIB', '')
63 vc_x64 = r'\x64' in os.environ.get('LIB', '')
63 arch = 'x64' if vc_x64 else 'x86'
64 arch = 'x64' if vc_x64 else 'x86'
64 inno_source_dir = source_dir / 'contrib' / 'packaging' / 'inno'
65 inno_build_dir = build_dir / ('inno-py2exe-%s' % arch)
65 inno_build_dir = build_dir / ('inno-%s' % arch)
66 staging_dir = inno_build_dir / 'stage'
66 staging_dir = inno_build_dir / 'stage'
67
67
68 requirements_txt = (
68 requirements_txt = (
@@ -93,7 +93,7 b' def build('
93 process_install_rules(EXTRA_INSTALL_RULES, source_dir, staging_dir)
93 process_install_rules(EXTRA_INSTALL_RULES, source_dir, staging_dir)
94
94
95 # hg.exe depends on VC9 runtime DLLs. Copy those into place.
95 # hg.exe depends on VC9 runtime DLLs. Copy those into place.
96 for f in find_vc_runtime_files(vc_x64):
96 for f in find_legacy_vc_runtime_files(vc_x64):
97 if f.name.endswith('.manifest'):
97 if f.name.endswith('.manifest'):
98 basename = 'Microsoft.VC90.CRT.manifest'
98 basename = 'Microsoft.VC90.CRT.manifest'
99 else:
99 else:
@@ -104,6 +104,62 b' def build('
104 print('copying %s to %s' % (f, dest_path))
104 print('copying %s to %s' % (f, dest_path))
105 shutil.copyfile(f, dest_path)
105 shutil.copyfile(f, dest_path)
106
106
107 build_installer(
108 source_dir,
109 inno_build_dir,
110 staging_dir,
111 iscc_exe,
112 version,
113 arch="x64" if vc_x64 else None,
114 suffix="-python2",
115 )
116
117
118 def build_with_pyoxidizer(
119 source_dir: pathlib.Path,
120 build_dir: pathlib.Path,
121 target_triple: str,
122 iscc_exe: pathlib.Path,
123 version=None,
124 ):
125 """Build the Inno installer using PyOxidizer."""
126 if not iscc_exe.exists():
127 raise Exception("%s does not exist" % iscc_exe)
128
129 inno_build_dir = build_dir / ("inno-pyoxidizer-%s" % target_triple)
130 staging_dir = inno_build_dir / "stage"
131
132 inno_build_dir.mkdir(parents=True, exist_ok=True)
133 run_pyoxidizer(source_dir, inno_build_dir, staging_dir, target_triple)
134
135 process_install_rules(EXTRA_INSTALL_RULES, source_dir, staging_dir)
136
137 build_installer(
138 source_dir,
139 inno_build_dir,
140 staging_dir,
141 iscc_exe,
142 version,
143 arch="x64" if "x86_64" in target_triple else None,
144 )
145
146
147 def build_installer(
148 source_dir: pathlib.Path,
149 inno_build_dir: pathlib.Path,
150 staging_dir: pathlib.Path,
151 iscc_exe: pathlib.Path,
152 version,
153 arch=None,
154 suffix="",
155 ):
156 """Build an Inno installer from staged Mercurial files.
157
158 This function is agnostic about how to build Mercurial. It just
159 cares that Mercurial files are in ``staging_dir``.
160 """
161 inno_source_dir = source_dir / "contrib" / "packaging" / "inno"
162
107 # The final package layout is simply a mirror of the staging directory.
163 # The final package layout is simply a mirror of the staging directory.
108 package_files = []
164 package_files = []
109 for root, dirs, files in os.walk(staging_dir):
165 for root, dirs, files in os.walk(staging_dir):
@@ -158,8 +214,11 b' def build('
158
214
159 args = [str(iscc_exe)]
215 args = [str(iscc_exe)]
160
216
161 if vc_x64:
217 if arch:
162 args.append('/dARCH=x64')
218 args.append('/dARCH=%s' % arch)
219 args.append('/dSUFFIX=-%s%s' % (arch, suffix))
220 else:
221 args.append('/dSUFFIX=-x86%s' % suffix)
163
222
164 if not version:
223 if not version:
165 version = read_version_py(source_dir)
224 version = read_version_py(source_dir)
@@ -29,7 +29,59 b' def extract_zip_to_directory(source: pat'
29 zf.extractall(dest)
29 zf.extractall(dest)
30
30
31
31
32 def find_vc_runtime_files(x64=False):
32 def find_vc_runtime_dll(x64=False):
33 """Finds Visual C++ Runtime DLL to include in distribution."""
34 # We invoke vswhere to find the latest Visual Studio install.
35 vswhere = (
36 pathlib.Path(os.environ["ProgramFiles(x86)"])
37 / "Microsoft Visual Studio"
38 / "Installer"
39 / "vswhere.exe"
40 )
41
42 if not vswhere.exists():
43 raise Exception(
44 "could not find vswhere.exe: %s does not exist" % vswhere
45 )
46
47 args = [
48 str(vswhere),
49 # -products * is necessary to return results from Build Tools
50 # (as opposed to full IDE installs).
51 "-products",
52 "*",
53 "-requires",
54 "Microsoft.VisualCpp.Redist.14.Latest",
55 "-latest",
56 "-property",
57 "installationPath",
58 ]
59
60 vs_install_path = pathlib.Path(
61 os.fsdecode(subprocess.check_output(args).strip())
62 )
63
64 # This just gets us a path like
65 # C:\Program Files (x86)\Microsoft Visual Studio\2019\Community
66 # Actually vcruntime140.dll is under a path like:
67 # VC\Redist\MSVC\<version>\<arch>\Microsoft.VC14<X>.CRT\vcruntime140.dll.
68
69 arch = "x64" if x64 else "x86"
70
71 search_glob = (
72 r"%s\VC\Redist\MSVC\*\%s\Microsoft.VC14*.CRT\vcruntime140.dll"
73 % (vs_install_path, arch)
74 )
75
76 candidates = glob.glob(search_glob, recursive=True)
77
78 for candidate in reversed(candidates):
79 return pathlib.Path(candidate)
80
81 raise Exception("could not find vcruntime140.dll")
82
83
84 def find_legacy_vc_runtime_files(x64=False):
33 """Finds Visual C++ Runtime DLLs to include in distribution."""
85 """Finds Visual C++ Runtime DLLs to include in distribution."""
34 winsxs = pathlib.Path(os.environ['SYSTEMROOT']) / 'WinSxS'
86 winsxs = pathlib.Path(os.environ['SYSTEMROOT']) / 'WinSxS'
35
87
@@ -22,6 +22,7 b' from .py2exe import ('
22 build_py2exe,
22 build_py2exe,
23 stage_install,
23 stage_install,
24 )
24 )
25 from .pyoxidizer import run_pyoxidizer
25 from .util import (
26 from .util import (
26 extract_zip_to_directory,
27 extract_zip_to_directory,
27 normalize_windows_version,
28 normalize_windows_version,
@@ -121,30 +122,6 b' def run_candle(wix, cwd, wxs, source_dir'
121 subprocess.run(args, cwd=str(cwd), check=True)
122 subprocess.run(args, cwd=str(cwd), check=True)
122
123
123
124
124 def make_post_build_signing_fn(
125 name,
126 subject_name=None,
127 cert_path=None,
128 cert_password=None,
129 timestamp_url=None,
130 ):
131 """Create a callable that will use signtool to sign hg.exe."""
132
133 def post_build_sign(source_dir, build_dir, dist_dir, version):
134 description = '%s %s' % (name, version)
135
136 sign_with_signtool(
137 dist_dir / 'hg.exe',
138 description,
139 subject_name=subject_name,
140 cert_path=cert_path,
141 cert_password=cert_password,
142 timestamp_url=timestamp_url,
143 )
144
145 return post_build_sign
146
147
148 def make_files_xml(staging_dir: pathlib.Path, is_x64) -> str:
125 def make_files_xml(staging_dir: pathlib.Path, is_x64) -> str:
149 """Create XML string listing every file to be installed."""
126 """Create XML string listing every file to be installed."""
150
127
@@ -308,27 +285,23 b' def make_files_xml(staging_dir: pathlib.'
308 return doc.toprettyxml()
285 return doc.toprettyxml()
309
286
310
287
311 def build_installer(
288 def build_installer_py2exe(
312 source_dir: pathlib.Path,
289 source_dir: pathlib.Path,
313 python_exe: pathlib.Path,
290 python_exe: pathlib.Path,
314 msi_name='mercurial',
291 msi_name='mercurial',
315 version=None,
292 version=None,
316 post_build_fn=None,
317 extra_packages_script=None,
293 extra_packages_script=None,
318 extra_wxs: typing.Optional[typing.Dict[str, str]] = None,
294 extra_wxs: typing.Optional[typing.Dict[str, str]] = None,
319 extra_features: typing.Optional[typing.List[str]] = None,
295 extra_features: typing.Optional[typing.List[str]] = None,
296 signing_info: typing.Optional[typing.Dict[str, str]] = None,
320 ):
297 ):
321 """Build a WiX MSI installer.
298 """Build a WiX MSI installer using py2exe.
322
299
323 ``source_dir`` is the path to the Mercurial source tree to use.
300 ``source_dir`` is the path to the Mercurial source tree to use.
324 ``arch`` is the target architecture. either ``x86`` or ``x64``.
301 ``arch`` is the target architecture. either ``x86`` or ``x64``.
325 ``python_exe`` is the path to the Python executable to use/bundle.
302 ``python_exe`` is the path to the Python executable to use/bundle.
326 ``version`` is the Mercurial version string. If not defined,
303 ``version`` is the Mercurial version string. If not defined,
327 ``mercurial/__version__.py`` will be consulted.
304 ``mercurial/__version__.py`` will be consulted.
328 ``post_build_fn`` is a callable that will be called after building
329 Mercurial but before invoking WiX. It can be used to e.g. facilitate
330 signing. It is passed the paths to the Mercurial source, build, and
331 dist directories and the resolved Mercurial version.
332 ``extra_packages_script`` is a command to be run to inject extra packages
305 ``extra_packages_script`` is a command to be run to inject extra packages
333 into the py2exe binary. It should stage packages into the virtualenv and
306 into the py2exe binary. It should stage packages into the virtualenv and
334 print a null byte followed by a newline-separated list of packages that
307 print a null byte followed by a newline-separated list of packages that
@@ -340,8 +313,6 b' def build_installer('
340 arch = 'x64' if r'\x64' in os.environ.get('LIB', '') else 'x86'
313 arch = 'x64' if r'\x64' in os.environ.get('LIB', '') else 'x86'
341
314
342 hg_build_dir = source_dir / 'build'
315 hg_build_dir = source_dir / 'build'
343 dist_dir = source_dir / 'dist'
344 wix_dir = source_dir / 'contrib' / 'packaging' / 'wix'
345
316
346 requirements_txt = (
317 requirements_txt = (
347 source_dir / 'contrib' / 'packaging' / 'requirements_win32.txt'
318 source_dir / 'contrib' / 'packaging' / 'requirements_win32.txt'
@@ -357,15 +328,6 b' def build_installer('
357 extra_packages_script=extra_packages_script,
328 extra_packages_script=extra_packages_script,
358 )
329 )
359
330
360 orig_version = version or find_version(source_dir)
361 version = normalize_windows_version(orig_version)
362 print('using version string: %s' % version)
363 if version != orig_version:
364 print('(normalized from: %s)' % orig_version)
365
366 if post_build_fn:
367 post_build_fn(source_dir, hg_build_dir, dist_dir, version)
368
369 build_dir = hg_build_dir / ('wix-%s' % arch)
331 build_dir = hg_build_dir / ('wix-%s' % arch)
370 staging_dir = build_dir / 'stage'
332 staging_dir = build_dir / 'stage'
371
333
@@ -388,13 +350,112 b' def build_installer('
388 print('removing %s' % p)
350 print('removing %s' % p)
389 p.unlink()
351 p.unlink()
390
352
391 wix_pkg, wix_entry = download_entry('wix', hg_build_dir)
353 return run_wix_packaging(
392 wix_path = hg_build_dir / ('wix-%s' % wix_entry['version'])
354 source_dir,
355 build_dir,
356 staging_dir,
357 arch,
358 version=version,
359 python2=True,
360 msi_name=msi_name,
361 suffix="-python2",
362 extra_wxs=extra_wxs,
363 extra_features=extra_features,
364 signing_info=signing_info,
365 )
366
367
368 def build_installer_pyoxidizer(
369 source_dir: pathlib.Path,
370 target_triple: str,
371 msi_name='mercurial',
372 version=None,
373 extra_wxs: typing.Optional[typing.Dict[str, str]] = None,
374 extra_features: typing.Optional[typing.List[str]] = None,
375 signing_info: typing.Optional[typing.Dict[str, str]] = None,
376 ):
377 """Build a WiX MSI installer using PyOxidizer."""
378 hg_build_dir = source_dir / "build"
379 build_dir = hg_build_dir / ("wix-%s" % target_triple)
380 staging_dir = build_dir / "stage"
381
382 arch = "x64" if "x86_64" in target_triple else "x86"
383
384 build_dir.mkdir(parents=True, exist_ok=True)
385 run_pyoxidizer(source_dir, build_dir, staging_dir, target_triple)
386
387 # We also install some extra files.
388 process_install_rules(EXTRA_INSTALL_RULES, source_dir, staging_dir)
389
390 # And remove some files we don't want.
391 for f in STAGING_REMOVE_FILES:
392 p = staging_dir / f
393 if p.exists():
394 print('removing %s' % p)
395 p.unlink()
396
397 return run_wix_packaging(
398 source_dir,
399 build_dir,
400 staging_dir,
401 arch,
402 version,
403 python2=False,
404 msi_name=msi_name,
405 extra_wxs=extra_wxs,
406 extra_features=extra_features,
407 signing_info=signing_info,
408 )
409
410
411 def run_wix_packaging(
412 source_dir: pathlib.Path,
413 build_dir: pathlib.Path,
414 staging_dir: pathlib.Path,
415 arch: str,
416 version: str,
417 python2: bool,
418 msi_name: typing.Optional[str] = "mercurial",
419 suffix: str = "",
420 extra_wxs: typing.Optional[typing.Dict[str, str]] = None,
421 extra_features: typing.Optional[typing.List[str]] = None,
422 signing_info: typing.Optional[typing.Dict[str, str]] = None,
423 ):
424 """Invokes WiX to package up a built Mercurial.
425
426 ``signing_info`` is a dict defining properties to facilitate signing the
427 installer. Recognized keys include ``name``, ``subject_name``,
428 ``cert_path``, ``cert_password``, and ``timestamp_url``. If populated,
429 we will sign both the hg.exe and the .msi using the signing credentials
430 specified.
431 """
432
433 orig_version = version or find_version(source_dir)
434 version = normalize_windows_version(orig_version)
435 print('using version string: %s' % version)
436 if version != orig_version:
437 print('(normalized from: %s)' % orig_version)
438
439 if signing_info:
440 sign_with_signtool(
441 staging_dir / "hg.exe",
442 "%s %s" % (signing_info["name"], version),
443 subject_name=signing_info["subject_name"],
444 cert_path=signing_info["cert_path"],
445 cert_password=signing_info["cert_password"],
446 timestamp_url=signing_info["timestamp_url"],
447 )
448
449 wix_dir = source_dir / 'contrib' / 'packaging' / 'wix'
450
451 wix_pkg, wix_entry = download_entry('wix', build_dir)
452 wix_path = build_dir / ('wix-%s' % wix_entry['version'])
393
453
394 if not wix_path.exists():
454 if not wix_path.exists():
395 extract_zip_to_directory(wix_pkg, wix_path)
455 extract_zip_to_directory(wix_pkg, wix_path)
396
456
397 ensure_vc90_merge_modules(hg_build_dir)
457 if python2:
458 ensure_vc90_merge_modules(build_dir)
398
459
399 source_build_rel = pathlib.Path(os.path.relpath(source_dir, build_dir))
460 source_build_rel = pathlib.Path(os.path.relpath(source_dir, build_dir))
400
461
@@ -413,7 +474,16 b' def build_installer('
413 source = wix_dir / 'mercurial.wxs'
474 source = wix_dir / 'mercurial.wxs'
414 defines['Version'] = version
475 defines['Version'] = version
415 defines['Comments'] = 'Installs Mercurial version %s' % version
476 defines['Comments'] = 'Installs Mercurial version %s' % version
416 defines['VCRedistSrcDir'] = str(hg_build_dir)
477
478 if python2:
479 defines["PythonVersion"] = "2"
480 defines['VCRedistSrcDir'] = str(build_dir)
481 else:
482 defines["PythonVersion"] = "3"
483
484 if (staging_dir / "lib").exists():
485 defines["MercurialHasLib"] = "1"
486
417 if extra_features:
487 if extra_features:
418 assert all(';' not in f for f in extra_features)
488 assert all(';' not in f for f in extra_features)
419 defines['MercurialExtraFeatures'] = ';'.join(extra_features)
489 defines['MercurialExtraFeatures'] = ';'.join(extra_features)
@@ -421,7 +491,9 b' def build_installer('
421 run_candle(wix_path, build_dir, source, source_build_rel, defines=defines)
491 run_candle(wix_path, build_dir, source, source_build_rel, defines=defines)
422
492
423 msi_path = (
493 msi_path = (
424 source_dir / 'dist' / ('%s-%s-%s.msi' % (msi_name, orig_version, arch))
494 source_dir
495 / 'dist'
496 / ('%s-%s-%s%s.msi' % (msi_name, orig_version, arch, suffix))
425 )
497 )
426
498
427 args = [
499 args = [
@@ -448,52 +520,16 b' def build_installer('
448
520
449 print('%s created' % msi_path)
521 print('%s created' % msi_path)
450
522
523 if signing_info:
524 sign_with_signtool(
525 msi_path,
526 "%s %s" % (signing_info["name"], version),
527 subject_name=signing_info["subject_name"],
528 cert_path=signing_info["cert_path"],
529 cert_password=signing_info["cert_password"],
530 timestamp_url=signing_info["timestamp_url"],
531 )
532
451 return {
533 return {
452 'msi_path': msi_path,
534 'msi_path': msi_path,
453 }
535 }
454
455
456 def build_signed_installer(
457 source_dir: pathlib.Path,
458 python_exe: pathlib.Path,
459 name: str,
460 version=None,
461 subject_name=None,
462 cert_path=None,
463 cert_password=None,
464 timestamp_url=None,
465 extra_packages_script=None,
466 extra_wxs=None,
467 extra_features=None,
468 ):
469 """Build an installer with signed executables."""
470
471 post_build_fn = make_post_build_signing_fn(
472 name,
473 subject_name=subject_name,
474 cert_path=cert_path,
475 cert_password=cert_password,
476 timestamp_url=timestamp_url,
477 )
478
479 info = build_installer(
480 source_dir,
481 python_exe=python_exe,
482 msi_name=name.lower(),
483 version=version,
484 post_build_fn=post_build_fn,
485 extra_packages_script=extra_packages_script,
486 extra_wxs=extra_wxs,
487 extra_features=extra_features,
488 )
489
490 description = '%s %s' % (name, version)
491
492 sign_with_signtool(
493 info['msi_path'],
494 description,
495 subject_name=subject_name,
496 cert_path=cert_path,
497 cert_password=cert_password,
498 timestamp_url=timestamp_url,
499 )
@@ -9,14 +9,13 b''
9 AppCopyright=Copyright 2005-2020 Matt Mackall and others
9 AppCopyright=Copyright 2005-2020 Matt Mackall and others
10 AppName=Mercurial
10 AppName=Mercurial
11 AppVersion={#VERSION}
11 AppVersion={#VERSION}
12 OutputBaseFilename=Mercurial-{#VERSION}{#SUFFIX}
12 #if ARCH == "x64"
13 #if ARCH == "x64"
13 AppVerName=Mercurial {#VERSION} (64-bit)
14 AppVerName=Mercurial {#VERSION} (64-bit)
14 OutputBaseFilename=Mercurial-{#VERSION}-x64
15 ArchitecturesAllowed=x64
15 ArchitecturesAllowed=x64
16 ArchitecturesInstallIn64BitMode=x64
16 ArchitecturesInstallIn64BitMode=x64
17 #else
17 #else
18 AppVerName=Mercurial {#VERSION}
18 AppVerName=Mercurial {#VERSION}
19 OutputBaseFilename=Mercurial-{#VERSION}
20 #endif
19 #endif
21 InfoAfterFile=../postinstall.txt
20 InfoAfterFile=../postinstall.txt
22 LicenseFile=Copying.txt
21 LicenseFile=Copying.txt
@@ -79,16 +79,21 b''
79 </Directory>
79 </Directory>
80 </Directory>
80 </Directory>
81
81
82 <?if $(var.Platform) = "x86" ?>
82 <!-- Install VCRedist merge modules on Python 2. On Python 3,
83 <Merge Id='VCRuntime' DiskId='1' Language='1033'
83 vcruntimeXXX.dll is part of the install layout and gets picked up
84 SourceFile='$(var.VCRedistSrcDir)\microsoft.vcxx.crt.x86_msm.msm' />
84 as a regular file. -->
85 <Merge Id='VCRuntimePolicy' DiskId='1' Language='1033'
85 <?if $(var.PythonVersion) = "2" ?>
86 SourceFile='$(var.VCRedistSrcDir)\policy.x.xx.microsoft.vcxx.crt.x86_msm.msm' />
86 <?if $(var.Platform) = "x86" ?>
87 <?else?>
87 <Merge Id='VCRuntime' DiskId='1' Language='1033'
88 <Merge Id='VCRuntime' DiskId='1' Language='1033'
88 SourceFile='$(var.VCRedistSrcDir)\microsoft.vcxx.crt.x86_msm.msm' />
89 SourceFile='$(var.VCRedistSrcDir)\microsoft.vcxx.crt.x64_msm.msm' />
89 <Merge Id='VCRuntimePolicy' DiskId='1' Language='1033'
90 <Merge Id='VCRuntimePolicy' DiskId='1' Language='1033'
90 SourceFile='$(var.VCRedistSrcDir)\policy.x.xx.microsoft.vcxx.crt.x86_msm.msm' />
91 SourceFile='$(var.VCRedistSrcDir)\policy.x.xx.microsoft.vcxx.crt.x64_msm.msm' />
91 <?else?>
92 <Merge Id='VCRuntime' DiskId='1' Language='1033'
93 SourceFile='$(var.VCRedistSrcDir)\microsoft.vcxx.crt.x64_msm.msm' />
94 <Merge Id='VCRuntimePolicy' DiskId='1' Language='1033'
95 SourceFile='$(var.VCRedistSrcDir)\policy.x.xx.microsoft.vcxx.crt.x64_msm.msm' />
96 <?endif?>
92 <?endif?>
97 <?endif?>
93 </Directory>
98 </Directory>
94
99
@@ -101,10 +106,14 b''
101 <ComponentGroupRef Id="hg.group.ROOT" />
106 <ComponentGroupRef Id="hg.group.ROOT" />
102 <ComponentGroupRef Id="hg.group.defaultrc" />
107 <ComponentGroupRef Id="hg.group.defaultrc" />
103 <ComponentGroupRef Id="hg.group.helptext" />
108 <ComponentGroupRef Id="hg.group.helptext" />
104 <ComponentGroupRef Id="hg.group.lib" />
109 <?ifdef MercurialHasLib?>
110 <ComponentGroupRef Id="hg.group.lib" />
111 <?endif?>
105 <ComponentGroupRef Id="hg.group.templates" />
112 <ComponentGroupRef Id="hg.group.templates" />
106 <MergeRef Id='VCRuntime' />
113 <?if $(var.PythonVersion) = "2" ?>
107 <MergeRef Id='VCRuntimePolicy' />
114 <MergeRef Id='VCRuntime' />
115 <MergeRef Id='VCRuntimePolicy' />
116 <?endif?>
108 </Feature>
117 </Feature>
109 <?ifdef MercurialExtraFeatures?>
118 <?ifdef MercurialExtraFeatures?>
110 <?foreach EXTRAFEAT in $(var.MercurialExtraFeatures)?>
119 <?foreach EXTRAFEAT in $(var.MercurialExtraFeatures)?>
@@ -2558,7 +2558,7 b' def diff('
2558 fctx2 is not None
2558 fctx2 is not None
2559 ), b'fctx2 unexpectly None in diff hunks filtering'
2559 ), b'fctx2 unexpectly None in diff hunks filtering'
2560 hunks = hunksfilterfn(fctx2, hunks)
2560 hunks = hunksfilterfn(fctx2, hunks)
2561 text = b''.join(sum((list(hlines) for hrange, hlines in hunks), []))
2561 text = b''.join(b''.join(hlines) for hrange, hlines in hunks)
2562 if hdr and (text or len(hdr) > 1):
2562 if hdr and (text or len(hdr) > 1):
2563 yield b'\n'.join(hdr) + b'\n'
2563 yield b'\n'.join(hdr) + b'\n'
2564 if text:
2564 if text:
@@ -1,13 +1,24 b''
1 ROOT = CWD + "/../.."
1 ROOT = CWD + "/../.."
2
2
3 def make_exe():
3 # Code to run in Python interpreter.
4 dist = default_python_distribution()
4 RUN_CODE = "import hgdemandimport; hgdemandimport.enable(); from mercurial import dispatch; dispatch.run()"
5
6
7 set_build_path(ROOT + "/build/pyoxidizer")
8
5
9
6 code = "import hgdemandimport; hgdemandimport.enable(); from mercurial import dispatch; dispatch.run()"
10 def make_distribution():
11 return default_python_distribution()
12
7
13
14 def make_distribution_windows():
15 return default_python_distribution(flavor="standalone_dynamic")
16
17
18 def make_exe(dist):
8 config = PythonInterpreterConfig(
19 config = PythonInterpreterConfig(
9 raw_allocator = "system",
20 raw_allocator = "system",
10 run_eval = code,
21 run_eval = RUN_CODE,
11 # We want to let the user load extensions from the file system
22 # We want to let the user load extensions from the file system
12 filesystem_importer = True,
23 filesystem_importer = True,
13 # We need this to make resourceutil happy, since it looks for sys.frozen.
24 # We need this to make resourceutil happy, since it looks for sys.frozen.
@@ -24,30 +35,65 b' def make_exe():'
24 extension_module_filter = "all",
35 extension_module_filter = "all",
25 )
36 )
26
37
27 exe.add_python_resources(dist.pip_install([ROOT]))
38 # Add Mercurial to resources.
39 for resource in dist.pip_install(["--verbose", ROOT]):
40 # This is a bit wonky and worth explaining.
41 #
42 # Various parts of Mercurial don't yet support loading package
43 # resources via the ResourceReader interface. Or, not having
44 # file-based resources would be too inconvenient for users.
45 #
46 # So, for package resources, we package them both in the
47 # filesystem as well as in memory. If both are defined,
48 # PyOxidizer will prefer the in-memory location. So even
49 # if the filesystem file isn't packaged in the location
50 # specified here, we should never encounter an errors as the
51 # resource will always be available in memory.
52 if type(resource) == "PythonPackageResource":
53 exe.add_filesystem_relative_python_resource(".", resource)
54 exe.add_in_memory_python_resource(resource)
55 else:
56 exe.add_python_resource(resource)
57
58 # On Windows, we install extra packages for convenience.
59 if "windows" in BUILD_TARGET_TRIPLE:
60 exe.add_python_resources(
61 dist.pip_install(["-r", ROOT + "/contrib/packaging/requirements_win32.txt"])
62 )
28
63
29 return exe
64 return exe
30
65
31 def make_install(exe):
66
67 def make_manifest(dist, exe):
32 m = FileManifest()
68 m = FileManifest()
33
34 # `hg` goes in root directory.
35 m.add_python_resource(".", exe)
69 m.add_python_resource(".", exe)
36
70
37 templates = glob(
71 return m
38 include = [ROOT + "/mercurial/templates/**/*"],
39 strip_prefix = ROOT + "/mercurial/",
40 )
41 m.add_manifest(templates)
42
72
43 return m
44
73
45 def make_embedded_resources(exe):
74 def make_embedded_resources(exe):
46 return exe.to_embedded_resources()
75 return exe.to_embedded_resources()
47
76
48 register_target("exe", make_exe)
77
49 register_target("app", make_install, depends = ["exe"], default = True)
78 register_target("distribution_posix", make_distribution)
50 register_target("embedded", make_embedded_resources, depends = ["exe"], default_build_script = True)
79 register_target("distribution_windows", make_distribution_windows)
80
81 register_target("exe_posix", make_exe, depends = ["distribution_posix"])
82 register_target("exe_windows", make_exe, depends = ["distribution_windows"])
83
84 register_target(
85 "app_posix",
86 make_manifest,
87 depends = ["distribution_posix", "exe_posix"],
88 default = "windows" not in BUILD_TARGET_TRIPLE,
89 )
90 register_target(
91 "app_windows",
92 make_manifest,
93 depends = ["distribution_windows", "exe_windows"],
94 default = "windows" in BUILD_TARGET_TRIPLE,
95 )
96
51 resolve_targets()
97 resolve_targets()
52
98
53 # END OF COMMON USER-ADJUSTED SETTINGS.
99 # END OF COMMON USER-ADJUSTED SETTINGS.
@@ -55,5 +101,4 b' resolve_targets()'
55 # Everything below this is typically managed by PyOxidizer and doesn't need
101 # Everything below this is typically managed by PyOxidizer and doesn't need
56 # to be updated by people.
102 # to be updated by people.
57
103
58 PYOXIDIZER_VERSION = "0.7.0-pre"
104 PYOXIDIZER_VERSION = "0.7.0"
59 PYOXIDIZER_COMMIT = "c772a1379c3026314eda1c8ea244b86c0658951d"
@@ -27,6 +27,7 b' New errors are not allowed. Warnings are'
27 Skipping contrib/packaging/hgpackaging/downloads.py it has no-che?k-code (glob)
27 Skipping contrib/packaging/hgpackaging/downloads.py it has no-che?k-code (glob)
28 Skipping contrib/packaging/hgpackaging/inno.py it has no-che?k-code (glob)
28 Skipping contrib/packaging/hgpackaging/inno.py it has no-che?k-code (glob)
29 Skipping contrib/packaging/hgpackaging/py2exe.py it has no-che?k-code (glob)
29 Skipping contrib/packaging/hgpackaging/py2exe.py it has no-che?k-code (glob)
30 Skipping contrib/packaging/hgpackaging/pyoxidizer.py it has no-che?k-code (glob)
30 Skipping contrib/packaging/hgpackaging/util.py it has no-che?k-code (glob)
31 Skipping contrib/packaging/hgpackaging/util.py it has no-che?k-code (glob)
31 Skipping contrib/packaging/hgpackaging/wix.py it has no-che?k-code (glob)
32 Skipping contrib/packaging/hgpackaging/wix.py it has no-che?k-code (glob)
32 Skipping i18n/polib.py it has no-che?k-code (glob)
33 Skipping i18n/polib.py it has no-che?k-code (glob)
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now