Patchwork D7159: packaging: stage installed files for Inno

login
register
mail settings
Submitter phabricator
Date Nov. 9, 2019, 12:05 a.m.
Message ID <1f8996d9e3200a8af41e18262c47cd5b@localhost.localdomain>
Download mbox | patch
Permalink /patch/42991/
State Not Applicable
Headers show

Comments

phabricator - Nov. 9, 2019, 12:05 a.m.
Closed by commit rHGd2ea31a2546d: packaging: stage installed files for Inno (authored by indygreg).
This revision was automatically updated to reflect the committed changes.
This revision was not accepted when it landed; it landed in state "Needs Review".

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D7159?vs=17380&id=17811

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7159/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7159

AFFECTED FILES
  contrib/packaging/hgpackaging/inno.py
  contrib/packaging/hgpackaging/py2exe.py
  contrib/packaging/hgpackaging/util.py
  contrib/packaging/inno/mercurial.iss

CHANGE DETAILS




To: indygreg, #hg-reviewers
Cc: mercurial-devel

Patch

diff --git a/contrib/packaging/inno/mercurial.iss b/contrib/packaging/inno/mercurial.iss
--- a/contrib/packaging/inno/mercurial.iss
+++ b/contrib/packaging/inno/mercurial.iss
@@ -33,8 +33,8 @@ 
 AppVerName=Mercurial {#VERSION}
 OutputBaseFilename=Mercurial-{#VERSION}
 #endif
-InfoAfterFile=contrib/win32/postinstall.txt
-LicenseFile=COPYING
+InfoAfterFile=../postinstall.txt
+LicenseFile=Copying.txt
 ShowLanguageDialog=yes
 AppPublisher=Matt Mackall and others
 AppPublisherURL=https://mercurial-scm.org/
@@ -43,49 +43,23 @@ 
 {{ 'AppID={{4B95A5F1-EF59-4B08-BED8-C891C46121B3}' }}
 AppContact=mercurial@mercurial-scm.org
 DefaultDirName={pf}\Mercurial
-SourceDir=..\..
+SourceDir=stage
 VersionInfoDescription=Mercurial distributed SCM (version {#VERSION})
 VersionInfoCopyright=Copyright 2005-2019 Matt Mackall and others
 VersionInfoCompany=Matt Mackall and others
 InternalCompressLevel=max
 SolidCompression=true
-SetupIconFile=contrib\win32\mercurial.ico
+SetupIconFile=../mercurial.ico
 AllowNoIcons=true
 DefaultGroupName=Mercurial
 PrivilegesRequired=none
 ChangesEnvironment=true
 
 [Files]
-Source: contrib\mercurial.el; DestDir: {app}/Contrib
-Source: contrib\vim\*.*; DestDir: {app}/Contrib/Vim
-Source: contrib\zsh_completion; DestDir: {app}/Contrib
-Source: contrib\bash_completion; DestDir: {app}/Contrib
-Source: contrib\tcsh_completion; DestDir: {app}/Contrib
-Source: contrib\tcsh_completion_build.sh; DestDir: {app}/Contrib
-Source: contrib\hgk; DestDir: {app}/Contrib; DestName: hgk.tcl
-Source: contrib\xml.rnc; DestDir: {app}/Contrib
-Source: contrib\mercurial.el; DestDir: {app}/Contrib
-Source: contrib\mq.el; DestDir: {app}/Contrib
-Source: contrib\hgweb.fcgi; DestDir: {app}/Contrib
-Source: contrib\hgweb.wsgi; DestDir: {app}/Contrib
-Source: contrib\win32\ReadMe.html; DestDir: {app}; Flags: isreadme
-Source: contrib\win32\postinstall.txt; DestDir: {app}; DestName: ReleaseNotes.txt
-Source: dist\hg.exe; DestDir: {app}; AfterInstall: Touch('{app}\hg.exe.local')
-Source: dist\lib\*.dll; Destdir: {app}\lib
-Source: dist\lib\*.pyd; Destdir: {app}\lib
-Source: dist\python*.dll; Destdir: {app}; Flags: skipifsourcedoesntexist
-Source: dist\msvc*.dll; DestDir: {app}; Flags: skipifsourcedoesntexist
-Source: dist\Microsoft.VC*.CRT.manifest; DestDir: {app}; Flags: skipifsourcedoesntexist
-Source: dist\lib\library.zip; DestDir: {app}\lib
-Source: doc\*.html; DestDir: {app}\Docs
-Source: doc\style.css; DestDir: {app}\Docs
-Source: mercurial\help\*.txt; DestDir: {app}\help
-Source: mercurial\help\internals\*.txt; DestDir: {app}\help\internals
-Source: mercurial\default.d\*.rc; DestDir: {app}\default.d
-Source: mercurial\locale\*.*; DestDir: {app}\locale; Flags: recursesubdirs createallsubdirs skipifsourcedoesntexist
-Source: mercurial\templates\*.*; DestDir: {app}\Templates; Flags: recursesubdirs createallsubdirs
-Source: CONTRIBUTORS; DestDir: {app}; DestName: Contributors.txt
-Source: COPYING; DestDir: {app}; DestName: Copying.txt
+{% for entry in package_files -%}
+Source: {{ entry.source }}; DestDir: {{ entry.dest_dir }}
+{%- if entry.metadata %}; {{ entry.metadata }}{% endif %}
+{% endfor %}
 
 [INI]
 Filename: {app}\Mercurial.url; Section: InternetShortcut; Key: URL; String: https://mercurial-scm.org/
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
@@ -9,8 +9,10 @@ 
 
 import distutils.version
 import getpass
+import glob
 import os
 import pathlib
+import shutil
 import subprocess
 import tarfile
 import zipfile
@@ -164,3 +166,47 @@ 
         'version': version,
         'py3': version >= distutils.version.LooseVersion('3'),
     }
+
+
+def process_install_rules(
+    rules: list, source_dir: pathlib.Path, dest_dir: pathlib.Path
+):
+    for source, dest in rules:
+        if '*' in source:
+            if not dest.endswith('/'):
+                raise ValueError('destination must end in / when globbing')
+
+            # We strip off the source path component before the first glob
+            # character to construct the relative install path.
+            prefix_end_index = source[: source.index('*')].rindex('/')
+            relative_prefix = source_dir / source[0:prefix_end_index]
+
+            for res in glob.glob(str(source_dir / source), recursive=True):
+                source_path = pathlib.Path(res)
+
+                if source_path.is_dir():
+                    continue
+
+                rel_path = source_path.relative_to(relative_prefix)
+
+                dest_path = dest_dir / dest[:-1] / rel_path
+
+                dest_path.parent.mkdir(parents=True, exist_ok=True)
+                print('copying %s to %s' % (source_path, dest_path))
+                shutil.copy(source_path, dest_path)
+
+        # Simple file case.
+        else:
+            source_path = pathlib.Path(source)
+
+            if dest.endswith('/'):
+                dest_path = pathlib.Path(dest) / source_path.name
+            else:
+                dest_path = pathlib.Path(dest)
+
+            full_source_path = source_dir / source_path
+            full_dest_path = dest_dir / dest_path
+
+            full_dest_path.parent.mkdir(parents=True, exist_ok=True)
+            shutil.copy(full_source_path, full_dest_path)
+            print('copying %s to %s' % (full_source_path, full_dest_path))
diff --git a/contrib/packaging/hgpackaging/py2exe.py b/contrib/packaging/hgpackaging/py2exe.py
--- a/contrib/packaging/hgpackaging/py2exe.py
+++ b/contrib/packaging/hgpackaging/py2exe.py
@@ -15,10 +15,43 @@ 
 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/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', 'Docs/'),
+    ('doc/style.css', 'Docs/'),
+    ('mercurial/help/**/*.txt', 'help/'),
+    ('mercurial/default.d/*.rc', 'default.d/'),
+    ('mercurial/locale/**/*', 'locale/'),
+    ('mercurial/templates/**/*', 'Templates/'),
+    ('CONTRIBUTORS', 'Contributors.txt'),
+    ('COPYING', 'Copying.txt'),
+]
+
+
 def build_py2exe(
     source_dir: pathlib.Path,
     build_dir: pathlib.Path,
@@ -169,3 +202,12 @@ 
         env=env,
         check=True,
     )
+
+
+def stage_install(source_dir: pathlib.Path, staging_dir: pathlib.Path):
+    """Copy all files to be installed to a directory.
+
+    This allows packaging to simply walk a directory tree to find source
+    files.
+    """
+    process_install_rules(STAGING_RULES, source_dir, staging_dir)
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,7 +14,10 @@ 
 
 import jinja2
 
-from .py2exe import build_py2exe
+from .py2exe import (
+    build_py2exe,
+    stage_install,
+)
 from .util import find_vc_runtime_files
 
 EXTRA_PACKAGES = {
@@ -24,6 +27,11 @@ 
     'win32ctypes',
 }
 
+PACKAGE_FILES_METADATA = {
+    'ReadMe.html': 'Flags: isreadme',
+    'hg.exe': "AfterInstall: Touch('{app}\\hg.exe.local')",
+}
+
 
 def build(
     source_dir: pathlib.Path,
@@ -47,6 +55,7 @@ 
     arch = 'x64' if vc_x64 else 'x86'
     inno_source_dir = source_dir / 'contrib' / 'packaging' / 'inno'
     inno_build_dir = build_dir / ('inno-%s' % arch)
+    staging_dir = inno_build_dir / 'stage'
 
     requirements_txt = (
         source_dir / 'contrib' / 'packaging' / 'inno' / 'requirements.txt'
@@ -63,6 +72,15 @@ 
         extra_packages=EXTRA_PACKAGES,
     )
 
+    # 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)
+
     # hg.exe depends on VC9 runtime DLLs. Copy those into place.
     for f in find_vc_runtime_files(vc_x64):
         if f.name.endswith('.manifest'):
@@ -70,11 +88,34 @@ 
         else:
             basename = f.name
 
-        dest_path = source_dir / 'dist' / basename
+        dest_path = staging_dir / basename
 
         print('copying %s to %s' % (f, dest_path))
         shutil.copyfile(f, dest_path)
 
+    # The final package layout is simply a mirror of the staging directory.
+    package_files = []
+    for root, dirs, files in os.walk(staging_dir):
+        dirs.sort()
+
+        root = pathlib.Path(root)
+
+        for f in sorted(files):
+            full = root / f
+            rel = full.relative_to(staging_dir)
+            if str(rel.parent) == '.':
+                dest_dir = '{app}'
+            else:
+                dest_dir = '{app}\\%s' % rel.parent
+
+            package_files.append(
+                {
+                    'source': rel,
+                    'dest_dir': dest_dir,
+                    'metadata': PACKAGE_FILES_METADATA.get(str(rel), None),
+                }
+            )
+
     print('creating installer')
 
     # Install Inno files by rendering a template.
@@ -93,11 +134,17 @@ 
             % (e.name, e.lineno, e.message,)
         )
 
-    content = template.render()
+    content = template.render(package_files=package_files)
 
     with (inno_build_dir / 'mercurial.iss').open('w', encoding='utf-8') as fh:
         fh.write(content)
 
+    # Copy additional files used by Inno.
+    for p in ('mercurial.ico', 'postinstall.txt'):
+        shutil.copyfile(
+            source_dir / 'contrib' / 'win32' / p, inno_build_dir / p
+        )
+
     args = [str(iscc_exe)]
 
     if vc_x64: