D12265: packaging: remove py2exe / Python 2.7 support
indygreg (Gregory Szorc)
phabricator at mercurial-scm.org
Wed Mar 2 15:44:34 UTC 2022
indygreg created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.
REVISION SUMMARY
This commit started by deleting references to py2exe (which is only used
on Python 2). After pulling the thread, quite a lot of code was orphaned
and was deleted.
REPOSITORY
rHG Mercurial
BRANCH
default
REVISION DETAIL
https://phab.mercurial-scm.org/D12265
AFFECTED FILES
contrib/packaging/hgpackaging/cli.py
contrib/packaging/hgpackaging/downloads.py
contrib/packaging/hgpackaging/inno.py
contrib/packaging/hgpackaging/py2exe.py
contrib/packaging/hgpackaging/util.py
contrib/packaging/hgpackaging/wix.py
contrib/packaging/requirements-windows-py2.txt
tests/test-check-code.t
CHANGE DETAILS
diff --git a/tests/test-check-code.t b/tests/test-check-code.t
--- a/tests/test-check-code.t
+++ b/tests/test-check-code.t
@@ -27,7 +27,6 @@
Skipping contrib/packaging/hgpackaging/cli.py it has no-che?k-code (glob)
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)
diff --git a/contrib/packaging/requirements-windows-py2.txt b/contrib/packaging/requirements-windows-py2.txt
deleted file mode 100644
--- a/contrib/packaging/requirements-windows-py2.txt
+++ /dev/null
@@ -1,59 +0,0 @@
-#
-# This file is autogenerated by pip-compile
-# To update, run:
-#
-# pip-compile --generate-hashes --output-file=contrib/packaging/requirements-windows-py2.txt contrib/packaging/requirements-windows.txt.in
-#
-certifi==2021.5.30 \
- --hash=sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee \
- --hash=sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8 \
- # via dulwich
-configparser==4.0.2 \
- --hash=sha256:254c1d9c79f60c45dfde850850883d5aaa7f19a23f13561243a050d5a7c3fe4c \
- --hash=sha256:c7d282687a5308319bf3d2e7706e575c635b0a470342641c93bea0ea3b5331df \
- # via entrypoints
-docutils==0.16 \
- --hash=sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af \
- --hash=sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc \
- # via -r contrib/packaging/requirements-windows.txt.in
-dulwich==0.19.16 ; python_version <= "2.7" \
- --hash=sha256:10699277c6268d0c16febe141a5b1c1a6e9744f3144c2d2de1706f4b1adafe63 \
- --hash=sha256:267160904e9a1cb6c248c5efc53597a35d038ecc6f60bdc4546b3053bed11982 \
- --hash=sha256:4e3aba5e4844e7c700721c1fc696987ea820ee3528a03604dc4e74eff4196826 \
- --hash=sha256:60bb2c2c92f5025c1b53a556304008f0f624c98ae36f22d870e056b2d4236c11 \
- --hash=sha256:dddae02d372fc3b5cfb0046d0f62246ef281fa0c088df7601ab5916607add94b \
- --hash=sha256:f00d132082b8fcc2eb0d722abc773d4aeb5558c1475d7edd1f0f571146c29db9 \
- --hash=sha256:f74561c448bfb6f04c07de731c1181ae4280017f759b0bb04fa5770aa84ca850 \
- # via -r contrib/packaging/requirements-windows.txt.in
-entrypoints==0.3 \
- --hash=sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19 \
- --hash=sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451 \
- # via keyring
-keyring==18.0.1 \
- --hash=sha256:67d6cc0132bd77922725fae9f18366bb314fd8f95ff4d323a4df41890a96a838 \
- --hash=sha256:7b29ebfcf8678c4da531b2478a912eea01e80007e5ddca9ee0c7038cb3489ec6 \
- # via -r contrib/packaging/requirements-windows.txt.in
-pygments==2.5.2 \
- --hash=sha256:2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b \
- --hash=sha256:98c8aa5a9f778fcd1026a17361ddaf7330d1b7c62ae97c3bb0ae73e0b9b6b0fe \
- # via -r contrib/packaging/requirements-windows.txt.in
-pywin32-ctypes==0.2.0 \
- --hash=sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942 \
- --hash=sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98 \
- # via -r contrib/packaging/requirements-windows.txt.in, keyring
-urllib3==1.25.11 \
- --hash=sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2 \
- --hash=sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e \
- # via dulwich
-windows-curses==2.1.0 \
- --hash=sha256:261fde5680d1ce4ce116908996b9a3cfb0ffb03ea68d42240f62b56a9fa6af2c \
- --hash=sha256:66034dc9a705d87308cc9ea90836f4ee60008a1d5e2c1d34ace627f60268158b \
- --hash=sha256:669caad3ae16faf2d201d7ab3b8af418a2fd074d8a39d60ca26f3acb34b6afe5 \
- --hash=sha256:73bd3eebccfda55330783f165151de115bfa238d1332f0b2e224b550d6187840 \
- --hash=sha256:89a6d973f88cfe49b41ea80164dcbec209d296e0cec34a02002578b0bf464a64 \
- --hash=sha256:8ba7c000d7ffa5452bbd0966b96e69261e4f117ebe510aeb8771a9650197b7f0 \
- --hash=sha256:97084c6b37b1534f6a28a514d521dfae402f77dcbad42b14ee32e8d5bdc13648 \
- --hash=sha256:9e474a181f96d60429a4766145628264e60b72e7715876f9135aeb2e842f9433 \
- --hash=sha256:cfe64c30807c146ef8d094412f90f2a2c81ad6aefff3ebfe8e37aabe2f801303 \
- --hash=sha256:ff8c67f74b88944d99fa9d22971c05c335bc74f149120f0a69340c2c3a595497 \
- # via -r contrib/packaging/requirements-windows.txt.in
diff --git a/contrib/packaging/hgpackaging/wix.py b/contrib/packaging/hgpackaging/wix.py
--- a/contrib/packaging/hgpackaging/wix.py
+++ b/contrib/packaging/hgpackaging/wix.py
@@ -7,376 +7,16 @@
# no-check-code because Python 3 native.
-import collections
import json
import os
import pathlib
-import re
import shutil
-import subprocess
import typing
-import uuid
-import xml.dom.minidom
-from .downloads import download_entry
-from .py2exe import (
- build_py2exe,
- stage_install,
-)
from .pyoxidizer import (
build_docs_html,
- create_pyoxidizer_install_layout,
run_pyoxidizer,
)
-from .util import (
- extract_zip_to_directory,
- normalize_windows_version,
- process_install_rules,
- sign_with_signtool,
-)
-
-
-EXTRA_PACKAGES = {
- 'dulwich',
- 'distutils',
- 'keyring',
- 'pygments',
- 'win32ctypes',
-}
-
-EXTRA_INCLUDES = {
- '_curses',
- '_curses_panel',
-}
-
-EXTRA_INSTALL_RULES = [
- ('contrib/packaging/wix/COPYING.rtf', 'COPYING.rtf'),
- ('contrib/win32/mercurial.ini', 'defaultrc/mercurial.rc'),
-]
-
-STAGING_REMOVE_FILES = [
- # We use the RTF variant.
- 'copying.txt',
-]
-
-SHORTCUTS = {
- # hg.1.html'
- 'hg.file.5d3e441c_28d9_5542_afd0_cdd4234f12d5': {
- 'Name': 'Mercurial Command Reference',
- },
- # hgignore.5.html
- 'hg.file.5757d8e0_f207_5e10_a2ec_3ba0a062f431': {
- 'Name': 'Mercurial Ignore Files',
- },
- # hgrc.5.html
- 'hg.file.92e605fd_1d1a_5dc6_9fc0_5d2998eb8f5e': {
- 'Name': 'Mercurial Configuration Files',
- },
-}
-
-
-def find_version(source_dir: pathlib.Path):
- version_py = source_dir / 'mercurial' / '__version__.py'
-
- with version_py.open('r', encoding='utf-8') as fh:
- source = fh.read().strip()
-
- m = re.search('version = b"(.*)"', source)
- return m.group(1)
-
-
-def ensure_vc90_merge_modules(build_dir):
- x86 = (
- download_entry(
- 'vc9-crt-x86-msm',
- build_dir,
- local_name='microsoft.vcxx.crt.x86_msm.msm',
- )[0],
- download_entry(
- 'vc9-crt-x86-msm-policy',
- build_dir,
- local_name='policy.x.xx.microsoft.vcxx.crt.x86_msm.msm',
- )[0],
- )
-
- x64 = (
- download_entry(
- 'vc9-crt-x64-msm',
- build_dir,
- local_name='microsoft.vcxx.crt.x64_msm.msm',
- )[0],
- download_entry(
- 'vc9-crt-x64-msm-policy',
- build_dir,
- local_name='policy.x.xx.microsoft.vcxx.crt.x64_msm.msm',
- )[0],
- )
- return {
- 'x86': x86,
- 'x64': x64,
- }
-
-
-def run_candle(wix, cwd, wxs, source_dir, defines=None):
- args = [
- str(wix / 'candle.exe'),
- '-nologo',
- str(wxs),
- '-dSourceDir=%s' % source_dir,
- ]
-
- if defines:
- args.extend('-d%s=%s' % define for define in sorted(defines.items()))
-
- subprocess.run(args, cwd=str(cwd), check=True)
-
-
-def make_files_xml(staging_dir: pathlib.Path, is_x64) -> str:
- """Create XML string listing every file to be installed."""
-
- # We derive GUIDs from a deterministic file path identifier.
- # We shoehorn the name into something that looks like a URL because
- # the UUID namespaces are supposed to work that way (even though
- # the input data probably is never validated).
-
- doc = xml.dom.minidom.parseString(
- '<?xml version="1.0" encoding="utf-8"?>'
- '<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">'
- '</Wix>'
- )
-
- # Assemble the install layout by directory. This makes it easier to
- # emit XML, since each directory has separate entities.
- manifest = collections.defaultdict(dict)
-
- for root, dirs, files in os.walk(staging_dir):
- dirs.sort()
-
- root = pathlib.Path(root)
- rel_dir = root.relative_to(staging_dir)
-
- for i in range(len(rel_dir.parts)):
- parent = '/'.join(rel_dir.parts[0 : i + 1])
- manifest.setdefault(parent, {})
-
- for f in sorted(files):
- full = root / f
- manifest[str(rel_dir).replace('\\', '/')][full.name] = full
-
- component_groups = collections.defaultdict(list)
-
- # Now emit a <Fragment> for each directory.
- # Each directory is composed of a <DirectoryRef> pointing to its parent
- # and defines child <Directory>'s and a <Component> with all the files.
- for dir_name, entries in sorted(manifest.items()):
- # The directory id is derived from the path. But the root directory
- # is special.
- if dir_name == '.':
- parent_directory_id = 'INSTALLDIR'
- else:
- parent_directory_id = 'hg.dir.%s' % dir_name.replace(
- '/', '.'
- ).replace('-', '_')
-
- fragment = doc.createElement('Fragment')
- directory_ref = doc.createElement('DirectoryRef')
- directory_ref.setAttribute('Id', parent_directory_id)
-
- # Add <Directory> entries for immediate children directories.
- for possible_child in sorted(manifest.keys()):
- if (
- dir_name == '.'
- and '/' not in possible_child
- and possible_child != '.'
- ):
- child_directory_id = ('hg.dir.%s' % possible_child).replace(
- '-', '_'
- )
- name = possible_child
- else:
- if not possible_child.startswith('%s/' % dir_name):
- continue
- name = possible_child[len(dir_name) + 1 :]
- if '/' in name:
- continue
-
- child_directory_id = 'hg.dir.%s' % possible_child.replace(
- '/', '.'
- ).replace('-', '_')
-
- directory = doc.createElement('Directory')
- directory.setAttribute('Id', child_directory_id)
- directory.setAttribute('Name', name)
- directory_ref.appendChild(directory)
-
- # Add <Component>s for files in this directory.
- for rel, source_path in sorted(entries.items()):
- if dir_name == '.':
- full_rel = rel
- else:
- full_rel = '%s/%s' % (dir_name, rel)
-
- component_unique_id = (
- 'https://www.mercurial-scm.org/wix-installer/0/component/%s'
- % full_rel
- )
- component_guid = uuid.uuid5(uuid.NAMESPACE_URL, component_unique_id)
- component_id = 'hg.component.%s' % str(component_guid).replace(
- '-', '_'
- )
-
- component = doc.createElement('Component')
-
- component.setAttribute('Id', component_id)
- component.setAttribute('Guid', str(component_guid).upper())
- component.setAttribute('Win64', 'yes' if is_x64 else 'no')
-
- # Assign this component to a top-level group.
- if dir_name == '.':
- component_groups['ROOT'].append(component_id)
- elif '/' in dir_name:
- component_groups[dir_name[0 : dir_name.index('/')]].append(
- component_id
- )
- else:
- component_groups[dir_name].append(component_id)
-
- unique_id = (
- 'https://www.mercurial-scm.org/wix-installer/0/%s' % full_rel
- )
- file_guid = uuid.uuid5(uuid.NAMESPACE_URL, unique_id)
-
- # IDs have length limits. So use GUID to derive them.
- file_guid_normalized = str(file_guid).replace('-', '_')
- file_id = 'hg.file.%s' % file_guid_normalized
-
- file_element = doc.createElement('File')
- file_element.setAttribute('Id', file_id)
- file_element.setAttribute('Source', str(source_path))
- file_element.setAttribute('KeyPath', 'yes')
- file_element.setAttribute('ReadOnly', 'yes')
-
- component.appendChild(file_element)
- directory_ref.appendChild(component)
-
- fragment.appendChild(directory_ref)
- doc.documentElement.appendChild(fragment)
-
- for group, component_ids in sorted(component_groups.items()):
- fragment = doc.createElement('Fragment')
- component_group = doc.createElement('ComponentGroup')
- component_group.setAttribute('Id', 'hg.group.%s' % group)
-
- for component_id in component_ids:
- component_ref = doc.createElement('ComponentRef')
- component_ref.setAttribute('Id', component_id)
- component_group.appendChild(component_ref)
-
- fragment.appendChild(component_group)
- doc.documentElement.appendChild(fragment)
-
- # Add <Shortcut> to files that have it defined.
- for file_id, metadata in sorted(SHORTCUTS.items()):
- els = doc.getElementsByTagName('File')
- els = [el for el in els if el.getAttribute('Id') == file_id]
-
- if not els:
- raise Exception('could not find File[Id=%s]' % file_id)
-
- for el in els:
- shortcut = doc.createElement('Shortcut')
- shortcut.setAttribute('Id', 'hg.shortcut.%s' % file_id)
- shortcut.setAttribute('Directory', 'ProgramMenuDir')
- shortcut.setAttribute('Icon', 'hgIcon.ico')
- shortcut.setAttribute('IconIndex', '0')
- shortcut.setAttribute('Advertise', 'yes')
- for k, v in sorted(metadata.items()):
- shortcut.setAttribute(k, v)
-
- el.appendChild(shortcut)
-
- return doc.toprettyxml()
-
-
-def build_installer_py2exe(
- source_dir: pathlib.Path,
- python_exe: pathlib.Path,
- msi_name='mercurial',
- version=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 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.
- ``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
- should be included in the exe.
- ``extra_wxs`` is a dict of {wxs_name: working_dir_for_wxs_build}.
- ``extra_features`` is a list of additional named Features to include in
- the build. These must match Feature names in one of the wxs scripts.
- """
- arch = 'x64' if r'\x64' in os.environ.get('LIB', '') else 'x86'
-
- hg_build_dir = source_dir / 'build'
-
- requirements_txt = (
- source_dir / 'contrib' / 'packaging' / 'requirements-windows-py2.txt'
- )
-
- build_py2exe(
- source_dir,
- hg_build_dir,
- python_exe,
- 'wix',
- requirements_txt,
- extra_packages=EXTRA_PACKAGES,
- extra_packages_script=extra_packages_script,
- extra_includes=EXTRA_INCLUDES,
- )
-
- build_dir = hg_build_dir / ('wix-%s' % arch)
- staging_dir = build_dir / 'stage'
-
- build_dir.mkdir(exist_ok=True)
-
- # Purge the staging directory for every build so packaging is pristine.
- if staging_dir.exists():
- print('purging %s' % staging_dir)
- shutil.rmtree(staging_dir)
-
- stage_install(source_dir, staging_dir, lower_case=True)
-
- # 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=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(
@@ -454,133 +94,3 @@
return {
"msi_path": dist_path,
}
-
-
-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)
-
- if python2:
- ensure_vc90_merge_modules(build_dir)
-
- source_build_rel = pathlib.Path(os.path.relpath(source_dir, build_dir))
-
- defines = {'Platform': arch}
-
- # Derive a .wxs file with the staged files.
- manifest_wxs = build_dir / 'stage.wxs'
- with manifest_wxs.open('w', encoding='utf-8') as fh:
- fh.write(make_files_xml(staging_dir, is_x64=arch == 'x64'))
-
- run_candle(wix_path, build_dir, manifest_wxs, staging_dir, defines=defines)
-
- for source, rel_path in sorted((extra_wxs or {}).items()):
- run_candle(wix_path, build_dir, source, rel_path, defines=defines)
-
- source = wix_dir / 'mercurial.wxs'
- defines['Version'] = version
- defines['Comments'] = 'Installs Mercurial version %s' % version
-
- 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)
-
- run_candle(wix_path, build_dir, source, source_build_rel, defines=defines)
-
- msi_path = (
- source_dir
- / 'dist'
- / ('%s-%s-%s%s.msi' % (msi_name, orig_version, arch, suffix))
- )
-
- args = [
- str(wix_path / 'light.exe'),
- '-nologo',
- '-ext',
- 'WixUIExtension',
- '-sw1076',
- '-spdb',
- '-o',
- str(msi_path),
- ]
-
- for source, rel_path in sorted((extra_wxs or {}).items()):
- assert source.endswith('.wxs')
- source = os.path.basename(source)
- args.append(str(build_dir / ('%s.wixobj' % source[:-4])))
-
- args.extend(
- [
- str(build_dir / 'stage.wixobj'),
- str(build_dir / 'mercurial.wixobj'),
- ]
- )
-
- subprocess.run(args, cwd=str(source_dir), check=True)
-
- 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,
- }
diff --git a/contrib/packaging/hgpackaging/util.py b/contrib/packaging/hgpackaging/util.py
--- a/contrib/packaging/hgpackaging/util.py
+++ b/contrib/packaging/hgpackaging/util.py
@@ -7,23 +7,15 @@
# no-check-code because Python 3 native.
-import distutils.version
-import getpass
import glob
import os
import pathlib
import re
import shutil
import subprocess
-import tarfile
import zipfile
-def extract_tar_to_directory(source: pathlib.Path, dest: pathlib.Path):
- with tarfile.open(source, 'r') as tf:
- tf.extractall(dest)
-
-
def extract_zip_to_directory(source: pathlib.Path, dest: pathlib.Path):
with zipfile.ZipFile(source, 'r') as zf:
zf.extractall(dest)
@@ -81,59 +73,6 @@
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'
-
- prefix = 'amd64' if x64 else 'x86'
-
- candidates = sorted(
- p
- for p in os.listdir(winsxs)
- if p.lower().startswith('%s_microsoft.vc90.crt_' % prefix)
- )
-
- for p in candidates:
- print('found candidate VC runtime: %s' % p)
-
- # Take the newest version.
- version = candidates[-1]
-
- d = winsxs / version
-
- return [
- d / 'msvcm90.dll',
- d / 'msvcp90.dll',
- d / 'msvcr90.dll',
- winsxs / 'Manifests' / ('%s.manifest' % version),
- ]
-
-
-def windows_10_sdk_info():
- """Resolves information about the Windows 10 SDK."""
-
- base = pathlib.Path(os.environ['ProgramFiles(x86)']) / 'Windows Kits' / '10'
-
- if not base.is_dir():
- raise Exception('unable to find Windows 10 SDK at %s' % base)
-
- # Find the latest version.
- bin_base = base / 'bin'
-
- versions = [v for v in os.listdir(bin_base) if v.startswith('10.')]
- version = sorted(versions, reverse=True)[0]
-
- bin_version = bin_base / version
-
- return {
- 'root': base,
- 'version': version,
- 'bin_root': bin_version,
- 'bin_x86': bin_version / 'x86',
- 'bin_x64': bin_version / 'x64',
- }
-
-
def normalize_windows_version(version):
"""Normalize Mercurial version string so WiX/Inno accepts it.
@@ -194,93 +133,6 @@
return '.'.join('%d' % x for x in versions[0:4])
-def find_signtool():
- """Find signtool.exe from the Windows SDK."""
- sdk = windows_10_sdk_info()
-
- for key in ('bin_x64', 'bin_x86'):
- p = sdk[key] / 'signtool.exe'
-
- if p.exists():
- return p
-
- raise Exception('could not find signtool.exe in Windows 10 SDK')
-
-
-def sign_with_signtool(
- file_path,
- description,
- subject_name=None,
- cert_path=None,
- cert_password=None,
- timestamp_url=None,
-):
- """Digitally sign a file with signtool.exe.
-
- ``file_path`` is file to sign.
- ``description`` is text that goes in the signature.
-
- The signing certificate can be specified by ``cert_path`` or
- ``subject_name``. These correspond to the ``/f`` and ``/n`` arguments
- to signtool.exe, respectively.
-
- The certificate password can be specified via ``cert_password``. If
- not provided, you will be prompted for the password.
-
- ``timestamp_url`` is the URL of a RFC 3161 timestamp server (``/tr``
- argument to signtool.exe).
- """
- if cert_path and subject_name:
- raise ValueError('cannot specify both cert_path and subject_name')
-
- while cert_path and not cert_password:
- cert_password = getpass.getpass('password for %s: ' % cert_path)
-
- args = [
- str(find_signtool()),
- 'sign',
- '/v',
- '/fd',
- 'sha256',
- '/d',
- description,
- ]
-
- if cert_path:
- args.extend(['/f', str(cert_path), '/p', cert_password])
- elif subject_name:
- args.extend(['/n', subject_name])
-
- if timestamp_url:
- args.extend(['/tr', timestamp_url, '/td', 'sha256'])
-
- args.append(str(file_path))
-
- print('signing %s' % file_path)
- subprocess.run(args, check=True)
-
-
-PRINT_PYTHON_INFO = '''
-import platform; print("%s:%s" % (platform.architecture()[0], platform.python_version()))
-'''.strip()
-
-
-def python_exe_info(python_exe: pathlib.Path):
- """Obtain information about a Python executable."""
-
- res = subprocess.check_output([str(python_exe), '-c', PRINT_PYTHON_INFO])
-
- arch, version = res.decode('utf-8').split(':')
-
- version = distutils.version.LooseVersion(version)
-
- return {
- 'arch': arch,
- 'version': version,
- 'py3': version >= distutils.version.LooseVersion('3'),
- }
-
-
def process_install_rules(
rules: list, source_dir: pathlib.Path, dest_dir: pathlib.Path
):
diff --git a/contrib/packaging/hgpackaging/py2exe.py b/contrib/packaging/hgpackaging/py2exe.py
deleted file mode 100644
--- a/contrib/packaging/hgpackaging/py2exe.py
+++ /dev/null
@@ -1,248 +0,0 @@
-# py2exe.py - Functionality for performing py2exe builds.
-#
-# Copyright 2019 Gregory Szorc <gregory.szorc at 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 subprocess
-
-from .downloads import download_entry
-from .util import (
- extract_tar_to_directory,
- extract_zip_to_directory,
- process_install_rules,
- python_exe_info,
-)
-
-
-STAGING_RULES = [
- ('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/'),
- ('dist/hg.exe', './'),
- ('dist/lib/*.dll', 'lib/'),
- ('dist/lib/*.pyd', 'lib/'),
- ('dist/lib/library.zip', 'lib/'),
- ('dist/Microsoft.VC*.CRT.manifest', './'),
- ('dist/msvc*.dll', './'),
- ('dist/python*.dll', './'),
- ('doc/*.html', 'doc/'),
- ('doc/style.css', 'doc/'),
- ('mercurial/helptext/**/*.txt', 'helptext/'),
- ('mercurial/defaultrc/*.rc', 'defaultrc/'),
- ('mercurial/locale/**/*', 'locale/'),
- ('mercurial/templates/**/*', 'templates/'),
- ('COPYING', 'Copying.txt'),
-]
-
-# List of paths to exclude from the staging area.
-STAGING_EXCLUDES = [
- 'doc/hg-ssh.8.html',
-]
-
-
-def build_py2exe(
- source_dir: pathlib.Path,
- build_dir: pathlib.Path,
- python_exe: pathlib.Path,
- build_name: str,
- venv_requirements_txt: pathlib.Path,
- extra_packages=None,
- extra_excludes=None,
- extra_dll_excludes=None,
- extra_packages_script=None,
- extra_includes=None,
-):
- """Build Mercurial with py2exe.
-
- Build files will be placed in ``build_dir``.
-
- py2exe's setup.py doesn't use setuptools. It doesn't have modern logic
- for finding the Python 2.7 toolchain. So, we require the environment
- to already be configured with an active toolchain.
- """
- if 'VCINSTALLDIR' not in os.environ:
- raise Exception(
- 'not running from a Visual C++ build environment; '
- 'execute the "Visual C++ <version> Command Prompt" '
- 'application shortcut or a vcsvarsall.bat file'
- )
-
- # Identity x86/x64 and validate the environment matches the Python
- # architecture.
- vc_x64 = r'\x64' in os.environ['LIB']
-
- py_info = python_exe_info(python_exe)
-
- if vc_x64:
- if py_info['arch'] != '64bit':
- raise Exception(
- 'architecture mismatch: Visual C++ environment '
- 'is configured for 64-bit but Python is 32-bit'
- )
- else:
- if py_info['arch'] != '32bit':
- raise Exception(
- 'architecture mismatch: Visual C++ environment '
- 'is configured for 32-bit but Python is 64-bit'
- )
-
- if py_info['py3']:
- raise Exception('Only Python 2 is currently supported')
-
- build_dir.mkdir(exist_ok=True)
-
- gettext_pkg, gettext_entry = download_entry('gettext', build_dir)
- gettext_dep_pkg = download_entry('gettext-dep', build_dir)[0]
- virtualenv_pkg, virtualenv_entry = download_entry('virtualenv', build_dir)
- py2exe_pkg, py2exe_entry = download_entry('py2exe', build_dir)
-
- venv_path = build_dir / (
- 'venv-%s-%s' % (build_name, 'x64' if vc_x64 else 'x86')
- )
-
- 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)
-
- # This assumes Python 2. We don't need virtualenv on Python 3.
- virtualenv_src_path = build_dir / (
- 'virtualenv-%s' % virtualenv_entry['version']
- )
- virtualenv_py = virtualenv_src_path / 'virtualenv.py'
-
- if not virtualenv_src_path.exists():
- extract_tar_to_directory(virtualenv_pkg, build_dir)
-
- py2exe_source_path = build_dir / ('py2exe-%s' % py2exe_entry['version'])
-
- if not py2exe_source_path.exists():
- extract_zip_to_directory(py2exe_pkg, build_dir)
-
- if not venv_path.exists():
- print('creating virtualenv with dependencies')
- subprocess.run(
- [str(python_exe), str(virtualenv_py), str(venv_path)], check=True
- )
-
- venv_python = venv_path / 'Scripts' / 'python.exe'
- venv_pip = venv_path / 'Scripts' / 'pip.exe'
-
- subprocess.run(
- [str(venv_pip), 'install', '-r', str(venv_requirements_txt)], check=True
- )
-
- # Force distutils to use VC++ settings from environment, which was
- # validated above.
- env = dict(os.environ)
- env['DISTUTILS_USE_SDK'] = '1'
- env['MSSdk'] = '1'
-
- if extra_packages_script:
- more_packages = set(
- subprocess.check_output(extra_packages_script, cwd=build_dir)
- .split(b'\0')[-1]
- .strip()
- .decode('utf-8')
- .splitlines()
- )
- if more_packages:
- if not extra_packages:
- extra_packages = more_packages
- else:
- extra_packages |= more_packages
-
- if extra_packages:
- env['HG_PY2EXE_EXTRA_PACKAGES'] = ' '.join(sorted(extra_packages))
- hgext3rd_extras = sorted(
- e for e in extra_packages if e.startswith('hgext3rd.')
- )
- if hgext3rd_extras:
- env['HG_PY2EXE_EXTRA_INSTALL_PACKAGES'] = ' '.join(hgext3rd_extras)
- if extra_includes:
- env['HG_PY2EXE_EXTRA_INCLUDES'] = ' '.join(sorted(extra_includes))
- if extra_excludes:
- env['HG_PY2EXE_EXTRA_EXCLUDES'] = ' '.join(sorted(extra_excludes))
- if extra_dll_excludes:
- env['HG_PY2EXE_EXTRA_DLL_EXCLUDES'] = ' '.join(
- sorted(extra_dll_excludes)
- )
-
- py2exe_py_path = venv_path / 'Lib' / 'site-packages' / 'py2exe'
- if not py2exe_py_path.exists():
- print('building py2exe')
- subprocess.run(
- [str(venv_python), 'setup.py', 'install'],
- cwd=py2exe_source_path,
- env=env,
- check=True,
- )
-
- # Register location of msgfmt and other binaries.
- env['PATH'] = '%s%s%s' % (
- env['PATH'],
- os.pathsep,
- str(gettext_root / 'bin'),
- )
-
- print('building Mercurial')
- subprocess.run(
- [str(venv_python), 'setup.py', 'py2exe', 'build_doc', '--html'],
- cwd=str(source_dir),
- env=env,
- check=True,
- )
-
-
-def stage_install(
- source_dir: pathlib.Path, staging_dir: pathlib.Path, lower_case=False
-):
- """Copy all files to be installed to a directory.
-
- This allows packaging to simply walk a directory tree to find source
- files.
- """
- if lower_case:
- rules = []
- for source, dest in STAGING_RULES:
- # Only lower directory names.
- if '/' in dest:
- parent, leaf = dest.rsplit('/', 1)
- dest = '%s/%s' % (parent.lower(), leaf)
- rules.append((source, dest))
- else:
- rules = STAGING_RULES
-
- process_install_rules(rules, source_dir, staging_dir)
-
- # Write out a default editor.rc file to configure notepad as the
- # default editor.
- with (staging_dir / 'defaultrc' / 'editor.rc').open(
- 'w', encoding='utf-8'
- ) as fh:
- fh.write('[ui]\neditor = notepad\n')
-
- # Purge any files we don't want to be there.
- for f in STAGING_EXCLUDES:
- p = staging_dir / f
- if p.exists():
- print('removing %s' % p)
- p.unlink()
diff --git a/contrib/packaging/hgpackaging/inno.py b/contrib/packaging/hgpackaging/inno.py
--- a/contrib/packaging/hgpackaging/inno.py
+++ b/contrib/packaging/hgpackaging/inno.py
@@ -14,29 +14,13 @@
import jinja2
-from .py2exe import (
- build_py2exe,
- stage_install,
-)
from .pyoxidizer import create_pyoxidizer_install_layout
from .util import (
- find_legacy_vc_runtime_files,
normalize_windows_version,
process_install_rules,
read_version_py,
)
-EXTRA_PACKAGES = {
- 'dulwich',
- 'keyring',
- 'pygments',
- 'win32ctypes',
-}
-
-EXTRA_INCLUDES = {
- '_curses',
- '_curses_panel',
-}
EXTRA_INSTALL_RULES = [
('contrib/win32/mercurial.ini', 'defaultrc/mercurial.rc'),
@@ -47,80 +31,6 @@
}
-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 using py2exe.
-
- Build files will be placed in ``build_dir``.
-
- py2exe's setup.py doesn't use setuptools. It doesn't have modern logic
- for finding the Python 2.7 toolchain. So, we require the environment
- to already be configured with an active toolchain.
- """
- if not iscc_exe.exists():
- raise Exception('%s does not exist' % iscc_exe)
-
- vc_x64 = r'\x64' in os.environ.get('LIB', '')
- arch = 'x64' if vc_x64 else 'x86'
- inno_build_dir = build_dir / ('inno-py2exe-%s' % arch)
- staging_dir = inno_build_dir / 'stage'
-
- requirements_txt = (
- source_dir / 'contrib' / 'packaging' / 'requirements-windows-py2.txt'
- )
-
- inno_build_dir.mkdir(parents=True, exist_ok=True)
-
- build_py2exe(
- source_dir,
- build_dir,
- python_exe,
- 'inno',
- requirements_txt,
- extra_packages=EXTRA_PACKAGES,
- extra_includes=EXTRA_INCLUDES,
- )
-
- # Purge the staging directory for every build so packaging is
- # pristine.
- if staging_dir.exists():
- print('purging %s' % staging_dir)
- shutil.rmtree(staging_dir)
-
- # Now assemble all the packaged files into the staging directory.
- stage_install(source_dir, staging_dir)
-
- # We also install some extra files.
- 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_legacy_vc_runtime_files(vc_x64):
- if f.name.endswith('.manifest'):
- basename = 'Microsoft.VC90.CRT.manifest'
- else:
- basename = f.name
-
- dest_path = staging_dir / basename
-
- 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,
diff --git a/contrib/packaging/hgpackaging/downloads.py b/contrib/packaging/hgpackaging/downloads.py
--- a/contrib/packaging/hgpackaging/downloads.py
+++ b/contrib/packaging/hgpackaging/downloads.py
@@ -25,48 +25,6 @@
'size': 715086,
'sha256': '411f94974492fd2ecf52590cb05b1023530aec67e64154a88b1e4ebcd9c28588',
},
- 'py2exe': {
- 'url': 'https://versaweb.dl.sourceforge.net/project/py2exe/py2exe/0.6.9/py2exe-0.6.9.zip',
- 'size': 149687,
- 'sha256': '6bd383312e7d33eef2e43a5f236f9445e4f3e0f6b16333c6f183ed445c44ddbd',
- 'version': '0.6.9',
- },
- # The VC9 CRT merge modules aren't readily available on most systems because
- # they are only installed as part of a full Visual Studio 2008 install.
- # While we could potentially extract them from a Visual Studio 2008
- # installer, it is easier to just fetch them from a known URL.
- 'vc9-crt-x86-msm': {
- 'url': 'https://github.com/indygreg/vc90-merge-modules/raw/9232f8f0b2135df619bf7946eaa176b4ac35ccff/Microsoft_VC90_CRT_x86.msm',
- 'size': 615424,
- 'sha256': '837e887ef31b332feb58156f429389de345cb94504228bb9a523c25a9dd3d75e',
- },
- 'vc9-crt-x86-msm-policy': {
- 'url': 'https://github.com/indygreg/vc90-merge-modules/raw/9232f8f0b2135df619bf7946eaa176b4ac35ccff/policy_9_0_Microsoft_VC90_CRT_x86.msm',
- 'size': 71168,
- 'sha256': '3fbcf92e3801a0757f36c5e8d304e134a68d5cafd197a6df7734ae3e8825c940',
- },
- 'vc9-crt-x64-msm': {
- 'url': 'https://github.com/indygreg/vc90-merge-modules/raw/9232f8f0b2135df619bf7946eaa176b4ac35ccff/Microsoft_VC90_CRT_x86_x64.msm',
- 'size': 662528,
- 'sha256': '50d9639b5ad4844a2285269c7551bf5157ec636e32396ddcc6f7ec5bce487a7c',
- },
- 'vc9-crt-x64-msm-policy': {
- 'url': 'https://github.com/indygreg/vc90-merge-modules/raw/9232f8f0b2135df619bf7946eaa176b4ac35ccff/policy_9_0_Microsoft_VC90_CRT_x86_x64.msm',
- 'size': 71168,
- 'sha256': '0550ea1929b21239134ad3a678c944ba0f05f11087117b6cf0833e7110686486',
- },
- 'virtualenv': {
- 'url': 'https://files.pythonhosted.org/packages/37/db/89d6b043b22052109da35416abc3c397655e4bd3cff031446ba02b9654fa/virtualenv-16.4.3.tar.gz',
- 'size': 3713208,
- 'sha256': '984d7e607b0a5d1329425dd8845bd971b957424b5ba664729fab51ab8c11bc39',
- 'version': '16.4.3',
- },
- 'wix': {
- 'url': 'https://github.com/wixtoolset/wix3/releases/download/wix3111rtm/wix311-binaries.zip',
- 'size': 34358269,
- 'sha256': '37f0a533b0978a454efb5dc3bd3598becf9660aaf4287e55bf68ca6b527d051d',
- 'version': '3.11.1',
- },
}
diff --git a/contrib/packaging/hgpackaging/cli.py b/contrib/packaging/hgpackaging/cli.py
--- a/contrib/packaging/hgpackaging/cli.py
+++ b/contrib/packaging/hgpackaging/cli.py
@@ -20,13 +20,7 @@
SOURCE_DIR = HERE.parent.parent.parent
-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")
-
+def build_inno(pyoxidizer_target, iscc=None, version=None):
if iscc:
iscc = pathlib.Path(iscc)
else:
@@ -38,59 +32,30 @@
build_dir = SOURCE_DIR / "build"
- 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,
- )
+ inno.build_with_pyoxidizer(
+ SOURCE_DIR, build_dir, pyoxidizer_target, iscc, version=version
+ )
def build_wix(
+ pyoxidizer_target,
name=None,
- pyoxidizer_target=None,
- python=None,
version=None,
sign_sn=None,
sign_cert=None,
sign_password=None,
sign_timestamp_url=None,
- extra_packages_script=None,
extra_wxs=None,
extra_features=None,
extra_pyoxidizer_vars=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")
-
kwargs = {
"source_dir": SOURCE_DIR,
"version": version,
+ "target_triple": pyoxidizer_target,
+ "extra_pyoxidizer_vars": extra_pyoxidizer_vars,
}
- if pyoxidizer_target:
- fn = wix.build_installer_pyoxidizer
- kwargs["target_triple"] = pyoxidizer_target
- kwargs["extra_pyoxidizer_vars"] = extra_pyoxidizer_vars
- 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(
thing.split("=") for thing in extra_wxs.split(",")
@@ -107,7 +72,7 @@
"timestamp_url": sign_timestamp_url,
}
- fn(**kwargs)
+ wix.build_installer_pyoxidizer(**kwargs)
def get_parser():
@@ -119,9 +84,9 @@
sp.add_argument(
"--pyoxidizer-target",
choices={"i686-pc-windows-msvc", "x86_64-pc-windows-msvc"},
+ required=True,
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",
@@ -137,9 +102,9 @@
sp.add_argument(
"--pyoxidizer-target",
choices={"i686-pc-windows-msvc", "x86_64-pc-windows-msvc"},
+ required=True,
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 "
@@ -155,12 +120,6 @@
)
sp.add_argument("--version", help="Version string to use")
sp.add_argument(
- "--extra-packages-script",
- help=(
- "Script to execute to include extra packages in " "py2exe binary."
- ),
- )
- sp.add_argument(
"--extra-wxs", help="CSV of path_to_wxs_file=working_dir_for_wxs_file"
)
sp.add_argument(
To: indygreg, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
More information about the Mercurial-devel
mailing list