automation: support building Python 3 Inno installers
The core packaging code now supports building Python 3 installers
using PyOxidizer. Let's teach the automation code to invoke it so
that we produce both Python 2 and Python 3 based exe installers.
When publishing the artifacts, the Python 3 versions are preferred
over the Python 2 versions given their higher weight (10 versus 9).
This may be a controversial change. But I think making Python 3
the default is warranted, as it is the future. The Python 2 installers
are still fully supported and can be installed should issues with
Python 3 arise.
Differential Revision: https://phab.mercurial-scm.org/D8483
--- a/contrib/automation/hgautomation/cli.py Fri Apr 24 11:48:07 2020 -0700
+++ b/contrib/automation/hgautomation/cli.py Fri Apr 24 12:11:08 2020 -0700
@@ -63,7 +63,13 @@
def build_inno(
- hga: HGAutomation, aws_region, arch, revision, version, base_image_name
+ hga: HGAutomation,
+ aws_region,
+ python_version,
+ arch,
+ revision,
+ version,
+ base_image_name,
):
c = hga.aws_connection(aws_region)
image = aws.ensure_windows_dev_ami(c, base_image_name=base_image_name)
@@ -74,10 +80,15 @@
windows.synchronize_hg(SOURCE_ROOT, revision, instance)
- for a in arch:
- windows.build_inno_installer(
- instance.winrm_client, a, DIST_PATH, version=version
- )
+ for py_version in python_version:
+ for a in arch:
+ windows.build_inno_installer(
+ instance.winrm_client,
+ py_version,
+ a,
+ DIST_PATH,
+ version=version,
+ )
def build_wix(
@@ -146,12 +157,15 @@
dest_path=DIST_PATH,
)
+ for py_version in (2, 3):
+ for arch in ('x86', 'x64'):
+ windows.purge_hg(winrm_client)
+ windows.build_inno_installer(
+ winrm_client, py_version, arch, DIST_PATH, version=version
+ )
+
for arch in ('x86', 'x64'):
windows.purge_hg(winrm_client)
- windows.build_inno_installer(
- winrm_client, arch, DIST_PATH, version=version
- )
- windows.purge_hg(winrm_client)
windows.build_wix_installer(
winrm_client, arch, DIST_PATH, version=version
)
@@ -309,6 +323,14 @@
'build-inno', help='Build Inno Setup installer(s)',
)
sp.add_argument(
+ '--python-version',
+ help='Which version of Python to target',
+ choices={2, 3},
+ type=int,
+ nargs='*',
+ default=[3],
+ )
+ sp.add_argument(
'--arch',
help='Architecture to build for',
choices={'x86', 'x64'},
--- a/contrib/automation/hgautomation/windows.py Fri Apr 24 11:48:07 2020 -0700
+++ b/contrib/automation/hgautomation/windows.py Fri Apr 24 12:11:08 2020 -0700
@@ -68,7 +68,17 @@
Write-Output "updated Mercurial working directory to {revision}"
'''.lstrip()
-BUILD_INNO = r'''
+BUILD_INNO_PYTHON3 = r'''
+$Env:RUSTUP_HOME = "C:\hgdev\rustup"
+$Env:CARGO_HOME = "C:\hgdev\cargo"
+Set-Location C:\hgdev\src
+C:\hgdev\python37-x64\python.exe contrib\packaging\packaging.py inno --pyoxidizer-target {pyoxidizer_target} --version {version}
+if ($LASTEXITCODE -ne 0) {{
+ throw "process exited non-0: $LASTEXITCODE"
+}}
+'''
+
+BUILD_INNO_PYTHON2 = r'''
Set-Location C:\hgdev\src
$python = "C:\hgdev\python27-{arch}\python.exe"
C:\hgdev\python37-x64\python.exe contrib\packaging\packaging.py inno --python $python {extra_args}
@@ -108,8 +118,10 @@
WHEEL_FILENAME_PYTHON38_X86 = 'mercurial-{version}-cp38-cp38-win32.whl'
WHEEL_FILENAME_PYTHON38_X64 = 'mercurial-{version}-cp38-cp38-win_amd64.whl'
-X86_EXE_FILENAME = 'Mercurial-{version}-x86-python2.exe'
-X64_EXE_FILENAME = 'Mercurial-{version}-x64-python2.exe'
+EXE_FILENAME_PYTHON2_X86 = 'Mercurial-{version}-x86-python2.exe'
+EXE_FILENAME_PYTHON2_X64 = 'Mercurial-{version}-x64-python2.exe'
+EXE_FILENAME_PYTHON3_X86 = 'Mercurial-{version}-x86.exe'
+EXE_FILENAME_PYTHON3_X64 = 'Mercurial-{version}-x64.exe'
X86_MSI_FILENAME = 'mercurial-{version}-x86-python2.msi'
X64_MSI_FILENAME = 'mercurial-{version}-x64-python2.msi'
@@ -118,12 +130,21 @@
X86_USER_AGENT_PATTERN = '.*Windows.*'
X64_USER_AGENT_PATTERN = '.*Windows.*(WOW|x)64.*'
-X86_EXE_DESCRIPTION = (
- 'Mercurial {version} Inno Setup installer - x86 Windows '
+EXE_PYTHON2_X86_DESCRIPTION = (
+ 'Mercurial {version} Inno Setup installer - x86 Windows (Python 2) '
+ '- does not require admin rights'
+)
+EXE_PYTHON2_X64_DESCRIPTION = (
+ 'Mercurial {version} Inno Setup installer - x64 Windows (Python 2) '
'- does not require admin rights'
)
-X64_EXE_DESCRIPTION = (
- 'Mercurial {version} Inno Setup installer - x64 Windows '
+# TODO remove Python version once Python 2 is dropped.
+EXE_PYTHON3_X86_DESCRIPTION = (
+ 'Mercurial {version} Inno Setup installer - x86 Windows (Python 3) '
+ '- does not require admin rights'
+)
+EXE_PYTHON3_X64_DESCRIPTION = (
+ 'Mercurial {version} Inno Setup installer - x64 Windows (Python 3) '
'- does not require admin rights'
)
X86_MSI_DESCRIPTION = (
@@ -285,22 +306,48 @@
def build_inno_installer(
- winrm_client, arch: str, dest_path: pathlib.Path, version=None
+ winrm_client,
+ python_version: int,
+ arch: str,
+ dest_path: pathlib.Path,
+ version=None,
):
"""Build the Inno Setup installer on a remote machine.
Using a WinRM client, remote commands are executed to build
a Mercurial Inno Setup installer.
"""
- print('building Inno Setup installer for %s' % arch)
+ print(
+ 'building Inno Setup installer for Python %d %s'
+ % (python_version, arch)
+ )
+
+ if python_version == 3:
+ # TODO fix this limitation in packaging code
+ if not version:
+ raise Exception(
+ "version string is required when building for Python 3"
+ )
- extra_args = []
- if version:
- extra_args.extend(['--version', version])
+ if arch == "x86":
+ target_triple = "i686-pc-windows-msvc"
+ elif arch == "x64":
+ target_triple = "x86_64-pc-windows-msvc"
+ else:
+ raise Exception("unhandled arch: %s" % arch)
- ps = get_vc_prefix(arch) + BUILD_INNO.format(
- arch=arch, extra_args=' '.join(extra_args)
- )
+ ps = BUILD_INNO_PYTHON3.format(
+ pyoxidizer_target=target_triple, version=version,
+ )
+ else:
+ extra_args = []
+ if version:
+ extra_args.extend(['--version', version])
+
+ ps = get_vc_prefix(arch) + BUILD_INNO_PYTHON2.format(
+ arch=arch, extra_args=' '.join(extra_args)
+ )
+
run_powershell(winrm_client, ps)
copy_latest_dist(winrm_client, '*.exe', dest_path)
@@ -388,16 +435,20 @@
dist_path / WHEEL_FILENAME_PYTHON37_X64.format(version=version),
dist_path / WHEEL_FILENAME_PYTHON38_X86.format(version=version),
dist_path / WHEEL_FILENAME_PYTHON38_X64.format(version=version),
- dist_path / X86_EXE_FILENAME.format(version=version),
- dist_path / X64_EXE_FILENAME.format(version=version),
+ dist_path / EXE_FILENAME_PYTHON2_X86.format(version=version),
+ dist_path / EXE_FILENAME_PYTHON2_X64.format(version=version),
+ dist_path / EXE_FILENAME_PYTHON3_X86.format(version=version),
+ dist_path / EXE_FILENAME_PYTHON3_X64.format(version=version),
dist_path / X86_MSI_FILENAME.format(version=version),
dist_path / X64_MSI_FILENAME.format(version=version),
)
def generate_latest_dat(version: str):
- x86_exe_filename = X86_EXE_FILENAME.format(version=version)
- x64_exe_filename = X64_EXE_FILENAME.format(version=version)
+ python2_x86_exe_filename = EXE_FILENAME_PYTHON2_X86.format(version=version)
+ python2_x64_exe_filename = EXE_FILENAME_PYTHON2_X64.format(version=version)
+ python3_x86_exe_filename = EXE_FILENAME_PYTHON3_X86.format(version=version)
+ python3_x64_exe_filename = EXE_FILENAME_PYTHON3_X64.format(version=version)
x86_msi_filename = X86_MSI_FILENAME.format(version=version)
x64_msi_filename = X64_MSI_FILENAME.format(version=version)
@@ -406,15 +457,29 @@
'10',
version,
X86_USER_AGENT_PATTERN,
- '%s/%s' % (MERCURIAL_SCM_BASE_URL, x86_exe_filename),
- X86_EXE_DESCRIPTION.format(version=version),
+ '%s/%s' % (MERCURIAL_SCM_BASE_URL, python3_x86_exe_filename),
+ EXE_PYTHON3_X86_DESCRIPTION.format(version=version),
),
(
'10',
version,
X64_USER_AGENT_PATTERN,
- '%s/%s' % (MERCURIAL_SCM_BASE_URL, x64_exe_filename),
- X64_EXE_DESCRIPTION.format(version=version),
+ '%s/%s' % (MERCURIAL_SCM_BASE_URL, python3_x64_exe_filename),
+ EXE_PYTHON3_X64_DESCRIPTION.format(version=version),
+ ),
+ (
+ '9',
+ version,
+ X86_USER_AGENT_PATTERN,
+ '%s/%s' % (MERCURIAL_SCM_BASE_URL, python2_x86_exe_filename),
+ EXE_PYTHON2_X86_DESCRIPTION.format(version=version),
+ ),
+ (
+ '9',
+ version,
+ X64_USER_AGENT_PATTERN,
+ '%s/%s' % (MERCURIAL_SCM_BASE_URL, python2_x64_exe_filename),
+ EXE_PYTHON2_X64_DESCRIPTION.format(version=version),
),
(
'10',