instsetoo_native/CustomTarget_install.mk                             |    3 
 instsetoo_native/inc_common/windows/msi_templates/Binary/Image_2.bmp |binary
 msicreator/create_installer.py                                       |   84 +
 msicreator/createmsi.py                                              |  605 
++++++++++
 4 files changed, 692 insertions(+)

New commits:
commit 9aee0383c3ebc3f267c99e19764728ba09c12d3a
Author:     Rakielle <rakielle...@gmail.com>
AuthorDate: Sun Feb 12 21:42:00 2023 +0100
Commit:     Thorsten Behrens <thorsten.behr...@allotropia.de>
CommitDate: Sat Apr 22 04:52:55 2023 +0200

    Integrate msicreator into LO and generate installer
    
    Co-authored-by: Ximena Alcaman <alcamanxim...@gmail.com>
    
    Change-Id: Iea2ea3b4bddc975a032592403727a4ff00db4a5d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146843
    Tested-by: Jenkins
    Reviewed-by: Thorsten Behrens <thorsten.behr...@allotropia.de>

diff --git a/instsetoo_native/CustomTarget_install.mk 
b/instsetoo_native/CustomTarget_install.mk
index 29dcb4b0b1cd..e4c0f7fcb9e7 100644
--- a/instsetoo_native/CustomTarget_install.mk
+++ b/instsetoo_native/CustomTarget_install.mk
@@ -74,6 +74,8 @@ instsetoo_installer_targets := $(foreach 
pkgformat,$(PKGFORMAT),\
             $(foreach lang,$(filter-out 
en-US,$(gb_WITH_LANG)),ooolangpack‧$(lang)‧‧-languagepack‧$(pkgformat)‧nostrip)))
 endif
 
+LIBO_VERSION = 
$(LIBO_VERSION_MAJOR).$(LIBO_VERSION_MINOR).$(LIBO_VERSION_MICRO).$(LIBO_VERSION_PATCH)
+
 instsetoo_wipe:
        $(call gb_Output_announce,wiping installation output dir,$(true),WPE,6)
        rm -rf $(instsetoo_OUT)
@@ -113,6 +115,7 @@ $(instsetoo_installer_targets): 
$(SRCDIR)/solenv/bin/make_installer.pl \
 $(call gb_CustomTarget_get_workdir,instsetoo_native/install)/install.phony: 
$(instsetoo_installer_targets)
        $(call gb_Output_announce,$(subst $(WORKDIR)/,,$@),$(true),PRL,2)
        $(call gb_Trace_StartRange,$(subst $(WORKDIR)/,,$@),PRL)
+       $(if $(LODE_HOME),$(call gb_ExternalExecutable_get_command,python) 
$(SRCDIR)/msicreator/create_installer.py $(BUILDDIR) $(SRCDIR) $(LIBO_VERSION) 
$(PRODUCTNAME_WITHOUT_SPACES))
 ifeq (TRUE,$(LIBO_TEST_INSTALL))
        unzip -q -d $(TESTINSTALLDIR) 
