--- a/contrib/automation/hgautomation/cli.py Thu Mar 05 17:55:05 2020 +0100
+++ b/contrib/automation/hgautomation/cli.py Fri May 01 08:07:25 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,14 +80,25 @@
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(
- 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)
@@ -92,14 +109,24 @@
windows.synchronize_hg(SOURCE_ROOT, revision, instance)
- for a in arch:
- windows.build_wix_installer(
- instance.winrm_client, a, DIST_PATH, version=version
- )
+ for py_version in python_version:
+ for a in arch:
+ windows.build_wix_installer(
+ instance.winrm_client,
+ py_version,
+ a,
+ DIST_PATH,
+ version=version,
+ )
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 +137,11 @@
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,17 +158,25 @@
windows.synchronize_hg(SOURCE_ROOT, revision, instance)
- 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
- )
- windows.purge_hg(winrm_client)
- windows.build_wix_installer(
- winrm_client, arch, DIST_PATH, version=version
- )
+ 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 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
+ )
+ windows.build_wix_installer(
+ winrm_client, py_version, arch, DIST_PATH, version=version
+ )
def terminate_ec2_instances(hga: HGAutomation, aws_region):
@@ -293,6 +331,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'},
@@ -316,6 +362,13 @@
'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'},
@@ -334,6 +387,14 @@
sp = subparsers.add_parser('build-wix', help='Build WiX 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/linux.py Thu Mar 05 17:55:05 2020 +0100
+++ b/contrib/automation/hgautomation/linux.py Fri May 01 08:07:25 2020 -0700
@@ -72,8 +72,10 @@
chmod +x rustup-init
sudo -H -u hg -g hg ./rustup-init -y
-sudo -H -u hg -g hg /home/hg/.cargo/bin/rustup install 1.31.1 1.34.2
+sudo -H -u hg -g hg /home/hg/.cargo/bin/rustup install 1.31.1 1.42.0
sudo -H -u hg -g hg /home/hg/.cargo/bin/rustup component add clippy
+
+sudo -H -u hg -g hg /home/hg/.cargo/bin/cargo install --version 0.7.0 pyoxidizer
'''
--- a/contrib/automation/hgautomation/windows.py Thu Mar 05 17:55:05 2020 +0100
+++ b/contrib/automation/hgautomation/windows.py Fri May 01 08:07:25 2020 -0700
@@ -68,10 +68,20 @@
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
+C:\hgdev\python37-x64\python.exe contrib\packaging\packaging.py inno --python $python {extra_args}
if ($LASTEXITCODE -ne 0) {{
throw "process exited non-0: $LASTEXITCODE"
}}
@@ -79,13 +89,23 @@
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"
}}
'''
-BUILD_WIX = r'''
+BUILD_WIX_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 wix --pyoxidizer-target {pyoxidizer_target} --version {version}
+if ($LASTEXITCODE -ne 0) {{
+ throw "process exited non-0: $LASTEXITCODE"
+}}
+'''
+
+BUILD_WIX_PYTHON2 = r'''
Set-Location C:\hgdev\src
$python = "C:\hgdev\python27-{arch}\python.exe"
C:\hgdev\python37-x64\python.exe contrib\packaging\packaging.py wix --python $python {extra_args}
@@ -101,31 +121,60 @@
}}
'''
-X86_WHEEL_FILENAME = 'mercurial-{version}-cp27-cp27m-win32.whl'
-X64_WHEEL_FILENAME = 'mercurial-{version}-cp27-cp27m-win_amd64.whl'
-X86_EXE_FILENAME = 'Mercurial-{version}.exe'
-X64_EXE_FILENAME = 'Mercurial-{version}-x64.exe'
-X86_MSI_FILENAME = 'mercurial-{version}-x86.msi'
-X64_MSI_FILENAME = 'mercurial-{version}-x64.msi'
+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'
+
+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'
+
+MSI_FILENAME_PYTHON2_X86 = 'mercurial-{version}-x86-python2.msi'
+MSI_FILENAME_PYTHON2_X64 = 'mercurial-{version}-x64-python2.msi'
+MSI_FILENAME_PYTHON3_X86 = 'mercurial-{version}-x86.msi'
+MSI_FILENAME_PYTHON3_X64 = 'mercurial-{version}-x64.msi'
MERCURIAL_SCM_BASE_URL = 'https://mercurial-scm.org/release/windows'
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'
+)
+# 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'
)
-X64_EXE_DESCRIPTION = (
- 'Mercurial {version} Inno Setup installer - x64 Windows '
+EXE_PYTHON3_X64_DESCRIPTION = (
+ 'Mercurial {version} Inno Setup installer - x64 Windows (Python 3) '
'- does not require admin rights'
)
-X86_MSI_DESCRIPTION = (
- 'Mercurial {version} MSI installer - x86 Windows ' '- requires admin rights'
+MSI_PYTHON2_X86_DESCRIPTION = (
+ 'Mercurial {version} MSI installer - x86 Windows (Python 2) '
+ '- requires admin rights'
+)
+MSI_PYTHON2_X64_DESCRIPTION = (
+ 'Mercurial {version} MSI installer - x64 Windows (Python 2) '
+ '- requires admin rights'
)
-X64_MSI_DESCRIPTION = (
- 'Mercurial {version} MSI installer - x64 Windows ' '- requires admin rights'
+MSI_PYTHON3_X86_DESCRIPTION = (
+ 'Mercurial {version} MSI installer - x86 Windows (Python 3) '
+ '- requires admin rights'
+)
+MSI_PYTHON3_X64_DESCRIPTION = (
+ 'Mercurial {version} MSI installer - x64 Windows (Python 3) '
+ '- requires admin rights'
)
@@ -280,53 +329,113 @@
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)
-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)
def build_wix_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 WiX installer on a remote machine.
Using a WinRM client, remote commands are executed to build a WiX installer.
"""
- print('Building WiX installer for %s' % arch)
- extra_args = []
- if version:
- extra_args.extend(['--version', version])
+ print('Building WiX 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"
+ )
- ps = get_vc_prefix(arch) + BUILD_WIX.format(
- arch=arch, extra_args=' '.join(extra_args)
- )
+ 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 = BUILD_WIX_PYTHON3.format(
+ pyoxidizer_target=target_triple, version=version,
+ )
+ else:
+ extra_args = []
+ if version:
+ extra_args.extend(['--version', version])
+
+ ps = get_vc_prefix(arch) + BUILD_WIX_PYTHON2.format(
+ arch=arch, extra_args=' '.join(extra_args)
+ )
+
run_powershell(winrm_client, ps)
copy_latest_dist(winrm_client, '*.msi', dest_path)
@@ -356,56 +465,100 @@
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 / X86_EXE_FILENAME.format(version=version),
- dist_path / X64_EXE_FILENAME.format(version=version),
- dist_path / X86_MSI_FILENAME.format(version=version),
- dist_path / X64_MSI_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 / 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 / MSI_FILENAME_PYTHON2_X86.format(version=version),
+ dist_path / MSI_FILENAME_PYTHON2_X64.format(version=version),
+ dist_path / MSI_FILENAME_PYTHON3_X86.format(version=version),
+ dist_path / MSI_FILENAME_PYTHON3_X64.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)
- x86_msi_filename = X86_MSI_FILENAME.format(version=version)
- x64_msi_filename = X64_MSI_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)
+ python2_x86_msi_filename = MSI_FILENAME_PYTHON2_X86.format(version=version)
+ python2_x64_msi_filename = MSI_FILENAME_PYTHON2_X64.format(version=version)
+ python3_x86_msi_filename = MSI_FILENAME_PYTHON3_X86.format(version=version)
+ python3_x64_msi_filename = MSI_FILENAME_PYTHON3_X64.format(version=version)
entries = (
(
'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',
version,
X86_USER_AGENT_PATTERN,
- '%s/%s' % (MERCURIAL_SCM_BASE_URL, x86_msi_filename),
- X86_MSI_DESCRIPTION.format(version=version),
+ '%s/%s' % (MERCURIAL_SCM_BASE_URL, python3_x86_msi_filename),
+ MSI_PYTHON3_X86_DESCRIPTION.format(version=version),
),
(
'10',
version,
X64_USER_AGENT_PATTERN,
- '%s/%s' % (MERCURIAL_SCM_BASE_URL, x64_msi_filename),
- X64_MSI_DESCRIPTION.format(version=version),
+ '%s/%s' % (MERCURIAL_SCM_BASE_URL, python3_x64_msi_filename),
+ MSI_PYTHON3_X64_DESCRIPTION.format(version=version),
+ ),
+ (
+ '9',
+ version,
+ X86_USER_AGENT_PATTERN,
+ '%s/%s' % (MERCURIAL_SCM_BASE_URL, python2_x86_msi_filename),
+ MSI_PYTHON2_X86_DESCRIPTION.format(version=version),
+ ),
+ (
+ '9',
+ version,
+ X64_USER_AGENT_PATTERN,
+ '%s/%s' % (MERCURIAL_SCM_BASE_URL, python2_x64_msi_filename),
+ MSI_PYTHON2_X64_DESCRIPTION.format(version=version),
),
)
--- a/contrib/install-windows-dependencies.ps1 Thu Mar 05 17:55:05 2020 +0100
+++ b/contrib/install-windows-dependencies.ps1 Fri May 01 08:07:25 2020 -0700
@@ -64,6 +64,9 @@
$MERCURIAL_WHEEL_URL = "https://files.pythonhosted.org/packages/6d/47/e031e47f7fe9b16e4e3383da47e2b0a7eae6e603996bc67a03ec4fa1b3f4/$MERCURIAL_WHEEL_FILENAME"
$MERCURIAL_WHEEL_SHA256 = "1d18c7f6ca1456f0f62ee65c9a50c14cbba48ce6e924930cdb10537f5c9eaf5f"
+$RUSTUP_INIT_URL = "https://static.rust-lang.org/rustup/archive/1.21.1/x86_64-pc-windows-gnu/rustup-init.exe"
+$RUSTUP_INIT_SHA256 = "d17df34ba974b9b19cf5c75883a95475aa22ddc364591d75d174090d55711c72"
+
# Writing progress slows down downloads substantially. So disable it.
$progressPreference = 'silentlyContinue'
@@ -116,6 +119,20 @@
Invoke-Process ${dest}\python.exe $pip
}
+function Install-Rust($prefix) {
+ Write-Output "installing Rust"
+ $Env:RUSTUP_HOME = "${prefix}\rustup"
+ $Env:CARGO_HOME = "${prefix}\cargo"
+
+ Invoke-Process "${prefix}\assets\rustup-init.exe" "-y --default-host x86_64-pc-windows-msvc"
+ Invoke-Process "${prefix}\cargo\bin\rustup.exe" "target add i686-pc-windows-msvc"
+ Invoke-Process "${prefix}\cargo\bin\rustup.exe" "install 1.42.0"
+ Invoke-Process "${prefix}\cargo\bin\rustup.exe" "component add clippy"
+
+ # Install PyOxidizer for packaging.
+ Invoke-Process "${prefix}\cargo\bin\cargo.exe" "install --version 0.7.0 pyoxidizer"
+}
+
function Install-Dependencies($prefix) {
if (!(Test-Path -Path $prefix\assets)) {
New-Item -Path $prefix\assets -ItemType Directory
@@ -140,6 +157,7 @@
Secure-Download $INNO_SETUP_URL ${prefix}\assets\InnoSetup.exe $INNO_SETUP_SHA256
Secure-Download $MINGW_BIN_URL ${prefix}\assets\mingw-get-bin.zip $MINGW_BIN_SHA256
Secure-Download $MERCURIAL_WHEEL_URL ${prefix}\assets\${MERCURIAL_WHEEL_FILENAME} $MERCURIAL_WHEEL_SHA256
+ Secure-Download $RUSTUP_INIT_URL ${prefix}\assets\rustup-init.exe $RUSTUP_INIT_SHA256
Write-Output "installing Python 2.7 32-bit"
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 @@
Write-Output "installing Visual Studio 2017 Build Tools and SDKs"
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"
+ Install-Rust ${prefix}
+
Write-Output "installing Visual C++ 9.0 for Python 2.7"
Invoke-Process msiexec.exe "/i ${prefix}\assets\VCForPython27.msi /l* ${prefix}\assets\VCForPython27.log /q"
--- a/contrib/packaging/hgpackaging/cli.py Thu Mar 05 17:55:05 2020 +0100
+++ b/contrib/packaging/hgpackaging/cli.py Fri May 01 08:07:25 2020 -0700
@@ -20,8 +20,11 @@
SOURCE_DIR = HERE.parent.parent.parent
-def build_inno(python=None, iscc=None, version=None):
- if not os.path.isabs(python):
+def build_inno(pyoxidizer_target=None, python=None, iscc=None, version=None):
+ if not pyoxidizer_target and not python:
+ raise Exception("--python required unless building with PyOxidizer")
+
+ if python and not os.path.isabs(python):
raise Exception("--python arg must be an absolute path")
if iscc:
@@ -35,13 +38,19 @@
build_dir = SOURCE_DIR / "build"
- inno.build(
- SOURCE_DIR, build_dir, pathlib.Path(python), iscc, version=version,
- )
+ if pyoxidizer_target:
+ inno.build_with_pyoxidizer(
+ SOURCE_DIR, build_dir, pyoxidizer_target, iscc, version=version
+ )
+ else:
+ inno.build_with_py2exe(
+ SOURCE_DIR, build_dir, pathlib.Path(python), iscc, version=version,
+ )
def build_wix(
name=None,
+ pyoxidizer_target=None,
python=None,
version=None,
sign_sn=None,
@@ -52,17 +61,29 @@
extra_wxs=None,
extra_features=None,
):
- fn = wix.build_installer
+ if not pyoxidizer_target and not python:
+ raise Exception("--python required unless building with PyOxidizer")
+
+ if python and not os.path.isabs(python):
+ raise Exception("--python arg must be an absolute path")
+
kwargs = {
"source_dir": SOURCE_DIR,
- "python_exe": pathlib.Path(python),
"version": version,
}
- if not os.path.isabs(python):
- raise Exception("--python arg must be an absolute path")
+ if pyoxidizer_target:
+ fn = wix.build_installer_pyoxidizer
+ kwargs["target_triple"] = pyoxidizer_target
+ else:
+ fn = wix.build_installer_py2exe
+ kwargs["python_exe"] = pathlib.Path(python)
if extra_packages_script:
+ if pyoxidizer_target:
+ raise Exception(
+ "pyoxidizer does not support --extra-packages-script"
+ )
kwargs["extra_packages_script"] = extra_packages_script
if extra_wxs:
kwargs["extra_wxs"] = dict(
@@ -72,12 +93,13 @@
kwargs["extra_features"] = extra_features.split(",")
if sign_sn or sign_cert:
- fn = wix.build_signed_installer
- kwargs["name"] = name
- kwargs["subject_name"] = sign_sn
- kwargs["cert_path"] = sign_cert
- kwargs["cert_password"] = sign_password
- kwargs["timestamp_url"] = sign_timestamp_url
+ kwargs["signing_info"] = {
+ "name": name,
+ "subject_name": sign_sn,
+ "cert_path": sign_cert,
+ "cert_password": sign_password,
+ "timestamp_url": sign_timestamp_url,
+ }
fn(**kwargs)
@@ -88,7 +110,12 @@
subparsers = parser.add_subparsers()
sp = subparsers.add_parser("inno", help="Build Inno Setup installer")
- sp.add_argument("--python", required=True, help="path to python.exe to use")
+ sp.add_argument(
+ "--pyoxidizer-target",
+ choices={"i686-pc-windows-msvc", "x86_64-pc-windows-msvc"},
+ help="Build with PyOxidizer targeting this host triple",
+ )
+ sp.add_argument("--python", help="path to python.exe to use")
sp.add_argument("--iscc", help="path to iscc.exe to use")
sp.add_argument(
"--version",
@@ -102,8 +129,11 @@
)
sp.add_argument("--name", help="Application name", default="Mercurial")
sp.add_argument(
- "--python", help="Path to Python executable to use", required=True
+ "--pyoxidizer-target",
+ choices={"i686-pc-windows-msvc", "x86_64-pc-windows-msvc"},
+ help="Build with PyOxidizer targeting this host triple",
)
+ sp.add_argument("--python", help="Path to Python executable to use")
sp.add_argument(
"--sign-sn",
help="Subject name (or fragment thereof) of certificate "
--- a/contrib/packaging/hgpackaging/inno.py Thu Mar 05 17:55:05 2020 +0100
+++ b/contrib/packaging/hgpackaging/inno.py Fri May 01 08:07:25 2020 -0700
@@ -18,8 +18,9 @@
build_py2exe,
stage_install,
)
+from .pyoxidizer import run_pyoxidizer
from .util import (
- find_vc_runtime_files,
+ find_legacy_vc_runtime_files,
normalize_windows_version,
process_install_rules,
read_version_py,
@@ -41,14 +42,14 @@
}
-def build(
+def build_with_py2exe(
source_dir: pathlib.Path,
build_dir: pathlib.Path,
python_exe: pathlib.Path,
iscc_exe: pathlib.Path,
version=None,
):
- """Build the Inno installer.
+ """Build the Inno installer using py2exe.
Build files will be placed in ``build_dir``.
@@ -61,8 +62,7 @@
vc_x64 = r'\x64' in os.environ.get('LIB', '')
arch = 'x64' if vc_x64 else 'x86'
- inno_source_dir = source_dir / 'contrib' / 'packaging' / 'inno'
- inno_build_dir = build_dir / ('inno-%s' % arch)
+ inno_build_dir = build_dir / ('inno-py2exe-%s' % arch)
staging_dir = inno_build_dir / 'stage'
requirements_txt = (
@@ -93,7 +93,7 @@
process_install_rules(EXTRA_INSTALL_RULES, source_dir, staging_dir)
# hg.exe depends on VC9 runtime DLLs. Copy those into place.
- for f in find_vc_runtime_files(vc_x64):
+ for f in find_legacy_vc_runtime_files(vc_x64):
if f.name.endswith('.manifest'):
basename = 'Microsoft.VC90.CRT.manifest'
else:
@@ -104,6 +104,62 @@
print('copying %s to %s' % (f, dest_path))
shutil.copyfile(f, dest_path)
+ build_installer(
+ source_dir,
+ inno_build_dir,
+ staging_dir,
+ iscc_exe,
+ version,
+ arch="x64" if vc_x64 else None,
+ suffix="-python2",
+ )
+
+
+def build_with_pyoxidizer(
+ source_dir: pathlib.Path,
+ build_dir: pathlib.Path,
+ target_triple: str,
+ iscc_exe: pathlib.Path,
+ version=None,
+):
+ """Build the Inno installer using PyOxidizer."""
+ if not iscc_exe.exists():
+ raise Exception("%s does not exist" % iscc_exe)
+
+ inno_build_dir = build_dir / ("inno-pyoxidizer-%s" % target_triple)
+ staging_dir = inno_build_dir / "stage"
+
+ inno_build_dir.mkdir(parents=True, exist_ok=True)
+ run_pyoxidizer(source_dir, inno_build_dir, staging_dir, target_triple)
+
+ process_install_rules(EXTRA_INSTALL_RULES, source_dir, staging_dir)
+
+ build_installer(
+ source_dir,
+ inno_build_dir,
+ staging_dir,
+ iscc_exe,
+ version,
+ arch="x64" if "x86_64" in target_triple else None,
+ )
+
+
+def build_installer(
+ source_dir: pathlib.Path,
+ inno_build_dir: pathlib.Path,
+ staging_dir: pathlib.Path,
+ iscc_exe: pathlib.Path,
+ version,
+ arch=None,
+ suffix="",
+):
+ """Build an Inno installer from staged Mercurial files.
+
+ This function is agnostic about how to build Mercurial. It just
+ cares that Mercurial files are in ``staging_dir``.
+ """
+ inno_source_dir = source_dir / "contrib" / "packaging" / "inno"
+
# The final package layout is simply a mirror of the staging directory.
package_files = []
for root, dirs, files in os.walk(staging_dir):
@@ -158,8 +214,11 @@
args = [str(iscc_exe)]
- if vc_x64:
- args.append('/dARCH=x64')
+ if arch:
+ args.append('/dARCH=%s' % arch)
+ args.append('/dSUFFIX=-%s%s' % (arch, suffix))
+ else:
+ args.append('/dSUFFIX=-x86%s' % suffix)
if not version:
version = read_version_py(source_dir)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/packaging/hgpackaging/pyoxidizer.py Fri May 01 08:07:25 2020 -0700
@@ -0,0 +1,145 @@
+# pyoxidizer.py - Packaging support for PyOxidizer
+#
+# Copyright 2020 Gregory Szorc <gregory.szorc@gmail.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+# no-check-code because Python 3 native.
+
+import os
+import pathlib
+import shutil
+import subprocess
+import sys
+
+from .downloads import download_entry
+from .util import (
+ extract_zip_to_directory,
+ process_install_rules,
+ find_vc_runtime_dll,
+)
+
+
+STAGING_RULES_WINDOWS = [
+ ('contrib/bash_completion', 'contrib/'),
+ ('contrib/hgk', 'contrib/hgk.tcl'),
+ ('contrib/hgweb.fcgi', 'contrib/'),
+ ('contrib/hgweb.wsgi', 'contrib/'),
+ ('contrib/logo-droplets.svg', 'contrib/'),
+ ('contrib/mercurial.el', 'contrib/'),
+ ('contrib/mq.el', 'contrib/'),
+ ('contrib/tcsh_completion', 'contrib/'),
+ ('contrib/tcsh_completion_build.sh', 'contrib/'),
+ ('contrib/vim/*', 'contrib/vim/'),
+ ('contrib/win32/postinstall.txt', 'ReleaseNotes.txt'),
+ ('contrib/win32/ReadMe.html', 'ReadMe.html'),
+ ('contrib/xml.rnc', 'contrib/'),
+ ('contrib/zsh_completion', 'contrib/'),
+ ('doc/*.html', 'doc/'),
+ ('doc/style.css', 'doc/'),
+ ('COPYING', 'Copying.txt'),
+]
+
+STAGING_RULES_APP = [
+ ('mercurial/helptext/**/*.txt', 'helptext/'),
+ ('mercurial/defaultrc/*.rc', 'defaultrc/'),
+ ('mercurial/locale/**/*', 'locale/'),
+ ('mercurial/templates/**/*', 'templates/'),
+]
+
+STAGING_EXCLUDES_WINDOWS = [
+ "doc/hg-ssh.8.html",
+]
+
+
+def run_pyoxidizer(
+ source_dir: pathlib.Path,
+ build_dir: pathlib.Path,
+ out_dir: pathlib.Path,
+ target_triple: str,
+):
+ """Build Mercurial with PyOxidizer and copy additional files into place.
+
+ After successful completion, ``out_dir`` contains files constituting a
+ Mercurial install.
+ """
+ # We need to make gettext binaries available for compiling i18n files.
+ gettext_pkg, gettext_entry = download_entry('gettext', build_dir)
+ gettext_dep_pkg = download_entry('gettext-dep', build_dir)[0]
+
+ gettext_root = build_dir / ('gettext-win-%s' % gettext_entry['version'])
+
+ if not gettext_root.exists():
+ extract_zip_to_directory(gettext_pkg, gettext_root)
+ extract_zip_to_directory(gettext_dep_pkg, gettext_root)
+
+ env = dict(os.environ)
+ env["PATH"] = "%s%s%s" % (
+ env["PATH"],
+ os.pathsep,
+ str(gettext_root / "bin"),
+ )
+
+ args = [
+ "pyoxidizer",
+ "build",
+ "--path",
+ str(source_dir / "rust" / "hgcli"),
+ "--release",
+ "--target-triple",
+ target_triple,
+ ]
+
+ subprocess.run(args, env=env, check=True)
+
+ if "windows" in target_triple:
+ target = "app_windows"
+ else:
+ target = "app_posix"
+
+ build_dir = (
+ source_dir / "build" / "pyoxidizer" / target_triple / "release" / target
+ )
+
+ if out_dir.exists():
+ print("purging %s" % out_dir)
+ shutil.rmtree(out_dir)
+
+ # Now assemble all the files from PyOxidizer into the staging directory.
+ shutil.copytree(build_dir, out_dir)
+
+ # Move some of those files around.
+ process_install_rules(STAGING_RULES_APP, build_dir, out_dir)
+ # Nuke the mercurial/* directory, as we copied resources
+ # to an appropriate location just above.
+ shutil.rmtree(out_dir / "mercurial")
+
+ # We also need to run setup.py build_doc to produce html files,
+ # as they aren't built as part of ``pip install``.
+ # This will fail if docutils isn't installed.
+ subprocess.run(
+ [sys.executable, str(source_dir / "setup.py"), "build_doc", "--html"],
+ cwd=str(source_dir),
+ check=True,
+ )
+
+ if "windows" in target_triple:
+ process_install_rules(STAGING_RULES_WINDOWS, source_dir, out_dir)
+
+ # Write out a default editor.rc file to configure notepad as the
+ # default editor.
+ with (out_dir / "defaultrc" / "editor.rc").open(
+ "w", encoding="utf-8"
+ ) as fh:
+ fh.write("[ui]\neditor = notepad\n")
+
+ for f in STAGING_EXCLUDES_WINDOWS:
+ p = out_dir / f
+ if p.exists():
+ print("removing %s" % p)
+ p.unlink()
+
+ # Add vcruntimeXXX.dll next to executable.
+ vc_runtime_dll = find_vc_runtime_dll(x64="x86_64" in target_triple)
+ shutil.copy(vc_runtime_dll, out_dir / vc_runtime_dll.name)
--- a/contrib/packaging/hgpackaging/util.py Thu Mar 05 17:55:05 2020 +0100
+++ b/contrib/packaging/hgpackaging/util.py Fri May 01 08:07:25 2020 -0700
@@ -29,7 +29,59 @@
zf.extractall(dest)
-def find_vc_runtime_files(x64=False):
+def find_vc_runtime_dll(x64=False):
+ """Finds Visual C++ Runtime DLL to include in distribution."""
+ # We invoke vswhere to find the latest Visual Studio install.
+ vswhere = (
+ pathlib.Path(os.environ["ProgramFiles(x86)"])
+ / "Microsoft Visual Studio"
+ / "Installer"
+ / "vswhere.exe"
+ )
+
+ if not vswhere.exists():
+ raise Exception(
+ "could not find vswhere.exe: %s does not exist" % vswhere
+ )
+
+ args = [
+ str(vswhere),
+ # -products * is necessary to return results from Build Tools
+ # (as opposed to full IDE installs).
+ "-products",
+ "*",
+ "-requires",
+ "Microsoft.VisualCpp.Redist.14.Latest",
+ "-latest",
+ "-property",
+ "installationPath",
+ ]
+
+ vs_install_path = pathlib.Path(
+ os.fsdecode(subprocess.check_output(args).strip())
+ )
+
+ # This just gets us a path like
+ # C:\Program Files (x86)\Microsoft Visual Studio\2019\Community
+ # Actually vcruntime140.dll is under a path like:
+ # VC\Redist\MSVC\<version>\<arch>\Microsoft.VC14<X>.CRT\vcruntime140.dll.
+
+ arch = "x64" if x64 else "x86"
+
+ search_glob = (
+ r"%s\VC\Redist\MSVC\*\%s\Microsoft.VC14*.CRT\vcruntime140.dll"
+ % (vs_install_path, arch)
+ )
+
+ candidates = glob.glob(search_glob, recursive=True)
+
+ for candidate in reversed(candidates):
+ return pathlib.Path(candidate)
+
+ raise Exception("could not find vcruntime140.dll")
+
+
+def find_legacy_vc_runtime_files(x64=False):
"""Finds Visual C++ Runtime DLLs to include in distribution."""
winsxs = pathlib.Path(os.environ['SYSTEMROOT']) / 'WinSxS'
--- a/contrib/packaging/hgpackaging/wix.py Thu Mar 05 17:55:05 2020 +0100
+++ b/contrib/packaging/hgpackaging/wix.py Fri May 01 08:07:25 2020 -0700
@@ -22,6 +22,7 @@
build_py2exe,
stage_install,
)
+from .pyoxidizer import run_pyoxidizer
from .util import (
extract_zip_to_directory,
normalize_windows_version,
@@ -121,30 +122,6 @@
subprocess.run(args, cwd=str(cwd), check=True)
-def make_post_build_signing_fn(
- name,
- subject_name=None,
- cert_path=None,
- cert_password=None,
- timestamp_url=None,
-):
- """Create a callable that will use signtool to sign hg.exe."""
-
- def post_build_sign(source_dir, build_dir, dist_dir, version):
- description = '%s %s' % (name, version)
-
- sign_with_signtool(
- dist_dir / 'hg.exe',
- description,
- subject_name=subject_name,
- cert_path=cert_path,
- cert_password=cert_password,
- timestamp_url=timestamp_url,
- )
-
- return post_build_sign
-
-
def make_files_xml(staging_dir: pathlib.Path, is_x64) -> str:
"""Create XML string listing every file to be installed."""
@@ -308,27 +285,23 @@
return doc.toprettyxml()
-def build_installer(
+def build_installer_py2exe(
source_dir: pathlib.Path,
python_exe: pathlib.Path,
msi_name='mercurial',
version=None,
- post_build_fn=None,
extra_packages_script=None,
extra_wxs: typing.Optional[typing.Dict[str, str]] = None,
extra_features: typing.Optional[typing.List[str]] = None,
+ signing_info: typing.Optional[typing.Dict[str, str]] = None,
):
- """Build a WiX MSI installer.
+ """Build a WiX MSI installer using py2exe.
``source_dir`` is the path to the Mercurial source tree to use.
``arch`` is the target architecture. either ``x86`` or ``x64``.
``python_exe`` is the path to the Python executable to use/bundle.
``version`` is the Mercurial version string. If not defined,
``mercurial/__version__.py`` will be consulted.
- ``post_build_fn`` is a callable that will be called after building
- Mercurial but before invoking WiX. It can be used to e.g. facilitate
- signing. It is passed the paths to the Mercurial source, build, and
- dist directories and the resolved Mercurial version.
``extra_packages_script`` is a command to be run to inject extra packages
into the py2exe binary. It should stage packages into the virtualenv and
print a null byte followed by a newline-separated list of packages that
@@ -340,8 +313,6 @@
arch = 'x64' if r'\x64' in os.environ.get('LIB', '') else 'x86'
hg_build_dir = source_dir / 'build'
- dist_dir = source_dir / 'dist'
- wix_dir = source_dir / 'contrib' / 'packaging' / 'wix'
requirements_txt = (
source_dir / 'contrib' / 'packaging' / 'requirements_win32.txt'
@@ -357,15 +328,6 @@
extra_packages_script=extra_packages_script,
)
- orig_version = version or find_version(source_dir)
- version = normalize_windows_version(orig_version)
- print('using version string: %s' % version)
- if version != orig_version:
- print('(normalized from: %s)' % orig_version)
-
- if post_build_fn:
- post_build_fn(source_dir, hg_build_dir, dist_dir, version)
-
build_dir = hg_build_dir / ('wix-%s' % arch)
staging_dir = build_dir / 'stage'
@@ -388,13 +350,112 @@
print('removing %s' % p)
p.unlink()
- wix_pkg, wix_entry = download_entry('wix', hg_build_dir)
- wix_path = hg_build_dir / ('wix-%s' % wix_entry['version'])
+ return run_wix_packaging(
+ source_dir,
+ build_dir,
+ staging_dir,
+ arch,
+ version=version,
+ python2=True,
+ msi_name=msi_name,
+ suffix="-python2",
+ extra_wxs=extra_wxs,
+ extra_features=extra_features,
+ signing_info=signing_info,
+ )
+
+
+def build_installer_pyoxidizer(
+ source_dir: pathlib.Path,
+ target_triple: str,
+ msi_name='mercurial',
+ version=None,
+ extra_wxs: typing.Optional[typing.Dict[str, str]] = None,
+ extra_features: typing.Optional[typing.List[str]] = None,
+ signing_info: typing.Optional[typing.Dict[str, str]] = None,
+):
+ """Build a WiX MSI installer using PyOxidizer."""
+ hg_build_dir = source_dir / "build"
+ build_dir = hg_build_dir / ("wix-%s" % target_triple)
+ staging_dir = build_dir / "stage"
+
+ arch = "x64" if "x86_64" in target_triple else "x86"
+
+ build_dir.mkdir(parents=True, exist_ok=True)
+ run_pyoxidizer(source_dir, build_dir, staging_dir, target_triple)
+
+ # We also install some extra files.
+ process_install_rules(EXTRA_INSTALL_RULES, source_dir, staging_dir)
+
+ # And remove some files we don't want.
+ for f in STAGING_REMOVE_FILES:
+ p = staging_dir / f
+ if p.exists():
+ print('removing %s' % p)
+ p.unlink()
+
+ return run_wix_packaging(
+ source_dir,
+ build_dir,
+ staging_dir,
+ arch,
+ version,
+ python2=False,
+ msi_name=msi_name,
+ extra_wxs=extra_wxs,
+ extra_features=extra_features,
+ signing_info=signing_info,
+ )
+
+
+def run_wix_packaging(
+ source_dir: pathlib.Path,
+ build_dir: pathlib.Path,
+ staging_dir: pathlib.Path,
+ arch: str,
+ version: str,
+ python2: bool,
+ msi_name: typing.Optional[str] = "mercurial",
+ suffix: str = "",
+ extra_wxs: typing.Optional[typing.Dict[str, str]] = None,
+ extra_features: typing.Optional[typing.List[str]] = None,
+ signing_info: typing.Optional[typing.Dict[str, str]] = None,
+):
+ """Invokes WiX to package up a built Mercurial.
+
+ ``signing_info`` is a dict defining properties to facilitate signing the
+ installer. Recognized keys include ``name``, ``subject_name``,
+ ``cert_path``, ``cert_password``, and ``timestamp_url``. If populated,
+ we will sign both the hg.exe and the .msi using the signing credentials
+ specified.
+ """
+
+ orig_version = version or find_version(source_dir)
+ version = normalize_windows_version(orig_version)
+ print('using version string: %s' % version)
+ if version != orig_version:
+ print('(normalized from: %s)' % orig_version)
+
+ if signing_info:
+ sign_with_signtool(
+ staging_dir / "hg.exe",
+ "%s %s" % (signing_info["name"], version),
+ subject_name=signing_info["subject_name"],
+ cert_path=signing_info["cert_path"],
+ cert_password=signing_info["cert_password"],
+ timestamp_url=signing_info["timestamp_url"],
+ )
+
+ wix_dir = source_dir / 'contrib' / 'packaging' / 'wix'
+
+ wix_pkg, wix_entry = download_entry('wix', build_dir)
+ wix_path = build_dir / ('wix-%s' % wix_entry['version'])
if not wix_path.exists():
extract_zip_to_directory(wix_pkg, wix_path)
- ensure_vc90_merge_modules(hg_build_dir)
+ if python2:
+ ensure_vc90_merge_modules(build_dir)
source_build_rel = pathlib.Path(os.path.relpath(source_dir, build_dir))
@@ -413,7 +474,16 @@
source = wix_dir / 'mercurial.wxs'
defines['Version'] = version
defines['Comments'] = 'Installs Mercurial version %s' % version
- defines['VCRedistSrcDir'] = str(hg_build_dir)
+
+ if python2:
+ defines["PythonVersion"] = "2"
+ defines['VCRedistSrcDir'] = str(build_dir)
+ else:
+ defines["PythonVersion"] = "3"
+
+ if (staging_dir / "lib").exists():
+ defines["MercurialHasLib"] = "1"
+
if extra_features:
assert all(';' not in f for f in extra_features)
defines['MercurialExtraFeatures'] = ';'.join(extra_features)
@@ -421,7 +491,9 @@
run_candle(wix_path, build_dir, source, source_build_rel, defines=defines)
msi_path = (
- source_dir / 'dist' / ('%s-%s-%s.msi' % (msi_name, orig_version, arch))
+ source_dir
+ / 'dist'
+ / ('%s-%s-%s%s.msi' % (msi_name, orig_version, arch, suffix))
)
args = [
@@ -448,52 +520,16 @@
print('%s created' % msi_path)
+ if signing_info:
+ sign_with_signtool(
+ msi_path,
+ "%s %s" % (signing_info["name"], version),
+ subject_name=signing_info["subject_name"],
+ cert_path=signing_info["cert_path"],
+ cert_password=signing_info["cert_password"],
+ timestamp_url=signing_info["timestamp_url"],
+ )
+
return {
'msi_path': msi_path,
}
-
-
-def build_signed_installer(
- source_dir: pathlib.Path,
- python_exe: pathlib.Path,
- name: str,
- version=None,
- subject_name=None,
- cert_path=None,
- cert_password=None,
- timestamp_url=None,
- extra_packages_script=None,
- extra_wxs=None,
- extra_features=None,
-):
- """Build an installer with signed executables."""
-
- post_build_fn = make_post_build_signing_fn(
- name,
- subject_name=subject_name,
- cert_path=cert_path,
- cert_password=cert_password,
- timestamp_url=timestamp_url,
- )
-
- info = build_installer(
- source_dir,
- python_exe=python_exe,
- msi_name=name.lower(),
- version=version,
- post_build_fn=post_build_fn,
- extra_packages_script=extra_packages_script,
- extra_wxs=extra_wxs,
- extra_features=extra_features,
- )
-
- description = '%s %s' % (name, version)
-
- sign_with_signtool(
- info['msi_path'],
- description,
- subject_name=subject_name,
- cert_path=cert_path,
- cert_password=cert_password,
- timestamp_url=timestamp_url,
- )
--- a/contrib/packaging/inno/mercurial.iss Thu Mar 05 17:55:05 2020 +0100
+++ b/contrib/packaging/inno/mercurial.iss Fri May 01 08:07:25 2020 -0700
@@ -9,14 +9,13 @@
AppCopyright=Copyright 2005-2020 Matt Mackall and others
AppName=Mercurial
AppVersion={#VERSION}
+OutputBaseFilename=Mercurial-{#VERSION}{#SUFFIX}
#if ARCH == "x64"
AppVerName=Mercurial {#VERSION} (64-bit)
-OutputBaseFilename=Mercurial-{#VERSION}-x64
ArchitecturesAllowed=x64
ArchitecturesInstallIn64BitMode=x64
#else
AppVerName=Mercurial {#VERSION}
-OutputBaseFilename=Mercurial-{#VERSION}
#endif
InfoAfterFile=../postinstall.txt
LicenseFile=Copying.txt
--- a/contrib/packaging/pyoxidizer.bzl Thu Mar 05 17:55:05 2020 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-# Instructions:
-#
-# 1. cargo install --version 0.5.0 pyoxidizer
-# 2. cd /path/to/hg
-# 3. pyoxidizer build --path contrib/packaging [--release]
-# 4. Run build/pyoxidizer/<arch>/<debug|release>/app/hg
-#
-# If you need to build again, you need to remove the build/lib.* and
-# build/temp.* directories, otherwise PyOxidizer fails to pick up C
-# extensions. This is a bug in PyOxidizer.
-
-ROOT = CWD + "/../.."
-
-set_build_path(ROOT + "/build/pyoxidizer")
-
-def make_exe():
- dist = default_python_distribution()
-
- code = "import hgdemandimport; hgdemandimport.enable(); from mercurial import dispatch; dispatch.run()"
-
- config = PythonInterpreterConfig(
- raw_allocator = "system",
- run_eval = code,
- # We want to let the user load extensions from the file system
- filesystem_importer = True,
- # We need this to make resourceutil happy, since it looks for sys.frozen.
- sys_frozen = True,
- legacy_windows_stdio = True,
- )
-
- exe = dist.to_python_executable(
- name = "hg",
- config = config,
- )
-
- # Use setup.py install to build Mercurial and collect Python resources to
- # embed in the executable.
- resources = dist.setup_py_install(ROOT)
- exe.add_python_resources(resources)
-
- return exe
-
-def make_install(exe):
- m = FileManifest()
-
- # `hg` goes in root directory.
- m.add_python_resource(".", exe)
-
- templates = glob(
- include=[ROOT + "/mercurial/templates/**/*"],
- strip_prefix = ROOT + "/mercurial/",
- )
- m.add_manifest(templates)
-
- return m
-
-register_target("exe", make_exe)
-register_target("app", make_install, depends = ["exe"], default = True)
-
-resolve_targets()
--- a/contrib/packaging/wix/mercurial.wxs Thu Mar 05 17:55:05 2020 +0100
+++ b/contrib/packaging/wix/mercurial.wxs Fri May 01 08:07:25 2020 -0700
@@ -79,16 +79,21 @@
</Directory>
</Directory>
- <?if $(var.Platform) = "x86" ?>
- <Merge Id='VCRuntime' DiskId='1' Language='1033'
- SourceFile='$(var.VCRedistSrcDir)\microsoft.vcxx.crt.x86_msm.msm' />
- <Merge Id='VCRuntimePolicy' DiskId='1' Language='1033'
- SourceFile='$(var.VCRedistSrcDir)\policy.x.xx.microsoft.vcxx.crt.x86_msm.msm' />
- <?else?>
- <Merge Id='VCRuntime' DiskId='1' Language='1033'
- SourceFile='$(var.VCRedistSrcDir)\microsoft.vcxx.crt.x64_msm.msm' />
- <Merge Id='VCRuntimePolicy' DiskId='1' Language='1033'
- SourceFile='$(var.VCRedistSrcDir)\policy.x.xx.microsoft.vcxx.crt.x64_msm.msm' />
+ <!-- Install VCRedist merge modules on Python 2. On Python 3,
+ vcruntimeXXX.dll is part of the install layout and gets picked up
+ as a regular file. -->
+ <?if $(var.PythonVersion) = "2" ?>
+ <?if $(var.Platform) = "x86" ?>
+ <Merge Id='VCRuntime' DiskId='1' Language='1033'
+ SourceFile='$(var.VCRedistSrcDir)\microsoft.vcxx.crt.x86_msm.msm' />
+ <Merge Id='VCRuntimePolicy' DiskId='1' Language='1033'
+ SourceFile='$(var.VCRedistSrcDir)\policy.x.xx.microsoft.vcxx.crt.x86_msm.msm' />
+ <?else?>
+ <Merge Id='VCRuntime' DiskId='1' Language='1033'
+ SourceFile='$(var.VCRedistSrcDir)\microsoft.vcxx.crt.x64_msm.msm' />
+ <Merge Id='VCRuntimePolicy' DiskId='1' Language='1033'
+ SourceFile='$(var.VCRedistSrcDir)\policy.x.xx.microsoft.vcxx.crt.x64_msm.msm' />
+ <?endif?>
<?endif?>
</Directory>
@@ -101,10 +106,14 @@
<ComponentGroupRef Id="hg.group.ROOT" />
<ComponentGroupRef Id="hg.group.defaultrc" />
<ComponentGroupRef Id="hg.group.helptext" />
- <ComponentGroupRef Id="hg.group.lib" />
+ <?ifdef MercurialHasLib?>
+ <ComponentGroupRef Id="hg.group.lib" />
+ <?endif?>
<ComponentGroupRef Id="hg.group.templates" />
- <MergeRef Id='VCRuntime' />
- <MergeRef Id='VCRuntimePolicy' />
+ <?if $(var.PythonVersion) = "2" ?>
+ <MergeRef Id='VCRuntime' />
+ <MergeRef Id='VCRuntimePolicy' />
+ <?endif?>
</Feature>
<?ifdef MercurialExtraFeatures?>
<?foreach EXTRAFEAT in $(var.MercurialExtraFeatures)?>
--- a/mercurial/patch.py Thu Mar 05 17:55:05 2020 +0100
+++ b/mercurial/patch.py Fri May 01 08:07:25 2020 -0700
@@ -2558,7 +2558,7 @@
fctx2 is not None
), b'fctx2 unexpectly None in diff hunks filtering'
hunks = hunksfilterfn(fctx2, hunks)
- text = b''.join(sum((list(hlines) for hrange, hlines in hunks), []))
+ text = b''.join(b''.join(hlines) for hrange, hlines in hunks)
if hdr and (text or len(hdr) > 1):
yield b'\n'.join(hdr) + b'\n'
if text:
--- a/rust/hgcli/pyoxidizer.bzl Thu Mar 05 17:55:05 2020 +0100
+++ b/rust/hgcli/pyoxidizer.bzl Fri May 01 08:07:25 2020 -0700
@@ -1,13 +1,24 @@
ROOT = CWD + "/../.."
-def make_exe():
- dist = default_python_distribution()
+# Code to run in Python interpreter.
+RUN_CODE = "import hgdemandimport; hgdemandimport.enable(); from mercurial import dispatch; dispatch.run()"
+
+
+set_build_path(ROOT + "/build/pyoxidizer")
+
- code = "import hgdemandimport; hgdemandimport.enable(); from mercurial import dispatch; dispatch.run()"
+def make_distribution():
+ return default_python_distribution()
+
+def make_distribution_windows():
+ return default_python_distribution(flavor="standalone_dynamic")
+
+
+def make_exe(dist):
config = PythonInterpreterConfig(
raw_allocator = "system",
- run_eval = code,
+ run_eval = RUN_CODE,
# We want to let the user load extensions from the file system
filesystem_importer = True,
# We need this to make resourceutil happy, since it looks for sys.frozen.
@@ -24,30 +35,65 @@
extension_module_filter = "all",
)
- exe.add_python_resources(dist.pip_install([ROOT]))
+ # Add Mercurial to resources.
+ for resource in dist.pip_install(["--verbose", ROOT]):
+ # This is a bit wonky and worth explaining.
+ #
+ # Various parts of Mercurial don't yet support loading package
+ # resources via the ResourceReader interface. Or, not having
+ # file-based resources would be too inconvenient for users.
+ #
+ # So, for package resources, we package them both in the
+ # filesystem as well as in memory. If both are defined,
+ # PyOxidizer will prefer the in-memory location. So even
+ # if the filesystem file isn't packaged in the location
+ # specified here, we should never encounter an errors as the
+ # resource will always be available in memory.
+ if type(resource) == "PythonPackageResource":
+ exe.add_filesystem_relative_python_resource(".", resource)
+ exe.add_in_memory_python_resource(resource)
+ else:
+ exe.add_python_resource(resource)
+
+ # On Windows, we install extra packages for convenience.
+ if "windows" in BUILD_TARGET_TRIPLE:
+ exe.add_python_resources(
+ dist.pip_install(["-r", ROOT + "/contrib/packaging/requirements_win32.txt"])
+ )
return exe
-def make_install(exe):
+
+def make_manifest(dist, exe):
m = FileManifest()
-
- # `hg` goes in root directory.
m.add_python_resource(".", exe)
- templates = glob(
- include = [ROOT + "/mercurial/templates/**/*"],
- strip_prefix = ROOT + "/mercurial/",
- )
- m.add_manifest(templates)
+ return m
- return m
def make_embedded_resources(exe):
return exe.to_embedded_resources()
-register_target("exe", make_exe)
-register_target("app", make_install, depends = ["exe"], default = True)
-register_target("embedded", make_embedded_resources, depends = ["exe"], default_build_script = True)
+
+register_target("distribution_posix", make_distribution)
+register_target("distribution_windows", make_distribution_windows)
+
+register_target("exe_posix", make_exe, depends = ["distribution_posix"])
+register_target("exe_windows", make_exe, depends = ["distribution_windows"])
+
+register_target(
+ "app_posix",
+ make_manifest,
+ depends = ["distribution_posix", "exe_posix"],
+ default = "windows" not in BUILD_TARGET_TRIPLE,
+)
+register_target(
+ "app_windows",
+ make_manifest,
+ depends = ["distribution_windows", "exe_windows"],
+ default = "windows" in BUILD_TARGET_TRIPLE,
+)
+
resolve_targets()
# END OF COMMON USER-ADJUSTED SETTINGS.
@@ -55,5 +101,4 @@
# Everything below this is typically managed by PyOxidizer and doesn't need
# to be updated by people.
-PYOXIDIZER_VERSION = "0.7.0-pre"
-PYOXIDIZER_COMMIT = "c772a1379c3026314eda1c8ea244b86c0658951d"
+PYOXIDIZER_VERSION = "0.7.0"
--- a/tests/test-check-code.t Thu Mar 05 17:55:05 2020 +0100
+++ b/tests/test-check-code.t Fri May 01 08:07:25 2020 -0700
@@ -27,6 +27,7 @@
Skipping contrib/packaging/hgpackaging/downloads.py it has no-che?k-code (glob)
Skipping contrib/packaging/hgpackaging/inno.py it has no-che?k-code (glob)
Skipping contrib/packaging/hgpackaging/py2exe.py it has no-che?k-code (glob)
+ Skipping contrib/packaging/hgpackaging/pyoxidizer.py it has no-che?k-code (glob)
Skipping contrib/packaging/hgpackaging/util.py it has no-che?k-code (glob)
Skipping contrib/packaging/hgpackaging/wix.py it has no-che?k-code (glob)
Skipping i18n/polib.py it has no-che?k-code (glob)