NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

bdist_msi.py (25263B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\stdlib\distutils\command\bdist_msi.py
      2 import sys, os
      3 from sysconfig import get_python_version
      4 from distutils.core import Command
      5 from distutils.dir_util import remove_tree
      6 from distutils.version import StrictVersion
      7 from distutils.errors import DistutilsOptionError
      8 from distutils import log
      9 from distutils.util import get_platform
     10 import msilib
     11 from msilib import schema, sequence, text
     12 from msilib import Directory, Feature, Dialog, add_data
     13 
     14 class PyDialog(Dialog):
     15 
     16     def __init__(self, *args, **kw):
     17         Dialog.__init__(self, *args)
     18         ruler = self.h - 36
     19         self.line('BottomLine', 0, ruler, self.w, 0)
     20 
     21     def title(self, title):
     22         self.text('Title', 15, 10, 320, 60, 196611, '{\\VerdanaBold10}%s' % title)
     23 
     24     def back(self, title, next, name = 'Back', active = 1):
     25         if active:
     26             flags = 3
     27         else:
     28             flags = 1
     29         return self.pushbutton(name, 180, self.h - 27, 56, 17, flags, title, next)
     30 
     31     def cancel(self, title, next, name = 'Cancel', active = 1):
     32         if active:
     33             flags = 3
     34         else:
     35             flags = 1
     36         return self.pushbutton(name, 304, self.h - 27, 56, 17, flags, title, next)
     37 
     38     def next(self, title, next, name = 'Next', active = 1):
     39         if active:
     40             flags = 3
     41         else:
     42             flags = 1
     43         return self.pushbutton(name, 236, self.h - 27, 56, 17, flags, title, next)
     44 
     45     def xbutton(self, name, title, next, xpos):
     46         return self.pushbutton(name, int(self.w * xpos - 28), self.h - 27, 56, 17, 3, title, next)
     47 
     48 
     49 class bdist_msi(Command):
     50     description = 'create a Microsoft Installer (.msi) binary distribution'
     51     user_options = [('bdist-dir=', None, 'temporary directory for creating the distribution'),
     52      ('plat-name=', 'p', 'platform name to embed in generated filenames (default: %s)' % get_platform()),
     53      ('keep-temp', 'k', 'keep the pseudo-installation tree around after ' + 'creating the distribution archive'),
     54      ('target-version=', None, 'require a specific python version' + ' on the target system'),
     55      ('no-target-compile', 'c', 'do not compile .py to .pyc on the target system'),
     56      ('no-target-optimize', 'o', 'do not compile .py to .pyo (optimized)on the target system'),
     57      ('dist-dir=', 'd', 'directory to put final built distributions in'),
     58      ('skip-build', None, 'skip rebuilding everything (for testing/debugging)'),
     59      ('install-script=', None, 'basename of installation script to be run afterinstallation or before deinstallation'),
     60      ('pre-install-script=', None, 'Fully qualified filename of a script to be run before any files are installed.  This script need not be in the distribution')]
     61     boolean_options = ['keep-temp',
     62      'no-target-compile',
     63      'no-target-optimize',
     64      'skip-build']
     65     all_versions = ['2.0',
     66      '2.1',
     67      '2.2',
     68      '2.3',
     69      '2.4',
     70      '2.5',
     71      '2.6',
     72      '2.7',
     73      '2.8',
     74      '2.9',
     75      '3.0',
     76      '3.1',
     77      '3.2',
     78      '3.3',
     79      '3.4',
     80      '3.5',
     81      '3.6',
     82      '3.7',
     83      '3.8',
     84      '3.9']
     85     other_version = 'X'
     86 
     87     def initialize_options(self):
     88         self.bdist_dir = None
     89         self.plat_name = None
     90         self.keep_temp = 0
     91         self.no_target_compile = 0
     92         self.no_target_optimize = 0
     93         self.target_version = None
     94         self.dist_dir = None
     95         self.skip_build = 0
     96         self.install_script = None
     97         self.pre_install_script = None
     98         self.versions = None
     99 
    100     def finalize_options(self):
    101         if self.bdist_dir is None:
    102             bdist_base = self.get_finalized_command('bdist').bdist_base
    103             self.bdist_dir = os.path.join(bdist_base, 'msi')
    104         short_version = get_python_version()
    105         if not self.target_version and self.distribution.has_ext_modules():
    106             self.target_version = short_version
    107         if self.target_version:
    108             self.versions = [self.target_version]
    109             if not self.skip_build and self.distribution.has_ext_modules() and self.target_version != short_version:
    110                 raise DistutilsOptionError, "target version can only be %s, or the '--skip-build' option must be specified" % (short_version,)
    111         else:
    112             self.versions = list(self.all_versions)
    113         self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'), ('plat_name', 'plat_name'))
    114         if self.pre_install_script:
    115             raise DistutilsOptionError, 'the pre-install-script feature is not yet implemented'
    116         if self.install_script:
    117             for script in self.distribution.scripts:
    118                 if self.install_script == os.path.basename(script):
    119                     break
    120             else:
    121                 raise DistutilsOptionError, "install_script '%s' not found in scripts" % self.install_script
    122 
    123         self.install_script_key = None
    124 
    125     def run(self):
    126         if not self.skip_build:
    127             self.run_command('build')
    128         install = self.reinitialize_command('install', reinit_subcommands=1)
    129         install.prefix = self.bdist_dir
    130         install.skip_build = self.skip_build
    131         install.warn_dir = 0
    132         install_lib = self.reinitialize_command('install_lib')
    133         install_lib.compile = 0
    134         install_lib.optimize = 0
    135         if self.distribution.has_ext_modules():
    136             target_version = self.target_version
    137             if not target_version:
    138                 target_version = sys.version[0:3]
    139             plat_specifier = '.%s-%s' % (self.plat_name, target_version)
    140             build = self.get_finalized_command('build')
    141             build.build_lib = os.path.join(build.build_base, 'lib' + plat_specifier)
    142         log.info('installing to %s', self.bdist_dir)
    143         install.ensure_finalized()
    144         sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB'))
    145         install.run()
    146         del sys.path[0]
    147         self.mkpath(self.dist_dir)
    148         fullname = self.distribution.get_fullname()
    149         installer_name = self.get_installer_filename(fullname)
    150         installer_name = os.path.abspath(installer_name)
    151         if os.path.exists(installer_name):
    152             os.unlink(installer_name)
    153         metadata = self.distribution.metadata
    154         author = metadata.author
    155         if not author:
    156             author = metadata.maintainer
    157         if not author:
    158             author = 'UNKNOWN'
    159         version = metadata.get_version()
    160         sversion = '%d.%d.%d' % StrictVersion(version).version
    161         fullname = self.distribution.get_fullname()
    162         if self.target_version:
    163             product_name = 'Python %s %s' % (self.target_version, fullname)
    164         else:
    165             product_name = 'Python %s' % fullname
    166         self.db = msilib.init_database(installer_name, schema, product_name, msilib.gen_uuid(), sversion, author)
    167         msilib.add_tables(self.db, sequence)
    168         props = [('DistVersion', version)]
    169         email = metadata.author_email or metadata.maintainer_email
    170         if email:
    171             props.append(('ARPCONTACT', email))
    172         if metadata.url:
    173             props.append(('ARPURLINFOABOUT', metadata.url))
    174         if props:
    175             add_data(self.db, 'Property', props)
    176         self.add_find_python()
    177         self.add_files()
    178         self.add_scripts()
    179         self.add_ui()
    180         self.db.Commit()
    181         if hasattr(self.distribution, 'dist_files'):
    182             tup = ('bdist_msi', self.target_version or 'any', fullname)
    183             self.distribution.dist_files.append(tup)
    184         if not self.keep_temp:
    185             remove_tree(self.bdist_dir, dry_run=self.dry_run)
    186 
    187     def add_files(self):
    188         db = self.db
    189         cab = msilib.CAB('distfiles')
    190         rootdir = os.path.abspath(self.bdist_dir)
    191         root = Directory(db, cab, None, rootdir, 'TARGETDIR', 'SourceDir')
    192         f = Feature(db, 'Python', 'Python', 'Everything', 0, 1, directory='TARGETDIR')
    193         items = [(f, root, '')]
    194         for version in self.versions + [self.other_version]:
    195             target = 'TARGETDIR' + version
    196             name = default = 'Python' + version
    197             desc = 'Everything'
    198             if version is self.other_version:
    199                 title = 'Python from another location'
    200                 level = 2
    201             else:
    202                 title = 'Python %s from registry' % version
    203                 level = 1
    204             f = Feature(db, name, title, desc, 1, level, directory=target)
    205             dir = Directory(db, cab, root, rootdir, target, default)
    206             items.append((f, dir, version))
    207 
    208         db.Commit()
    209         seen = {}
    210         for feature, dir, version in items:
    211             todo = [dir]
    212             while todo:
    213                 dir = todo.pop()
    214                 for file in os.listdir(dir.absolute):
    215                     afile = os.path.join(dir.absolute, file)
    216                     if os.path.isdir(afile):
    217                         short = '%s|%s' % (dir.make_short(file), file)
    218                         default = file + version
    219                         newdir = Directory(db, cab, dir, file, default, short)
    220                         todo.append(newdir)
    221                     else:
    222                         if not dir.component:
    223                             dir.start_component(dir.logical, feature, 0)
    224                         if afile not in seen:
    225                             key = seen[afile] = dir.add_file(file)
    226                             if file == self.install_script:
    227                                 if self.install_script_key:
    228                                     raise DistutilsOptionError('Multiple files with name %s' % file)
    229                                 self.install_script_key = '[#%s]' % key
    230                         else:
    231                             key = seen[afile]
    232                             add_data(self.db, 'DuplicateFile', [(key + version,
    233                               dir.component,
    234                               key,
    235                               None,
    236                               dir.logical)])
    237 
    238             db.Commit()
    239 
    240         cab.commit(db)
    241 
    242     def add_find_python(self):
    243         start = 402
    244         for ver in self.versions:
    245             install_path = 'SOFTWARE\\Python\\PythonCore\\%s\\InstallPath' % ver
    246             machine_reg = 'python.machine.' + ver
    247             user_reg = 'python.user.' + ver
    248             machine_prop = 'PYTHON.MACHINE.' + ver
    249             user_prop = 'PYTHON.USER.' + ver
    250             machine_action = 'PythonFromMachine' + ver
    251             user_action = 'PythonFromUser' + ver
    252             exe_action = 'PythonExe' + ver
    253             target_dir_prop = 'TARGETDIR' + ver
    254             exe_prop = 'PYTHON' + ver
    255             if msilib.Win64:
    256                 Type = 18
    257             else:
    258                 Type = 2
    259             add_data(self.db, 'RegLocator', [(machine_reg,
    260               2,
    261               install_path,
    262               None,
    263               Type), (user_reg,
    264               1,
    265               install_path,
    266               None,
    267               Type)])
    268             add_data(self.db, 'AppSearch', [(machine_prop, machine_reg), (user_prop, user_reg)])
    269             add_data(self.db, 'CustomAction', [(machine_action,
    270               307,
    271               target_dir_prop,
    272               '[' + machine_prop + ']'), (user_action,
    273               307,
    274               target_dir_prop,
    275               '[' + user_prop + ']'), (exe_action,
    276               307,
    277               exe_prop,
    278               '[' + target_dir_prop + ']\\python.exe')])
    279             add_data(self.db, 'InstallExecuteSequence', [(machine_action, machine_prop, start), (user_action, user_prop, start + 1), (exe_action, None, start + 2)])
    280             add_data(self.db, 'InstallUISequence', [(machine_action, machine_prop, start), (user_action, user_prop, start + 1), (exe_action, None, start + 2)])
    281             add_data(self.db, 'Condition', [('Python' + ver, 0, 'NOT TARGETDIR' + ver)])
    282             start += 4
    283 
    284     def add_scripts(self):
    285         if self.install_script:
    286             start = 6800
    287             for ver in self.versions + [self.other_version]:
    288                 install_action = 'install_script.' + ver
    289                 exe_prop = 'PYTHON' + ver
    290                 add_data(self.db, 'CustomAction', [(install_action,
    291                   50,
    292                   exe_prop,
    293                   self.install_script_key)])
    294                 add_data(self.db, 'InstallExecuteSequence', [(install_action, '&Python%s=3' % ver, start)])
    295                 start += 1
    296 
    297         if self.pre_install_script:
    298             scriptfn = os.path.join(self.bdist_dir, 'preinstall.bat')
    299             f = open(scriptfn, 'w')
    300             f.write('rem ="""\n%1 %0\nexit\n"""\n')
    301             f.write(open(self.pre_install_script).read())
    302             f.close()
    303             add_data(self.db, 'Binary', [('PreInstall', msilib.Binary(scriptfn))])
    304             add_data(self.db, 'CustomAction', [('PreInstall', 2, 'PreInstall', None)])
    305             add_data(self.db, 'InstallExecuteSequence', [('PreInstall', 'NOT Installed', 450)])
    306 
    307     def add_ui(self):
    308         db = self.db
    309         x = y = 50
    310         w = 370
    311         h = 300
    312         title = '[ProductName] Setup'
    313         modal = 3
    314         modeless = 1
    315         add_data(db, 'Property', [('DefaultUIFont', 'DlgFont8'),
    316          ('ErrorDialog', 'ErrorDlg'),
    317          ('Progress1', 'Install'),
    318          ('Progress2', 'installs'),
    319          ('MaintenanceForm_Action', 'Repair'),
    320          ('WhichUsers', 'ALL')])
    321         add_data(db, 'TextStyle', [('DlgFont8', 'Tahoma', 9, None, 0),
    322          ('DlgFontBold8', 'Tahoma', 8, None, 1),
    323          ('VerdanaBold10', 'Verdana', 10, None, 1),
    324          ('VerdanaRed9', 'Verdana', 9, 255, 0)])
    325         add_data(db, 'InstallUISequence', [('PrepareDlg', 'Not Privileged or Windows9x or Installed', 140),
    326          ('WhichUsersDlg', 'Privileged and not Windows9x and not Installed', 141),
    327          ('SelectFeaturesDlg', 'Not Installed', 1230),
    328          ('MaintenanceTypeDlg', 'Installed AND NOT RESUME AND NOT Preselected', 1250),
    329          ('ProgressDlg', None, 1280)])
    330         add_data(db, 'ActionText', text.ActionText)
    331         add_data(db, 'UIText', text.UIText)
    332         fatal = PyDialog(db, 'FatalError', x, y, w, h, modal, title, 'Finish', 'Finish', 'Finish')
    333         fatal.title('[ProductName] Installer ended prematurely')
    334         fatal.back('< Back', 'Finish', active=0)
    335         fatal.cancel('Cancel', 'Back', active=0)
    336         fatal.text('Description1', 15, 70, 320, 80, 196611, '[ProductName] setup ended prematurely because of an error.  Your system has not been modified.  To install this program at a later time, please run the installation again.')
    337         fatal.text('Description2', 15, 155, 320, 20, 196611, 'Click the Finish button to exit the Installer.')
    338         c = fatal.next('Finish', 'Cancel', name='Finish')
    339         c.event('EndDialog', 'Exit')
    340         user_exit = PyDialog(db, 'UserExit', x, y, w, h, modal, title, 'Finish', 'Finish', 'Finish')
    341         user_exit.title('[ProductName] Installer was interrupted')
    342         user_exit.back('< Back', 'Finish', active=0)
    343         user_exit.cancel('Cancel', 'Back', active=0)
    344         user_exit.text('Description1', 15, 70, 320, 80, 196611, '[ProductName] setup was interrupted.  Your system has not been modified.  To install this program at a later time, please run the installation again.')
    345         user_exit.text('Description2', 15, 155, 320, 20, 196611, 'Click the Finish button to exit the Installer.')
    346         c = user_exit.next('Finish', 'Cancel', name='Finish')
    347         c.event('EndDialog', 'Exit')
    348         exit_dialog = PyDialog(db, 'ExitDialog', x, y, w, h, modal, title, 'Finish', 'Finish', 'Finish')
    349         exit_dialog.title('Completing the [ProductName] Installer')
    350         exit_dialog.back('< Back', 'Finish', active=0)
    351         exit_dialog.cancel('Cancel', 'Back', active=0)
    352         exit_dialog.text('Description', 15, 235, 320, 20, 196611, 'Click the Finish button to exit the Installer.')
    353         c = exit_dialog.next('Finish', 'Cancel', name='Finish')
    354         c.event('EndDialog', 'Return')
    355         inuse = PyDialog(db, 'FilesInUse', x, y, w, h, 19, title, 'Retry', 'Retry', 'Retry', bitmap=False)
    356         inuse.text('Title', 15, 6, 200, 15, 196611, '{\\DlgFontBold8}Files in Use')
    357         inuse.text('Description', 20, 23, 280, 20, 196611, 'Some files that need to be updated are currently in use.')
    358         inuse.text('Text', 20, 55, 330, 50, 3, 'The following applications are using files that need to be updated by this setup. Close these applications and then click Retry to continue the installation or Cancel to exit it.')
    359         inuse.control('List', 'ListBox', 20, 107, 330, 130, 7, 'FileInUseProcess', None, None, None)
    360         c = inuse.back('Exit', 'Ignore', name='Exit')
    361         c.event('EndDialog', 'Exit')
    362         c = inuse.next('Ignore', 'Retry', name='Ignore')
    363         c.event('EndDialog', 'Ignore')
    364         c = inuse.cancel('Retry', 'Exit', name='Retry')
    365         c.event('EndDialog', 'Retry')
    366         error = Dialog(db, 'ErrorDlg', 50, 10, 330, 101, 65543, title, 'ErrorText', None, None)
    367         error.text('ErrorText', 50, 9, 280, 48, 3, '')
    368         error.pushbutton('N', 120, 72, 81, 21, 3, 'No', None).event('EndDialog', 'ErrorNo')
    369         error.pushbutton('Y', 240, 72, 81, 21, 3, 'Yes', None).event('EndDialog', 'ErrorYes')
    370         error.pushbutton('A', 0, 72, 81, 21, 3, 'Abort', None).event('EndDialog', 'ErrorAbort')
    371         error.pushbutton('C', 42, 72, 81, 21, 3, 'Cancel', None).event('EndDialog', 'ErrorCancel')
    372         error.pushbutton('I', 81, 72, 81, 21, 3, 'Ignore', None).event('EndDialog', 'ErrorIgnore')
    373         error.pushbutton('O', 159, 72, 81, 21, 3, 'Ok', None).event('EndDialog', 'ErrorOk')
    374         error.pushbutton('R', 198, 72, 81, 21, 3, 'Retry', None).event('EndDialog', 'ErrorRetry')
    375         cancel = Dialog(db, 'CancelDlg', 50, 10, 260, 85, 3, title, 'No', 'No', 'No')
    376         cancel.text('Text', 48, 15, 194, 30, 3, 'Are you sure you want to cancel [ProductName] installation?')
    377         c = cancel.pushbutton('Yes', 72, 57, 56, 17, 3, 'Yes', 'No')
    378         c.event('EndDialog', 'Exit')
    379         c = cancel.pushbutton('No', 132, 57, 56, 17, 3, 'No', 'Yes')
    380         c.event('EndDialog', 'Return')
    381         costing = Dialog(db, 'WaitForCostingDlg', 50, 10, 260, 85, modal, title, 'Return', 'Return', 'Return')
    382         costing.text('Text', 48, 15, 194, 30, 3, 'Please wait while the installer finishes determining your disk space requirements.')
    383         c = costing.pushbutton('Return', 102, 57, 56, 17, 3, 'Return', None)
    384         c.event('EndDialog', 'Exit')
    385         prep = PyDialog(db, 'PrepareDlg', x, y, w, h, modeless, title, 'Cancel', 'Cancel', 'Cancel')
    386         prep.text('Description', 15, 70, 320, 40, 196611, 'Please wait while the Installer prepares to guide you through the installation.')
    387         prep.title('Welcome to the [ProductName] Installer')
    388         c = prep.text('ActionText', 15, 110, 320, 20, 196611, 'Pondering...')
    389         c.mapping('ActionText', 'Text')
    390         c = prep.text('ActionData', 15, 135, 320, 30, 196611, None)
    391         c.mapping('ActionData', 'Text')
    392         prep.back('Back', None, active=0)
    393         prep.next('Next', None, active=0)
    394         c = prep.cancel('Cancel', None)
    395         c.event('SpawnDialog', 'CancelDlg')
    396         seldlg = PyDialog(db, 'SelectFeaturesDlg', x, y, w, h, modal, title, 'Next', 'Next', 'Cancel')
    397         seldlg.title('Select Python Installations')
    398         seldlg.text('Hint', 15, 30, 300, 20, 3, 'Select the Python locations where %s should be installed.' % self.distribution.get_fullname())
    399         seldlg.back('< Back', None, active=0)
    400         c = seldlg.next('Next >', 'Cancel')
    401         order = 1
    402         c.event('[TARGETDIR]', '[SourceDir]', ordering=order)
    403         for version in self.versions + [self.other_version]:
    404             order += 1
    405             c.event('[TARGETDIR]', '[TARGETDIR%s]' % version, 'FEATURE_SELECTED AND &Python%s=3' % version, ordering=order)
    406 
    407         c.event('SpawnWaitDialog', 'WaitForCostingDlg', ordering=order + 1)
    408         c.event('EndDialog', 'Return', ordering=order + 2)
    409         c = seldlg.cancel('Cancel', 'Features')
    410         c.event('SpawnDialog', 'CancelDlg')
    411         c = seldlg.control('Features', 'SelectionTree', 15, 60, 300, 120, 3, 'FEATURE', None, 'PathEdit', None)
    412         c.event('[FEATURE_SELECTED]', '1')
    413         ver = self.other_version
    414         install_other_cond = 'FEATURE_SELECTED AND &Python%s=3' % ver
    415         dont_install_other_cond = 'FEATURE_SELECTED AND &Python%s<>3' % ver
    416         c = seldlg.text('Other', 15, 200, 300, 15, 3, 'Provide an alternate Python location')
    417         c.condition('Enable', install_other_cond)
    418         c.condition('Show', install_other_cond)
    419         c.condition('Disable', dont_install_other_cond)
    420         c.condition('Hide', dont_install_other_cond)
    421         c = seldlg.control('PathEdit', 'PathEdit', 15, 215, 300, 16, 1, 'TARGETDIR' + ver, None, 'Next', None)
    422         c.condition('Enable', install_other_cond)
    423         c.condition('Show', install_other_cond)
    424         c.condition('Disable', dont_install_other_cond)
    425         c.condition('Hide', dont_install_other_cond)
    426         cost = PyDialog(db, 'DiskCostDlg', x, y, w, h, modal, title, 'OK', 'OK', 'OK', bitmap=False)
    427         cost.text('Title', 15, 6, 200, 15, 196611, '{\\DlgFontBold8}Disk Space Requirements')
    428         cost.text('Description', 20, 20, 280, 20, 196611, 'The disk space required for the installation of the selected features.')
    429         cost.text('Text', 20, 53, 330, 60, 3, 'The highlighted volumes (if any) do not have enough disk space available for the currently selected features.  You can either remove some files from the highlighted volumes, or choose to install less features onto local drive(s), or select different destination drive(s).')
    430         cost.control('VolumeList', 'VolumeCostList', 20, 100, 330, 150, 393223, None, '{120}{70}{70}{70}{70}', None, None)
    431         cost.xbutton('OK', 'Ok', None, 0.5).event('EndDialog', 'Return')
    432         whichusers = PyDialog(db, 'WhichUsersDlg', x, y, w, h, modal, title, 'AdminInstall', 'Next', 'Cancel')
    433         whichusers.title('Select whether to install [ProductName] for all users of this computer.')
    434         g = whichusers.radiogroup('AdminInstall', 15, 60, 260, 50, 3, 'WhichUsers', '', 'Next')
    435         g.add('ALL', 0, 5, 150, 20, 'Install for all users')
    436         g.add('JUSTME', 0, 25, 150, 20, 'Install just for me')
    437         whichusers.back('Back', None, active=0)
    438         c = whichusers.next('Next >', 'Cancel')
    439         c.event('[ALLUSERS]', '1', 'WhichUsers="ALL"', 1)
    440         c.event('EndDialog', 'Return', ordering=2)
    441         c = whichusers.cancel('Cancel', 'AdminInstall')
    442         c.event('SpawnDialog', 'CancelDlg')
    443         progress = PyDialog(db, 'ProgressDlg', x, y, w, h, modeless, title, 'Cancel', 'Cancel', 'Cancel', bitmap=False)
    444         progress.text('Title', 20, 15, 200, 15, 196611, '{\\DlgFontBold8}[Progress1] [ProductName]')
    445         progress.text('Text', 35, 65, 300, 30, 3, 'Please wait while the Installer [Progress2] [ProductName]. This may take several minutes.')
    446         progress.text('StatusLabel', 35, 100, 35, 20, 3, 'Status:')
    447         c = progress.text('ActionText', 70, 100, w - 70, 20, 3, 'Pondering...')
    448         c.mapping('ActionText', 'Text')
    449         c = progress.control('ProgressBar', 'ProgressBar', 35, 120, 300, 10, 65537, None, 'Progress done', None, None)
    450         c.mapping('SetProgress', 'Progress')
    451         progress.back('< Back', 'Next', active=False)
    452         progress.next('Next >', 'Cancel', active=False)
    453         progress.cancel('Cancel', 'Back').event('SpawnDialog', 'CancelDlg')
    454         maint = PyDialog(db, 'MaintenanceTypeDlg', x, y, w, h, modal, title, 'Next', 'Next', 'Cancel')
    455         maint.title('Welcome to the [ProductName] Setup Wizard')
    456         maint.text('BodyText', 15, 63, 330, 42, 3, 'Select whether you want to repair or remove [ProductName].')
    457         g = maint.radiogroup('RepairRadioGroup', 15, 108, 330, 60, 3, 'MaintenanceForm_Action', '', 'Next')
    458         g.add('Repair', 0, 18, 200, 17, '&Repair [ProductName]')
    459         g.add('Remove', 0, 36, 200, 17, 'Re&move [ProductName]')
    460         maint.back('< Back', None, active=False)
    461         c = maint.next('Finish', 'Cancel')
    462         c.event('[REINSTALL]', 'ALL', 'MaintenanceForm_Action="Repair"', 5)
    463         c.event('[Progress1]', 'Repairing', 'MaintenanceForm_Action="Repair"', 6)
    464         c.event('[Progress2]', 'repairs', 'MaintenanceForm_Action="Repair"', 7)
    465         c.event('Reinstall', 'ALL', 'MaintenanceForm_Action="Repair"', 8)
    466         c.event('[REMOVE]', 'ALL', 'MaintenanceForm_Action="Remove"', 11)
    467         c.event('[Progress1]', 'Removing', 'MaintenanceForm_Action="Remove"', 12)
    468         c.event('[Progress2]', 'removes', 'MaintenanceForm_Action="Remove"', 13)
    469         c.event('Remove', 'ALL', 'MaintenanceForm_Action="Remove"', 14)
    470         c.event('EndDialog', 'Return', 'MaintenanceForm_Action<>"Change"', 20)
    471         maint.cancel('Cancel', 'RepairRadioGroup').event('SpawnDialog', 'CancelDlg')
    472 
    473     def get_installer_filename(self, fullname):
    474         if self.target_version:
    475             base_name = '%s.%s-py%s.msi' % (fullname, self.plat_name, self.target_version)
    476         else:
    477             base_name = '%s.%s.msi' % (fullname, self.plat_name)
    478         installer_name = os.path.join(self.dist_dir, base_name)
    479         return installer_name