$(instsetoo_OUT)/$(PRODUCTNAME_WITHOUT_SPACES)/archive/install/en-US/LibreOffice*_archive.zip
        mv $(TESTINSTALLDIR)/LibreOffice*_archive/LibreOffice*/* 
$(TESTINSTALLDIR)/
diff --git 
a/instsetoo_native/inc_common/windows/msi_templates/Binary/Image_2.bmp 
b/instsetoo_native/inc_common/windows/msi_templates/Binary/Image_2.bmp
new file mode 100644
index 000000000000..b3795cadea7d
Binary files /dev/null and 
b/instsetoo_native/inc_common/windows/msi_templates/Binary/Image_2.bmp differ
diff --git a/msicreator/create_installer.py b/msicreator/create_installer.py
new file mode 100644
index 000000000000..01394a48c4a3
--- /dev/null
+++ b/msicreator/create_installer.py
@@ -0,0 +1,84 @@
+import os, sys
+from shutil import copytree, copy2, move, rmtree
+import json
+import createmsi
+
+build_dir = sys.argv[1]
+src_dir = sys.argv[2]
+creator_dir = os.path.join(build_dir, 'workdir/installation/MSICreatorLO')
+
+def prepare_project_dir():
+    instdir = os.path.join(build_dir, 'instdir')
+    fonts_dir = os.path.join(instdir, 'share/fonts')
+    new_fonts_dir = os.path.join(creator_dir, 'libo-fonts/share/fonts')
+    main_dir = os.path.join(creator_dir, 'main')
+    src_uninstaller_icon = os.path.join(src_dir, 
'icon-themes/colibre/res/mainapp_48_8.png')
+    src_ui_banner = os.path.join(src_dir, 
'instsetoo_native/inc_common/windows/msi_templates/Binary/Banner.bmp')
+    src_ui_background = os.path.join(src_dir, 
'instsetoo_native/inc_common/windows/msi_templates/Binary/Image_2.bmp')
+    graphics_dir = os.path.join(creator_dir, 'graphics')
+    sdk_dir = os.path.join(creator_dir, 'main/sdk')
+    try:
+        move(fonts_dir, new_fonts_dir)
+        copytree(instdir, main_dir, dirs_exist_ok=True)
+        copy2(src_uninstaller_icon, creator_dir)
+        os.mkdir(graphics_dir)
+        copy2(src_ui_banner, graphics_dir)
+        copy2(src_ui_background, graphics_dir)
+        rmtree(sdk_dir)
+    except FileExistsError as err:
+        print(err)
+
+def create_creator_json():
+    lo_version = sys.argv[3]
+    lo_name = sys.argv[4]
+    uninstaller_icon = 'mainapp_48_8.png'
+    lo_dictionary = {
+        "upgrade_guid": "6f05ed48-a735-4155-ab60-e4cc98455262",
+        "version": lo_version,
+        "product_name": lo_name,
+        "manufacturer": "The Document Foundation",
+        "name": lo_name,
+        "name_base": lo_name,
+        "comments": "Testing a libo installer",
+        "installdir": "LibreOffice Test",
+        "startmenu_shortcut": "program/soffice.exe",
+        "desktop_shortcut": "program/soffice.exe",
+        "addremove_icon": uninstaller_icon,
+        "major_upgrade": {
+            "AllowDowngrades": "yes",
+            "IgnoreRemoveFailure": "yes"
+        },
+        "graphics": {
+            "banner": "graphics/Banner.bmp",
+            "background": "graphics/Image_2.bmp"
+        },
+        "parts": [
+            {
+                "id": "libreoffice",
+                "title": "The LibreOffice Suite",
+                "description": "This is a test for the LibreOffice installer",
+                "absent": "disallow",
+                "staged_dir": "main"
+            },
+            {
+                "id": "libreofficefonts",
+                "title": "The LibreOffice Fonts ",
+                "description": "This is a test for the LibreOffice Fonts",
+                "absent": "allow",
+                "staged_dir": "libo-fonts"
+            }
+        ]
+    }
+
+    lo_object = json.dumps(lo_dictionary, indent=4)
+    with open(os.path.join(creator_dir, 'lo.json'), 'w') as lo_json:
+        lo_json.write(lo_object)
+
+def generate_installer():
+    os.chdir(creator_dir)
+    createmsi.run(['lo.json'])
+
+if __name__ == '__main__':
+    prepare_project_dir()
+    create_creator_json()
+    generate_installer()
diff --git a/msicreator/createmsi.py b/msicreator/createmsi.py
new file mode 100644
index 000000000000..3a25c56c266e
--- /dev/null
+++ b/msicreator/createmsi.py
@@ -0,0 +1,605 @@
+#!/usr/bin/env python3
+
+# Copyright 2017-2018 Jussi Pakkanen et al
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sys, os, subprocess, shutil, uuid, json, re
+from glob import glob
+import platform
+import xml.etree.ElementTree as ET
+
+sys.path.append(os.getcwd())
+
+def gen_guid():
+    return str(uuid.uuid4()).upper()
+
+class Node:
+    def __init__(self, dirs, files):
+        assert(isinstance(dirs, list))
+        assert(isinstance(files, list))
+        self.dirs = dirs
+        self.files = files
+
+class UIGraphics:
+    def __init__(self):
+        self.banner = None
+        self.background = None
+
+class PackageGenerator:
+
+    def __init__(self, jsonfile):
+        jsondata = json.load(open(jsonfile, 'rb'))
+        self.product_name = jsondata['product_name']
+        self.manufacturer = jsondata['manufacturer']
+        self.version = jsondata['version']
+        self.comments = jsondata['comments']
+        self.installdir = jsondata['installdir']
+        self.license_file = jsondata.get('license_file', None)
+        self.name = jsondata['name']
+        self.guid = jsondata.get('product_guid', '*')
+        self.upgrade_guid = jsondata['upgrade_guid']
+        self.basename = jsondata['name_base']
+        self.need_msvcrt = jsondata.get('need_msvcrt', False)
+        self.addremove_icon = jsondata.get('addremove_icon', None)
+        self.startmenu_shortcut = jsondata.get('startmenu_shortcut', None)
+        self.desktop_shortcut = jsondata.get('desktop_shortcut', None)
+        self.main_xml = self.basename + '.wxs'
+        self.main_o = self.basename + '.wixobj'
+        self.idnum = 0
+        self.graphics = UIGraphics()
+        if 'graphics' in jsondata:
+            if 'banner' in jsondata['graphics']:
+                self.graphics.banner = jsondata['graphics']['banner']
+            if 'background' in jsondata['graphics']:
+                self.graphics.background = jsondata['graphics']['background']
+        if 'arch' in jsondata:
+            self.arch = jsondata['arch']
+        else:
+            # rely on the environment variable since python architecture may 
not be the same as system architecture
+            if 'PROGRAMFILES(X86)' in os.environ:
+                self.arch = 64
+            else:
+                self.arch = 32 if '32' in platform.architecture()[0] else 64
+        self.final_output = '%s-%s-%d.msi' % (self.basename, self.version, 
self.arch)
+        if self.arch == 64:
+            self.progfile_dir = 'ProgramFiles64Folder'
+            if platform.system() == "Windows":
+                redist_glob = 'C:\\Program Files\\Microsoft Visual 
Studio\\*\\*\\VC\\Redist\\MSVC\\v*\\MergeModules\\Microsoft_VC*_CRT_x64.msm'
+            else:
+                redist_glob = 
'/usr/share/msicreator/Microsoft_VC141_CRT_x64.msm'
+        else:
+            self.progfile_dir = 'ProgramFilesFolder'
+            if platform.system() == "Windows":
+                redist_glob = 'C:\\Program Files\\Microsoft Visual 
Studio\\*\\Community\\VC\\Redist\\MSVC\\*\\MergeModules\\Microsoft_VC*_CRT_x86.msm'
+            else:
+                redist_glob = 
'/usr/share/msicreator/Microsoft_VC141_CRT_x86.msm'
+        trials = glob(redist_glob)
+        if self.need_msvcrt:
+            if len(trials) > 1:
+                sys.exit('There are more than one redist dirs: ' +
+                         ', '.join(trials))
+            if len(trials) == 0:
+                sys.exit('No redist dirs were detected, install MSM 
redistributables with VS installer.')
+            self.redist_path = trials[0]
+        self.component_num = 0
+        self.registry_entries = jsondata.get('registry_entries', None)
+        self.major_upgrade = jsondata.get('major_upgrade', None)
+        self.parts = jsondata['parts']
+        self.feature_components = {}
+        self.feature_properties = {}
+
+    def generate_files(self):
+        self.root = ET.Element('Wix', {'xmlns': 
'http://schemas.microsoft.com/wix/2006/wi'})
+        product = ET.SubElement(self.root, 'Product', {
+            'Name': self.product_name,
+            'Manufacturer': self.manufacturer,
+            'Id': self.guid,
+            'UpgradeCode': self.upgrade_guid,
+            'Language': '1033',
+            'Codepage':  '1252',
+            'Version': self.version,
+        })
+
+        package = ET.SubElement(product, 'Package',  {
+            'Id': '*',
+            'Keywords': 'Installer',
+            'Description': '%s %s installer' % (self.name, self.version),
+            'Comments': self.comments,
+            'Manufacturer': self.manufacturer,
+            'InstallerVersion': '500',
+            'Languages': '1033',
+            'Compressed': 'yes',
+            'SummaryCodepage': '1252',
+        })
+
+        if self.major_upgrade is not None:
+            majorupgrade = ET.SubElement(product, 'MajorUpgrade', {})
+            for mkey in self.major_upgrade.keys():
+                majorupgrade.set(mkey, self.major_upgrade[mkey])
+        else:
+            ET.SubElement(product, 'MajorUpgrade', {'DowngradeErrorMessage': 
'A newer version of %s is already installed.' % self.name})
+        if self.arch == 64:
+            package.set('Platform', 'x64')
+        ET.SubElement(product, 'Media', {
+            'Id': '1',
+            'Cabinet': self.basename + '.cab',
+            'EmbedCab': 'yes',
+        })
+        targetdir = ET.SubElement(product, 'Directory', {
+            'Id': 'TARGETDIR',
+            'Name': 'SourceDir',
+        })
+        progfiledir = ET.SubElement(targetdir, 'Directory', {
+            'Id': self.progfile_dir,
+        })
+        pmf = ET.SubElement(targetdir, 'Directory', {'Id': 
'ProgramMenuFolder'},)
+        if self.startmenu_shortcut is not None:
+            ET.SubElement(pmf, 'Directory', {
+                'Id': 'ApplicationProgramsFolder',
+                'Name': self.product_name,
+            })
+        if self.desktop_shortcut is not None:
+            ET.SubElement(pmf, 'Directory', {'Id': 'DesktopFolder',
+                                             'Name': 'Desktop',
+            })
+        installdir = ET.SubElement(progfiledir, 'Directory', {
+            'Id': 'INSTALLDIR',
+            'Name': self.installdir,
+        })
+        if self.need_msvcrt:
+            ET.SubElement(installdir, 'Merge', {
+                'Id': 'VCRedist',
+                'SourceFile': self.redist_path,
+                'DiskId': '1',
+                'Language': '0',
+            })
+
+        if self.startmenu_shortcut is not None:
+            ap = ET.SubElement(product, 'DirectoryRef', {'Id': 
'ApplicationProgramsFolder'})
+            comp = ET.SubElement(ap, 'Component', {'Id': 'ApplicationShortcut',
+                                                   'Guid': gen_guid(),
+                                                   })
+            ET.SubElement(comp, 'Shortcut', {'Id': 
'ApplicationStartMenuShortcut',
+                                             'Name': self.product_name,
+                                             'Description': self.comments,
+                                             'Target': '[INSTALLDIR]' + 
self.startmenu_shortcut,
+                                             'WorkingDirectory': 'INSTALLDIR',
+            })
+            ET.SubElement(comp, 'RemoveFolder', {'Id': 
'RemoveApplicationProgramsFolder',
+                                                 'Directory': 
'ApplicationProgramsFolder',
+                                                 'On': 'uninstall',
+                                                 })
+            ET.SubElement(comp, 'RegistryValue', {'Root': 'HKCU',
+                                                  'Key': 
'Software\\Microsoft\\' + self.name,
+                                                  'Name': 'Installed',
+                                                  'Type': 'integer',
+                                                  'Value': '1',
+                                                  'KeyPath': 'yes',
+                                                  })
+        if self.desktop_shortcut is not None:
+            desk = ET.SubElement(product, 'DirectoryRef', {'Id': 
'DesktopFolder'})
+            comp = ET.SubElement(desk, 'Component', 
{'Id':'ApplicationShortcutDesktop',
+                                                     'Guid': gen_guid(),
+                                                     })
+            ET.SubElement(comp, 'Shortcut', {'Id': 
'ApplicationDesktopShortcut',
+                                             'Name': self.product_name,
+                                             'Description': self.comments,
+                                             'Target': '[INSTALLDIR]' + 
self.desktop_shortcut,
+                                             'WorkingDirectory': 'INSTALLDIR',
+            })
+            ET.SubElement(comp, 'RemoveFolder', {'Id': 'RemoveDesktopFolder',
+                                                 'Directory': 'DesktopFolder',
+                                                 'On': 'uninstall',
+                                                 })
+            ET.SubElement(comp, 'RegistryValue', {'Root': 'HKCU',
+                                                  'Key': 
'Software\\Microsoft\\' + self.name,
+                                                  'Name': 'Installed',
+                                                  'Type': 'integer',
+                                                  'Value': '1',
+                                                  'KeyPath': 'yes',
+                                                  })
+
+        ET.SubElement(product, 'Property', {
+            'Id': 'WIXUI_INSTALLDIR',
+            'Value': 'INSTALLDIR',
+        })
+        if platform.system() == "Windows":
+            if self.license_file:
+                ET.SubElement(product, 'UIRef', {
+                    'Id': 'WixUI_FeatureTree',
+                })
+            else:
+                self.create_licenseless_dialog_entries(product)
+
+        if self.graphics.banner is not None:
+            ET.SubElement(product, 'WixVariable', {
+                'Id': 'WixUIBannerBmp',
+                'Value': self.graphics.banner,
+            })
+        if self.graphics.background is not None:
+            ET.SubElement(product, 'WixVariable', {
+                'Id': 'WixUIDialogBmp',
+                'Value': self.graphics.background,
+            })
+
+        top_feature = ET.SubElement(product, 'Feature', {
+            'Id': 'Complete',
+            'Title': self.name + ' ' + self.version,
+            'Description': 'The complete package',
+            'Display': 'expand',
+            'Level': '1',
+            'ConfigurableDirectory': 'INSTALLDIR',
+        })
+
+        for f in self.parts:
+            self.scan_feature(top_feature, installdir, 1, f)
+
+        if self.need_msvcrt:
+            vcredist_feature = ET.SubElement(top_feature, 'Feature', {
+                'Id': 'VCRedist',
+                'Title': 'Visual C++ runtime',
+                'AllowAdvertise': 'no',
+                'Display': 'hidden',
+                'Level': '1',
+            })
+            ET.SubElement(vcredist_feature, 'MergeRef', {'Id': 'VCRedist'})
+        if self.startmenu_shortcut is not None:
+            ET.SubElement(top_feature, 'ComponentRef', {'Id': 
'ApplicationShortcut'})
+        if self.desktop_shortcut is not None:
+            ET.SubElement(top_feature, 'ComponentRef', {'Id': 
'ApplicationShortcutDesktop'})
+        if self.addremove_icon is not None:
+            icoid = 'addremoveicon.ico'
+            ET.SubElement(product, 'Icon', {'Id': icoid,
+                                            'SourceFile': self.addremove_icon,
+            })
+            ET.SubElement(product, 'Property', {'Id': 'ARPPRODUCTICON',
+                                                'Value': icoid,
+            })
+
+        if self.registry_entries is not None:
+            registry_entries_directory = ET.SubElement(product, 
'DirectoryRef', {'Id': 'TARGETDIR'})
+            registry_entries_component = 
ET.SubElement(registry_entries_directory, 'Component', {'Id': 
'RegistryEntries', 'Guid': gen_guid()})
+            if self.arch == 64:
+                registry_entries_component.set('Win64', 'yes')
+            ET.SubElement(top_feature, 'ComponentRef', {'Id': 
'RegistryEntries'})
+            for r in self.registry_entries:
+                self.create_registry_entries(registry_entries_component, r)
+
+        ET.ElementTree(self.root).write(self.main_xml, encoding='utf-8', 
xml_declaration=True)
+        # ElementTree can not do prettyprinting so do it manually
+        import xml.dom.minidom
+        doc = xml.dom.minidom.parse(self.main_xml)
+        with open(self.main_xml, 'w') as of:
+            of.write(doc.toprettyxml(indent=' '))
+
+    def create_registry_entries(self, comp, reg):
+        reg_key = ET.SubElement(comp, 'RegistryKey', {
+            'Root': reg['root'],
+            'Key': reg['key'],
+            'Action': reg['action'],
+        })
+        ET.SubElement(reg_key, 'RegistryValue', {
+            'Name': reg['name'],
+            'Type': reg['type'],
+            'Value': reg['value'],
+            'KeyPath': reg['key_path'],
+          })
+
+    def scan_feature(self, top_feature, installdir, depth, feature):
+        for sd in [feature['staged_dir']]:
+            if '/' in sd or '\\' in sd:
+                sys.exit('Staged_dir %s must not have a path segment.' % sd)
+            nodes = {}
+            for root, dirs, files in os.walk(sd):
+                cur_node = Node(dirs, files)
+                nodes[root] = cur_node
+            fdict = {
+                'Id': feature['id'],
+                'Title': feature['title'],
+                'Description': feature['description'],
+                'Level': '1'
+            }
+            if feature.get('absent', 'ab') == 'disallow':
+                fdict['Absent'] = 'disallow'
+            self.feature_properties[sd] = fdict
+
+            self.feature_components[sd] = []
+            self.create_xml(nodes, sd, installdir, sd)
+            self.build_features(nodes, top_feature, sd)
+
+    def build_features(self, nodes, top_feature, staging_dir):
+        feature = ET.SubElement(top_feature, 'Feature',  
self.feature_properties[staging_dir])
+        for component_id in self.feature_components[staging_dir]:
+            ET.SubElement(feature, 'ComponentRef', {
+                'Id': component_id,
+            })
+
+    def path_to_id(self, pathname):
+        #return re.sub(r'[^a-zA-Z0-9_.]', '_', str(pathname))[-72:]
+        idstr = f'pathid{self.idnum}'
+        self.idnum += 1
+        return idstr
+
+    def create_xml(self, nodes, current_dir, parent_xml_node, staging_dir):
+        cur_node = nodes[current_dir]
+        if cur_node.files:
+            component_id = 'ApplicationFiles%d' % self.component_num
+            comp_xml_node = ET.SubElement(parent_xml_node, 'Component', {
+                'Id': component_id,
+                'Guid': gen_guid(),
+            })
+            self.feature_components[staging_dir].append(component_id)
+            if self.arch == 64:
+                comp_xml_node.set('Win64', 'yes')
+            if platform.system() == "Windows" and self.component_num == 0:
+                ET.SubElement(comp_xml_node, 'Environment', {
+                    'Id': 'Environment',
+                    'Name': 'PATH',
+                    'Part': 'last',
+                    'System': 'yes',
+                    'Action': 'set',
+                    'Value': '[INSTALLDIR]',
+                })
+            self.component_num += 1
+            for f in cur_node.files:
+                file_id = self.path_to_id(os.path.join(current_dir, f))
+                ET.SubElement(comp_xml_node, 'File', {
+                    'Id': file_id,
+                    'Name': f,
+                    'Source': os.path.join(current_dir, f),
+                })
+
+        for dirname in cur_node.dirs:
+            dir_id = self.path_to_id(os.path.join(current_dir, dirname))
+            dir_node = ET.SubElement(parent_xml_node, 'Directory', {
+                'Id': dir_id,
+                'Name': dirname,
+            })
+            self.create_xml(nodes, os.path.join(current_dir, dirname), 
dir_node, staging_dir)
+
+    def create_licenseless_dialog_entries(self, product_element):
+        ui = ET.SubElement(product_element, 'UI', {
+            'Id': 'WixUI_FeatureTree'
+        })
+
+        ET.SubElement(ui, 'TextStyle', {
+            'Id': 'WixUI_Font_Normal',
+            'FaceName': 'Tahoma',
+            'Size': '8'
+        })
+
+        ET.SubElement(ui, 'TextStyle', {
+            'Id': 'WixUI_Font_Bigger',
+            'FaceName': 'Tahoma',
+            'Size': '12'
+        })
+
+        ET.SubElement(ui, 'TextStyle', {
+            'Id': 'WixUI_Font_Title',
+            'FaceName': 'Tahoma',
+            'Size': '9',
+            'Bold': 'yes'
+        })
+
+        ET.SubElement(ui, 'Property', {
+            'Id': 'DefaultUIFont',
+            'Value': 'WixUI_Font_Normal'
+        })
+
+        ET.SubElement(ui, 'Property', {
+            'Id': 'WixUI_Mode',
+            'Value': 'FeatureTree'
+        })
+
+        ET.SubElement(ui, 'DialogRef', {
+            'Id': 'ErrorDlg'
+        })
+
+        ET.SubElement(ui, 'DialogRef', {
+            'Id': 'FatalError'
+        })
+
+        ET.SubElement(ui, 'DialogRef', {
+            'Id': 'FilesInUse'
+        })
+
+        ET.SubElement(ui, 'DialogRef', {
+            'Id': 'MsiRMFilesInUse'
+        })
+
+        ET.SubElement(ui, 'DialogRef', {
+            'Id': 'PrepareDlg'
+        })
+
+        ET.SubElement(ui, 'DialogRef', {
+            'Id': 'ProgressDlg'
+        })
+
+        ET.SubElement(ui, 'DialogRef', {
+            'Id': 'ResumeDlg'
+        })
+
+        ET.SubElement(ui, 'DialogRef', {
+            'Id': 'UserExit'
+        })
+
+        pub_exit = ET.SubElement(ui, 'Publish', {
+            'Dialog': 'ExitDialog',
+            'Control': 'Finish',
+            'Event': 'EndDialog',
+            'Value': 'Return',
+            'Order': '999'
+        })
+
+        pub_exit.text = '1'
+
+        pub_welcome_next = ET.SubElement(ui, 'Publish', {
+            'Dialog': 'WelcomeDlg',
+            'Control': 'Next',
+            'Event': 'NewDialog',
+            'Value': 'CustomizeDlg'
+        })
+
+        pub_welcome_next.text = 'NOT Installed'
+
+        pub_welcome_maint_next = ET.SubElement(ui, 'Publish', {
+            'Dialog': 'WelcomeDlg',
+            'Control': 'Next',
+            'Event': 'NewDialog',
+            'Value': 'VerifyReadyDlg'
+        })
+
+        pub_welcome_maint_next.text = 'Installed AND PATCH'
+
+        pub_customize_back_maint = ET.SubElement(ui, 'Publish', {
+            'Dialog': 'CustomizeDlg',
+            'Control': 'Back',
+            'Event': 'NewDialog',
+            'Value': 'MaintenanceTypeDlg',
+            'Order': '1'
+        })
+
+        pub_customize_back_maint.text = 'Installed'
+
+        pub_customize_back_welcome = ET.SubElement(ui, 'Publish', {
+            'Dialog': 'CustomizeDlg',
+            'Control': 'Back',
+            'Event': 'NewDialog',
+            'Value': 'WelcomeDlg',
+            'Order': '2'
+        })
+
+        pub_customize_back_welcome.text = 'Not Installed'
+
+        pub_customize_next = ET.SubElement(ui, 'Publish', {
+            'Dialog': 'CustomizeDlg',
+            'Control': 'Next',
+            'Event': 'NewDialog',
+            'Value': 'VerifyReadyDlg'
+        })
+
+        pub_customize_next.text = '1'
+
+        pub_verify_customize_back = ET.SubElement(ui, 'Publish', {
+            'Dialog': 'VerifyReadyDlg',
+            'Control': 'Back',
+            'Event': 'NewDialog',
+            'Value': 'CustomizeDlg',
+            'Order': '1'
+        })
+
+        pub_verify_customize_back.text = 'NOT Installed OR WixUI_InstallMode = 
"Change"'
+
+        pub_verify_maint_back = ET.SubElement(ui, 'Publish', {
+            'Dialog': 'VerifyReadyDlg',
+            'Control': 'Back',
+            'Event': 'NewDialog',
+            'Value': 'MaintenanceTypeDlg',
+            'Order': '2'
+        })
+
+        pub_verify_maint_back.text = 'Installed AND NOT PATCH'
+
+        pub_verify_welcome_back = ET.SubElement(ui, 'Publish', {
+            'Dialog': 'VerifyReadyDlg',
+            'Control': 'Back',
+            'Event': 'NewDialog',
+            'Value': 'WelcomeDlg',
+            'Order': '3'
+        })
+
+        pub_verify_welcome_back.text = 'Installed AND PATCH'
+
+        pub_maint_welcome_next = ET.SubElement(ui, 'Publish', {
+            'Dialog': 'MaintenanceWelcomeDlg',
+            'Control': 'Next',
+            'Event': 'NewDialog',
+            'Value': 'MaintenanceTypeDlg'
+        })
+
+        pub_maint_welcome_next.text = '1'
+
+        pub_maint_type_change = ET.SubElement(ui, 'Publish', {
+            'Dialog': 'MaintenanceTypeDlg',
+            'Control': 'ChangeButton',
+            'Event': 'NewDialog',
+            'Value': 'CustomizeDlg'
+        })
+
+        pub_maint_type_change.text = '1'
+
+        pub_maint_type_repair = ET.SubElement(ui, 'Publish', {
+            'Dialog': 'MaintenanceTypeDlg',
+            'Control': 'RepairButton',
+            'Event': 'NewDialog',
+            'Value': 'VerifyReadyDlg'
+        })
+
+        pub_maint_type_repair.text = '1'
+
+        pub_maint_type_remove = ET.SubElement(ui, 'Publish', {
+            'Dialog': 'MaintenanceTypeDlg',
+            'Control': 'RemoveButton',
+            'Event': 'NewDialog',
+            'Value': 'VerifyReadyDlg'
+        })
+
+        pub_maint_type_remove.text = '1'
+
+        pub_maint_type_back = ET.SubElement(ui, 'Publish', {
+            'Dialog': 'MaintenanceTypeDlg',
+            'Control': 'Back',
+            'Event': 'NewDialog',
+            'Value': 'MaintenanceWelcomeDlg'
+        })
+
+        pub_maint_type_back.text = '1'
+
+        ET.SubElement(product_element, 'UIRef', {
+            'Id': 'WixUI_Common',
+        })
+
+    def build_package(self):
+        wixdir = 'c:\\Program Files\\Wix Toolset v3.11\\bin'
+        if platform.system() != "Windows":
+            wixdir = '/usr/bin'
+        if not os.path.isdir(wixdir):
+            wixdir = 'c:\\Program Files (x86)\\Wix Toolset v3.11\\bin'
+        if not os.path.isdir(wixdir):
+            print("ERROR: This script requires WIX")
+            sys.exit(1)
+        if platform.system() == "Windows":
+            subprocess.check_call([os.path.join(wixdir, 'candle'), 
self.main_xml])
+            subprocess.check_call([os.path.join(wixdir, 'light'),
+                                   '-ext', 'WixUIExtension',
+                                   '-cultures:en-us',
+                                   '-dWixUILicenseRtf=' + self.license_file if 
self.license_file else '',
+                                   '-dcl:high',
+                                   '-out', self.final_output,
+                                   self.main_o])
+        else:
+            subprocess.check_call([os.path.join(wixdir, 'wixl'), '-o', 
self.final_output, self.main_xml])
+
+def run(args):
+    if len(args) != 1:
+        sys.exit('createmsi.py <msi definition json>')
+    jsonfile = args[0]
+    if '/' in jsonfile or '\\' in jsonfile:
+        sys.exit('Input file %s must not contain a path segment.' % jsonfile)
+    p = PackageGenerator(jsonfile)
+    p.generate_files()
+    p.build_package()
+
+if __name__ == '__main__':
+    run(sys.argv[1:])

Reply via email to