Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/msicreator/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 23 kB image not shown  

Quelle  createmsi.py   Sprache: Python

 
#!/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
import os
import subprocess
import uuid
import json
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):
        with open(jsonfile, 'rb'as f:
            jsondata = json.load(f)
            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 (x86)\\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 ')
    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:])

91%


¤ Dauer der Verarbeitung: 0.17 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.