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