Mercurial > hg-stable
view contrib/packaging/hgpackaging/wix.py @ 41929:9d4ae5044b4c
wix: remove enum and future packages
These were cargo culted from the THG installer code. I'm not sure
what needs them in THG land. But the official MSIs certainly do
not - at least not as direct dependencies.
.. bc::
The Windows MSI installers no longer include the enum and future
Python packages.
Differential Revision: https://phab.mercurial-scm.org/D6101
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Fri, 08 Mar 2019 10:29:48 -0800 |
parents | c569f769c41d |
children | b83de9150c1c |
line wrap: on
line source
# wix.py - WiX installer functionality # # Copyright 2019 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 re import subprocess from .downloads import ( download_entry, ) from .py2exe import ( build_py2exe, ) from .util import ( extract_zip_to_directory, sign_with_signtool, ) SUPPORT_WXS = [ ('contrib.wxs', r'contrib'), ('dist.wxs', r'dist'), ('doc.wxs', r'doc'), ('help.wxs', r'mercurial\help'), ('i18n.wxs', r'i18n'), ('locale.wxs', r'mercurial\locale'), ('templates.wxs', r'mercurial\templates'), ] EXTRA_PACKAGES = { 'distutils', 'pygments', } 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 normalize_version(version): """Normalize Mercurial version string so WiX accepts it. Version strings have to be numeric X.Y.Z. """ if '+' in version: version, extra = version.split('+', 1) else: extra = None # 4.9rc0 if version[:-1].endswith('rc'): version = version[:-3] versions = [int(v) for v in version.split('.')] while len(versions) < 3: versions.append(0) major, minor, build = versions[:3] if extra: # <commit count>-<hash>+<date> build = int(extra.split('-')[0]) return '.'.join('%d' % x for x in (major, minor, build)) 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_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 build_installer(source_dir: pathlib.Path, python_exe: pathlib.Path, msi_name='mercurial', version=None, post_build_fn=None): """Build a WiX MSI installer. ``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. """ arch = 'x64' if r'\x64' in os.environ.get('LIB', '') else 'x86' hg_build_dir = source_dir / 'build' dist_dir = source_dir / 'dist' requirements_txt = (source_dir / 'contrib' / 'packaging' / 'wix' / 'requirements.txt') build_py2exe(source_dir, hg_build_dir, python_exe, 'wix', requirements_txt, extra_packages=EXTRA_PACKAGES) version = version or normalize_version(find_version(source_dir)) print('using version string: %s' % version) if post_build_fn: post_build_fn(source_dir, hg_build_dir, dist_dir, version) build_dir = hg_build_dir / ('wix-%s' % arch) build_dir.mkdir(exist_ok=True) wix_pkg, wix_entry = download_entry('wix', hg_build_dir) wix_path = hg_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) source_build_rel = pathlib.Path(os.path.relpath(source_dir, build_dir)) defines = {'Platform': arch} for wxs, rel_path in SUPPORT_WXS: wxs = source_dir / 'contrib' / 'packaging' / 'wix' / wxs wxs_source_dir = source_dir / rel_path run_candle(wix_path, build_dir, wxs, wxs_source_dir, defines=defines) source = source_dir / 'contrib' / 'packaging' / 'wix' / 'mercurial.wxs' defines['Version'] = version defines['Comments'] = 'Installs Mercurial version %s' % version defines['VCRedistSrcDir'] = str(hg_build_dir) run_candle(wix_path, build_dir, source, source_build_rel, defines=defines) msi_path = source_dir / 'dist' / ( '%s-%s-%s.msi' % (msi_name, version, arch)) args = [ str(wix_path / 'light.exe'), '-nologo', '-ext', 'WixUIExtension', '-sw1076', '-spdb', '-o', str(msi_path), ] for source, rel_path in SUPPORT_WXS: assert source.endswith('.wxs') args.append(str(build_dir / ('%s.wixobj' % source[:-4]))) args.append(str(build_dir / 'mercurial.wixobj')) subprocess.run(args, cwd=str(source_dir), check=True) print('%s created' % msi_path) 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): """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) 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)