# HG changeset patch # User Gregory Szorc # Date 2020-04-24 19:11:08 # Node ID 802ee93c205d5666d6c57ec9132597fb7a6be550 # Parent 47609da1537959a18249e022416423b5569ce444 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 diff --git a/contrib/automation/hgautomation/cli.py b/contrib/automation/hgautomation/cli.py --- a/contrib/automation/hgautomation/cli.py +++ b/contrib/automation/hgautomation/cli.py @@ -63,7 +63,13 @@ def bootstrap_windows_dev(hga: HGAutomat 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 @@ def build_inno( 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 @@ def build_all_windows_packages( 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 @@ def get_parser(): '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'}, diff --git a/contrib/automation/hgautomation/windows.py b/contrib/automation/hgautomation/windows.py --- a/contrib/automation/hgautomation/windows.py +++ b/contrib/automation/hgautomation/windows.py @@ -68,7 +68,17 @@ hg.exe log -r . 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_PYTHON37_X64 = 'mercurial 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 @@ MERCURIAL_SCM_BASE_URL = 'https://mercur 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 copy_latest_dist(winrm_client, patte 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 @@ def resolve_all_artifacts(dist_path: pat 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 @@ def generate_latest_dat(version: str): '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',