# HG changeset patch # User Gregory Szorc # Date 2020-04-24 00:24:37 # Node ID 48096e264d6cccf149c74d4ef4d07677ec8257d6 # Parent c9517d9d3ae705c5f6efb48b79d6038d532611d4 automation: support building Windows wheels for Python 3.7 and 3.8 The time has come to support Python 3 on Windows. Let's teach our automation code to produce Windows wheels for Python 3.7 and 3.8. We could theoretically support 3.5 and 3.6. But I don't think it is worth it. People on Windows generally use the Mercurial installers, not wheels. And I'd prefer we limit variability and not have to worry about supporting earlier Python versions if it can be helped. As part of this, we change the invocation of pip to `python.exe -m pip`, as this is what is being recommended in Python docs these days. And it seemed to be required to avoid a weird build error. Why, I'm not sure. But it looks like pip was having trouble finding a Visual Studio files when invoked as `pip.exe` but not when using `python.exe -m pip`. Who knows. Differential Revision: https://phab.mercurial-scm.org/D8478 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 @@ -99,7 +99,12 @@ def build_wix( def build_windows_wheel( - hga: HGAutomation, aws_region, arch, revision, base_image_name + hga: HGAutomation, + aws_region, + python_version, + arch, + revision, + base_image_name, ): c = hga.aws_connection(aws_region) image = aws.ensure_windows_dev_ami(c, base_image_name=base_image_name) @@ -110,8 +115,11 @@ def build_windows_wheel( windows.synchronize_hg(SOURCE_ROOT, revision, instance) - for a in arch: - windows.build_wheel(instance.winrm_client, a, DIST_PATH) + for py_version in python_version: + for a in arch: + windows.build_wheel( + instance.winrm_client, py_version, a, DIST_PATH + ) def build_all_windows_packages( @@ -128,10 +136,18 @@ def build_all_windows_packages( windows.synchronize_hg(SOURCE_ROOT, revision, instance) + for py_version in ("2.7", "3.7", "3.8"): + for arch in ("x86", "x64"): + windows.purge_hg(winrm_client) + windows.build_wheel( + winrm_client, + python_version=py_version, + arch=arch, + dest_path=DIST_PATH, + ) + for arch in ('x86', 'x64'): windows.purge_hg(winrm_client) - windows.build_wheel(winrm_client, arch, DIST_PATH) - windows.purge_hg(winrm_client) windows.build_inno_installer( winrm_client, arch, DIST_PATH, version=version ) @@ -316,6 +332,13 @@ def get_parser(): 'build-windows-wheel', help='Build Windows wheel(s)', ) sp.add_argument( + '--python-version', + help='Python version to build for', + choices={'2.7', '3.7', '3.8'}, + nargs='*', + default=['3.8'], + ) + 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 @@ -79,7 +79,7 @@ if ($LASTEXITCODE -ne 0) {{ BUILD_WHEEL = r''' Set-Location C:\hgdev\src -C:\hgdev\python27-{arch}\Scripts\pip.exe wheel --wheel-dir dist . +C:\hgdev\python{python_version}-{arch}\python.exe -m pip wheel --wheel-dir dist . if ($LASTEXITCODE -ne 0) {{ throw "process exited non-0: $LASTEXITCODE" }} @@ -101,8 +101,13 @@ if ($LASTEXITCODE -ne 0) {{ }} ''' -X86_WHEEL_FILENAME = 'mercurial-{version}-cp27-cp27m-win32.whl' -X64_WHEEL_FILENAME = 'mercurial-{version}-cp27-cp27m-win_amd64.whl' +WHEEL_FILENAME_PYTHON27_X86 = 'mercurial-{version}-cp27-cp27m-win32.whl' +WHEEL_FILENAME_PYTHON27_X64 = 'mercurial-{version}-cp27-cp27m-win_amd64.whl' +WHEEL_FILENAME_PYTHON37_X86 = 'mercurial-{version}-cp37-cp37m-win32.whl' +WHEEL_FILENAME_PYTHON37_X64 = 'mercurial-{version}-cp37-cp37m-win_amd64.whl' +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}.exe' X64_EXE_FILENAME = 'Mercurial-{version}-x64.exe' X86_MSI_FILENAME = 'mercurial-{version}-x86.msi' @@ -300,14 +305,24 @@ def build_inno_installer( copy_latest_dist(winrm_client, '*.exe', dest_path) -def build_wheel(winrm_client, arch: str, dest_path: pathlib.Path): +def build_wheel( + winrm_client, python_version: str, arch: str, dest_path: pathlib.Path +): """Build Python wheels on a remote machine. Using a WinRM client, remote commands are executed to build a Python wheel for Mercurial. """ - print('Building Windows wheel for %s' % arch) - ps = get_vc_prefix(arch) + BUILD_WHEEL.format(arch=arch) + print('Building Windows wheel for Python %s %s' % (python_version, arch)) + + ps = BUILD_WHEEL.format( + python_version=python_version.replace(".", ""), arch=arch + ) + + # Python 2.7 requires an activated environment. + if python_version == "2.7": + ps = get_vc_prefix(arch) + ps + run_powershell(winrm_client, ps) copy_latest_dist(winrm_client, '*.whl', dest_path) @@ -356,15 +371,23 @@ def run_tests(winrm_client, python_versi def resolve_wheel_artifacts(dist_path: pathlib.Path, version: str): return ( - dist_path / X86_WHEEL_FILENAME.format(version=version), - dist_path / X64_WHEEL_FILENAME.format(version=version), + dist_path / WHEEL_FILENAME_PYTHON27_X86.format(version=version), + dist_path / WHEEL_FILENAME_PYTHON27_X64.format(version=version), + dist_path / WHEEL_FILENAME_PYTHON37_X86.format(version=version), + 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), ) def resolve_all_artifacts(dist_path: pathlib.Path, version: str): return ( - dist_path / X86_WHEEL_FILENAME.format(version=version), - dist_path / X64_WHEEL_FILENAME.format(version=version), + dist_path / WHEEL_FILENAME_PYTHON27_X86.format(version=version), + dist_path / WHEEL_FILENAME_PYTHON27_X64.format(version=version), + dist_path / WHEEL_FILENAME_PYTHON37_X86.format(version=version), + 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 / X86_MSI_FILENAME.format(version=version),