diff --git a/builders/windows-builder.py b/builders/windows-builder.py index aa85a82..818fe51 100644 --- a/builders/windows-builder.py +++ b/builders/windows-builder.py @@ -97,6 +97,7 @@ Portable App Builds import os import glob +import re import sys from distutils import dir_util from hashlib import md5 @@ -109,11 +110,51 @@ from lxml.objectify import fromstring from builder import Builder +PEP440 = re.compile(r'(?P\d)\.(?P\d)(\.(?P\d))?((?P
a|b|rc)(?P\d))?')
+
+
 class WindowsBuilder(Builder):
     """
     The :class:`WindowsBuilder` class encapsulates everything that is needed
     to build a Windows installer.
     """
+    # Make mypy happy
+    program_files: str
+    candle_exe: str
+    light_exe: str
+    htmlhelp_exe: str
+    mutool_exe: str
+    dist_path: str
+    icon_path: str
+    license_path: str
+    portable_source_path: str
+    portable_dest_path: str
+    portablelauncher_exe: str
+    portableinstaller_exe: str
+
+    def _pep440_to_windows_version(self, version: str) -> str:
+        """Convert a PEP440-compatible version string to a Windows version string"""
+        if m := PEP440.match(version):
+            groups = m.groupdict()
+            if not groups.get('fix'):
+                groups['fix'] = '0'
+            build_number = 5000
+            if groups.get('pre'):
+                if groups['pre'] == 'a':
+                    build_number = 1000
+                elif groups['pre'] == 'b':
+                    build_number = 2000
+                elif groups['pre'] == 'rc':
+                    build_number = 3000
+            if groups.get('rel'):
+                try:
+                    rel = int(groups['rel'])
+                    build_number += rel
+                except ValueError:
+                    pass
+            return f'{groups["major"]}.{groups["minor"]}.{groups["fix"]}.{build_number}'
+        else:
+            return '0.0.0.0'
 
     def _walk_dirs(self, dir_dict, path):
         """
@@ -214,12 +255,11 @@ class WindowsBuilder(Builder):
         self._print_verbose('Reading base WiX file')
         with open(os.path.join(config_dir, 'OpenLP-base.wxs'), 'rt') as base_file:
             xml = base_file.read()
-        # convert the version string to format x.x.x if needed
         if '.dev' in self.version:
             windows_version = self.version.replace('.dev', '.')
             windows_version = windows_version.rsplit('+', 1)[0]
         else:
-            windows_version = self.version
+            windows_version = self._pep440_to_windows_version(self.version)
         xml = xml % {
             'dialog': os.path.join(config_dir, 'WizardMain.bmp'),
             'banner': os.path.join(config_dir, 'WizardBanner.bmp'),
@@ -395,7 +435,7 @@ class WindowsBuilder(Builder):
         super().setup_system_paths()
         self.python_root = os.path.dirname(self.python)
         self.site_packages = os.path.join(self.python_root, 'Lib', 'site-packages')
-        self.program_files = os.getenv('PROGRAMFILES')
+        self.program_files = os.environ['PROGRAMFILES']
         self.program_files_x86 = os.getenv('PROGRAMFILES(x86)')
         self._print_verbose('   {:.<20}: {}'.format('site packages: ', self.site_packages))
         self._print_verbose('   {:.<20}: {}'.format('program files: ', self.program_files))
diff --git a/tests/test_version.py b/tests/test_version.py
new file mode 100644
index 0000000..8e71847
--- /dev/null
+++ b/tests/test_version.py
@@ -0,0 +1,35 @@
+import re
+import pytest
+
+PEP440 = re.compile(r'(?P\d)\.(?P\d)(\.(?P\d))?((?P
a|b|rc)(?P\d))?')
+
+
+def _pep440_to_windows_version(version: str) -> str:
+    """Convert a PEP440-compatible version string to a Windows version string"""
+    if m := PEP440.match(version):
+        groups = m.groupdict()
+        if not groups.get('fix'):
+            groups['fix'] = '0'
+        build_number = 5000
+        if groups.get('pre'):
+            if groups['pre'] == 'a':
+                build_number = 1000
+            elif groups['pre'] == 'b':
+                build_number = 2000
+            elif groups['pre'] == 'rc':
+                build_number = 3000
+        if groups.get('rel'):
+            try:
+                rel = int(groups['rel'])
+                build_number += rel
+            except ValueError:
+                pass
+        return f'{groups["major"]}.{groups["minor"]}.{groups["fix"]}.{build_number}'
+    else:
+        return '0.0.0.0'
+
+
+@pytest.mark.parametrize('pep440_version, windows_version', [
+    ('2.9.1', '2.9.1.5000'), ('3.1rc1', '3.1.0.3001'), ('3.0.2b2', '3.0.2.2002'), ('1.9a1', '1.9.0.1001')])
+def test_pep440_to_windows_version(pep440_version, windows_version):
+    assert _pep440_to_windows_version(pep440_version) == windows_version