]> git.proxmox.com Git - mirror_edk2.git/blob - AppPkg/Applications/Python/Python-2.7.2/Lib/distutils/command/bdist_msi.py
AppPkg/Applications/Python: Add Python 2.7.2 sources since the release of Python...
[mirror_edk2.git] / AppPkg / Applications / Python / Python-2.7.2 / Lib / distutils / command / bdist_msi.py
1 # -*- coding: iso-8859-1 -*-
2 # Copyright (C) 2005, 2006 Martin von Löwis
3 # Licensed to PSF under a Contributor Agreement.
4 # The bdist_wininst command proper
5 # based on bdist_wininst
6 """
7 Implements the bdist_msi command.
8 """
9 import sys, os
10 from sysconfig import get_python_version
11
12 from distutils.core import Command
13 from distutils.dir_util import remove_tree
14 from distutils.version import StrictVersion
15 from distutils.errors import DistutilsOptionError
16 from distutils import log
17 from distutils.util import get_platform
18
19 import msilib
20 from msilib import schema, sequence, text
21 from msilib import Directory, Feature, Dialog, add_data
22
23 class PyDialog(Dialog):
24 """Dialog class with a fixed layout: controls at the top, then a ruler,
25 then a list of buttons: back, next, cancel. Optionally a bitmap at the
26 left."""
27 def __init__(self, *args, **kw):
28 """Dialog(database, name, x, y, w, h, attributes, title, first,
29 default, cancel, bitmap=true)"""
30 Dialog.__init__(self, *args)
31 ruler = self.h - 36
32 #if kw.get("bitmap", True):
33 # self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin")
34 self.line("BottomLine", 0, ruler, self.w, 0)
35
36 def title(self, title):
37 "Set the title text of the dialog at the top."
38 # name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix,
39 # text, in VerdanaBold10
40 self.text("Title", 15, 10, 320, 60, 0x30003,
41 r"{\VerdanaBold10}%s" % title)
42
43 def back(self, title, next, name = "Back", active = 1):
44 """Add a back button with a given title, the tab-next button,
45 its name in the Control table, possibly initially disabled.
46
47 Return the button, so that events can be associated"""
48 if active:
49 flags = 3 # Visible|Enabled
50 else:
51 flags = 1 # Visible
52 return self.pushbutton(name, 180, self.h-27 , 56, 17, flags, title, next)
53
54 def cancel(self, title, next, name = "Cancel", active = 1):
55 """Add a cancel button with a given title, the tab-next button,
56 its name in the Control table, possibly initially disabled.
57
58 Return the button, so that events can be associated"""
59 if active:
60 flags = 3 # Visible|Enabled
61 else:
62 flags = 1 # Visible
63 return self.pushbutton(name, 304, self.h-27, 56, 17, flags, title, next)
64
65 def next(self, title, next, name = "Next", active = 1):
66 """Add a Next button with a given title, the tab-next button,
67 its name in the Control table, possibly initially disabled.
68
69 Return the button, so that events can be associated"""
70 if active:
71 flags = 3 # Visible|Enabled
72 else:
73 flags = 1 # Visible
74 return self.pushbutton(name, 236, self.h-27, 56, 17, flags, title, next)
75
76 def xbutton(self, name, title, next, xpos):
77 """Add a button with a given title, the tab-next button,
78 its name in the Control table, giving its x position; the
79 y-position is aligned with the other buttons.
80
81 Return the button, so that events can be associated"""
82 return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next)
83
84 class bdist_msi (Command):
85
86 description = "create a Microsoft Installer (.msi) binary distribution"
87
88 user_options = [('bdist-dir=', None,
89 "temporary directory for creating the distribution"),
90 ('plat-name=', 'p',
91 "platform name to embed in generated filenames "
92 "(default: %s)" % get_platform()),
93 ('keep-temp', 'k',
94 "keep the pseudo-installation tree around after " +
95 "creating the distribution archive"),
96 ('target-version=', None,
97 "require a specific python version" +
98 " on the target system"),
99 ('no-target-compile', 'c',
100 "do not compile .py to .pyc on the target system"),
101 ('no-target-optimize', 'o',
102 "do not compile .py to .pyo (optimized)"
103 "on the target system"),
104 ('dist-dir=', 'd',
105 "directory to put final built distributions in"),
106 ('skip-build', None,
107 "skip rebuilding everything (for testing/debugging)"),
108 ('install-script=', None,
109 "basename of installation script to be run after"
110 "installation or before deinstallation"),
111 ('pre-install-script=', None,
112 "Fully qualified filename of a script to be run before "
113 "any files are installed. This script need not be in the "
114 "distribution"),
115 ]
116
117 boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize',
118 'skip-build']
119
120 all_versions = ['2.0', '2.1', '2.2', '2.3', '2.4',
121 '2.5', '2.6', '2.7', '2.8', '2.9',
122 '3.0', '3.1', '3.2', '3.3', '3.4',
123 '3.5', '3.6', '3.7', '3.8', '3.9']
124 other_version = 'X'
125
126 def initialize_options (self):
127 self.bdist_dir = None
128 self.plat_name = None
129 self.keep_temp = 0
130 self.no_target_compile = 0
131 self.no_target_optimize = 0
132 self.target_version = None
133 self.dist_dir = None
134 self.skip_build = 0
135 self.install_script = None
136 self.pre_install_script = None
137 self.versions = None
138
139 def finalize_options (self):
140 if self.bdist_dir is None:
141 bdist_base = self.get_finalized_command('bdist').bdist_base
142 self.bdist_dir = os.path.join(bdist_base, 'msi')
143 short_version = get_python_version()
144 if (not self.target_version) and self.distribution.has_ext_modules():
145 self.target_version = short_version
146 if self.target_version:
147 self.versions = [self.target_version]
148 if not self.skip_build and self.distribution.has_ext_modules()\
149 and self.target_version != short_version:
150 raise DistutilsOptionError, \
151 "target version can only be %s, or the '--skip-build'" \
152 " option must be specified" % (short_version,)
153 else:
154 self.versions = list(self.all_versions)
155
156 self.set_undefined_options('bdist',
157 ('dist_dir', 'dist_dir'),
158 ('plat_name', 'plat_name'),
159 )
160
161 if self.pre_install_script:
162 raise DistutilsOptionError, "the pre-install-script feature is not yet implemented"
163
164 if self.install_script:
165 for script in self.distribution.scripts:
166 if self.install_script == os.path.basename(script):
167 break
168 else:
169 raise DistutilsOptionError, \
170 "install_script '%s' not found in scripts" % \
171 self.install_script
172 self.install_script_key = None
173 # finalize_options()
174
175
176 def run (self):
177 if not self.skip_build:
178 self.run_command('build')
179
180 install = self.reinitialize_command('install', reinit_subcommands=1)
181 install.prefix = self.bdist_dir
182 install.skip_build = self.skip_build
183 install.warn_dir = 0
184
185 install_lib = self.reinitialize_command('install_lib')
186 # we do not want to include pyc or pyo files
187 install_lib.compile = 0
188 install_lib.optimize = 0
189
190 if self.distribution.has_ext_modules():
191 # If we are building an installer for a Python version other
192 # than the one we are currently running, then we need to ensure
193 # our build_lib reflects the other Python version rather than ours.
194 # Note that for target_version!=sys.version, we must have skipped the
195 # build step, so there is no issue with enforcing the build of this
196 # version.
197 target_version = self.target_version
198 if not target_version:
199 assert self.skip_build, "Should have already checked this"
200 target_version = sys.version[0:3]
201 plat_specifier = ".%s-%s" % (self.plat_name, target_version)
202 build = self.get_finalized_command('build')
203 build.build_lib = os.path.join(build.build_base,
204 'lib' + plat_specifier)
205
206 log.info("installing to %s", self.bdist_dir)
207 install.ensure_finalized()
208
209 # avoid warning of 'install_lib' about installing
210 # into a directory not in sys.path
211 sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB'))
212
213 install.run()
214
215 del sys.path[0]
216
217 self.mkpath(self.dist_dir)
218 fullname = self.distribution.get_fullname()
219 installer_name = self.get_installer_filename(fullname)
220 installer_name = os.path.abspath(installer_name)
221 if os.path.exists(installer_name): os.unlink(installer_name)
222
223 metadata = self.distribution.metadata
224 author = metadata.author
225 if not author:
226 author = metadata.maintainer
227 if not author:
228 author = "UNKNOWN"
229 version = metadata.get_version()
230 # ProductVersion must be strictly numeric
231 # XXX need to deal with prerelease versions
232 sversion = "%d.%d.%d" % StrictVersion(version).version
233 # Prefix ProductName with Python x.y, so that
234 # it sorts together with the other Python packages
235 # in Add-Remove-Programs (APR)
236 fullname = self.distribution.get_fullname()
237 if self.target_version:
238 product_name = "Python %s %s" % (self.target_version, fullname)
239 else:
240 product_name = "Python %s" % (fullname)
241 self.db = msilib.init_database(installer_name, schema,
242 product_name, msilib.gen_uuid(),
243 sversion, author)
244 msilib.add_tables(self.db, sequence)
245 props = [('DistVersion', version)]
246 email = metadata.author_email or metadata.maintainer_email
247 if email:
248 props.append(("ARPCONTACT", email))
249 if metadata.url:
250 props.append(("ARPURLINFOABOUT", metadata.url))
251 if props:
252 add_data(self.db, 'Property', props)
253
254 self.add_find_python()
255 self.add_files()
256 self.add_scripts()
257 self.add_ui()
258 self.db.Commit()
259
260 if hasattr(self.distribution, 'dist_files'):
261 tup = 'bdist_msi', self.target_version or 'any', fullname
262 self.distribution.dist_files.append(tup)
263
264 if not self.keep_temp:
265 remove_tree(self.bdist_dir, dry_run=self.dry_run)
266
267 def add_files(self):
268 db = self.db
269 cab = msilib.CAB("distfiles")
270 rootdir = os.path.abspath(self.bdist_dir)
271
272 root = Directory(db, cab, None, rootdir, "TARGETDIR", "SourceDir")
273 f = Feature(db, "Python", "Python", "Everything",
274 0, 1, directory="TARGETDIR")
275
276 items = [(f, root, '')]
277 for version in self.versions + [self.other_version]:
278 target = "TARGETDIR" + version
279 name = default = "Python" + version
280 desc = "Everything"
281 if version is self.other_version:
282 title = "Python from another location"
283 level = 2
284 else:
285 title = "Python %s from registry" % version
286 level = 1
287 f = Feature(db, name, title, desc, 1, level, directory=target)
288 dir = Directory(db, cab, root, rootdir, target, default)
289 items.append((f, dir, version))
290 db.Commit()
291
292 seen = {}
293 for feature, dir, version in items:
294 todo = [dir]
295 while todo:
296 dir = todo.pop()
297 for file in os.listdir(dir.absolute):
298 afile = os.path.join(dir.absolute, file)
299 if os.path.isdir(afile):
300 short = "%s|%s" % (dir.make_short(file), file)
301 default = file + version
302 newdir = Directory(db, cab, dir, file, default, short)
303 todo.append(newdir)
304 else:
305 if not dir.component:
306 dir.start_component(dir.logical, feature, 0)
307 if afile not in seen:
308 key = seen[afile] = dir.add_file(file)
309 if file==self.install_script:
310 if self.install_script_key:
311 raise DistutilsOptionError(
312 "Multiple files with name %s" % file)
313 self.install_script_key = '[#%s]' % key
314 else:
315 key = seen[afile]
316 add_data(self.db, "DuplicateFile",
317 [(key + version, dir.component, key, None, dir.logical)])
318 db.Commit()
319 cab.commit(db)
320
321 def add_find_python(self):
322 """Adds code to the installer to compute the location of Python.
323
324 Properties PYTHON.MACHINE.X.Y and PYTHON.USER.X.Y will be set from the
325 registry for each version of Python.
326
327 Properties TARGETDIRX.Y will be set from PYTHON.USER.X.Y if defined,
328 else from PYTHON.MACHINE.X.Y.
329
330 Properties PYTHONX.Y will be set to TARGETDIRX.Y\\python.exe"""
331
332 start = 402
333 for ver in self.versions:
334 install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % ver
335 machine_reg = "python.machine." + ver
336 user_reg = "python.user." + ver
337 machine_prop = "PYTHON.MACHINE." + ver
338 user_prop = "PYTHON.USER." + ver
339 machine_action = "PythonFromMachine" + ver
340 user_action = "PythonFromUser" + ver
341 exe_action = "PythonExe" + ver
342 target_dir_prop = "TARGETDIR" + ver
343 exe_prop = "PYTHON" + ver
344 if msilib.Win64:
345 # type: msidbLocatorTypeRawValue + msidbLocatorType64bit
346 Type = 2+16
347 else:
348 Type = 2
349 add_data(self.db, "RegLocator",
350 [(machine_reg, 2, install_path, None, Type),
351 (user_reg, 1, install_path, None, Type)])
352 add_data(self.db, "AppSearch",
353 [(machine_prop, machine_reg),
354 (user_prop, user_reg)])
355 add_data(self.db, "CustomAction",
356 [(machine_action, 51+256, target_dir_prop, "[" + machine_prop + "]"),
357 (user_action, 51+256, target_dir_prop, "[" + user_prop + "]"),
358 (exe_action, 51+256, exe_prop, "[" + target_dir_prop + "]\\python.exe"),
359 ])
360 add_data(self.db, "InstallExecuteSequence",
361 [(machine_action, machine_prop, start),
362 (user_action, user_prop, start + 1),
363 (exe_action, None, start + 2),
364 ])
365 add_data(self.db, "InstallUISequence",
366 [(machine_action, machine_prop, start),
367 (user_action, user_prop, start + 1),
368 (exe_action, None, start + 2),
369 ])
370 add_data(self.db, "Condition",
371 [("Python" + ver, 0, "NOT TARGETDIR" + ver)])
372 start += 4
373 assert start < 500
374
375 def add_scripts(self):
376 if self.install_script:
377 start = 6800
378 for ver in self.versions + [self.other_version]:
379 install_action = "install_script." + ver
380 exe_prop = "PYTHON" + ver
381 add_data(self.db, "CustomAction",
382 [(install_action, 50, exe_prop, self.install_script_key)])
383 add_data(self.db, "InstallExecuteSequence",
384 [(install_action, "&Python%s=3" % ver, start)])
385 start += 1
386 # XXX pre-install scripts are currently refused in finalize_options()
387 # but if this feature is completed, it will also need to add
388 # entries for each version as the above code does
389 if self.pre_install_script:
390 scriptfn = os.path.join(self.bdist_dir, "preinstall.bat")
391 f = open(scriptfn, "w")
392 # The batch file will be executed with [PYTHON], so that %1
393 # is the path to the Python interpreter; %0 will be the path
394 # of the batch file.
395 # rem ="""
396 # %1 %0
397 # exit
398 # """
399 # <actual script>
400 f.write('rem ="""\n%1 %0\nexit\n"""\n')
401 f.write(open(self.pre_install_script).read())
402 f.close()
403 add_data(self.db, "Binary",
404 [("PreInstall", msilib.Binary(scriptfn))
405 ])
406 add_data(self.db, "CustomAction",
407 [("PreInstall", 2, "PreInstall", None)
408 ])
409 add_data(self.db, "InstallExecuteSequence",
410 [("PreInstall", "NOT Installed", 450)])
411
412
413 def add_ui(self):
414 db = self.db
415 x = y = 50
416 w = 370
417 h = 300
418 title = "[ProductName] Setup"
419
420 # see "Dialog Style Bits"
421 modal = 3 # visible | modal
422 modeless = 1 # visible
423
424 # UI customization properties
425 add_data(db, "Property",
426 # See "DefaultUIFont Property"
427 [("DefaultUIFont", "DlgFont8"),
428 # See "ErrorDialog Style Bit"
429 ("ErrorDialog", "ErrorDlg"),
430 ("Progress1", "Install"), # modified in maintenance type dlg
431 ("Progress2", "installs"),
432 ("MaintenanceForm_Action", "Repair"),
433 # possible values: ALL, JUSTME
434 ("WhichUsers", "ALL")
435 ])
436
437 # Fonts, see "TextStyle Table"
438 add_data(db, "TextStyle",
439 [("DlgFont8", "Tahoma", 9, None, 0),
440 ("DlgFontBold8", "Tahoma", 8, None, 1), #bold
441 ("VerdanaBold10", "Verdana", 10, None, 1),
442 ("VerdanaRed9", "Verdana", 9, 255, 0),
443 ])
444
445 # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table"
446 # Numbers indicate sequence; see sequence.py for how these action integrate
447 add_data(db, "InstallUISequence",
448 [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140),
449 ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141),
450 # In the user interface, assume all-users installation if privileged.
451 ("SelectFeaturesDlg", "Not Installed", 1230),
452 # XXX no support for resume installations yet
453 #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240),
454 ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250),
455 ("ProgressDlg", None, 1280)])
456
457 add_data(db, 'ActionText', text.ActionText)
458 add_data(db, 'UIText', text.UIText)
459 #####################################################################
460 # Standard dialogs: FatalError, UserExit, ExitDialog
461 fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title,
462 "Finish", "Finish", "Finish")
463 fatal.title("[ProductName] Installer ended prematurely")
464 fatal.back("< Back", "Finish", active = 0)
465 fatal.cancel("Cancel", "Back", active = 0)
466 fatal.text("Description1", 15, 70, 320, 80, 0x30003,
467 "[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.")
468 fatal.text("Description2", 15, 155, 320, 20, 0x30003,
469 "Click the Finish button to exit the Installer.")
470 c=fatal.next("Finish", "Cancel", name="Finish")
471 c.event("EndDialog", "Exit")
472
473 user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title,
474 "Finish", "Finish", "Finish")
475 user_exit.title("[ProductName] Installer was interrupted")
476 user_exit.back("< Back", "Finish", active = 0)
477 user_exit.cancel("Cancel", "Back", active = 0)
478 user_exit.text("Description1", 15, 70, 320, 80, 0x30003,
479 "[ProductName] setup was interrupted. Your system has not been modified. "
480 "To install this program at a later time, please run the installation again.")
481 user_exit.text("Description2", 15, 155, 320, 20, 0x30003,
482 "Click the Finish button to exit the Installer.")
483 c = user_exit.next("Finish", "Cancel", name="Finish")
484 c.event("EndDialog", "Exit")
485
486 exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title,
487 "Finish", "Finish", "Finish")
488 exit_dialog.title("Completing the [ProductName] Installer")
489 exit_dialog.back("< Back", "Finish", active = 0)
490 exit_dialog.cancel("Cancel", "Back", active = 0)
491 exit_dialog.text("Description", 15, 235, 320, 20, 0x30003,
492 "Click the Finish button to exit the Installer.")
493 c = exit_dialog.next("Finish", "Cancel", name="Finish")
494 c.event("EndDialog", "Return")
495
496 #####################################################################
497 # Required dialog: FilesInUse, ErrorDlg
498 inuse = PyDialog(db, "FilesInUse",
499 x, y, w, h,
500 19, # KeepModeless|Modal|Visible
501 title,
502 "Retry", "Retry", "Retry", bitmap=False)
503 inuse.text("Title", 15, 6, 200, 15, 0x30003,
504 r"{\DlgFontBold8}Files in Use")
505 inuse.text("Description", 20, 23, 280, 20, 0x30003,
506 "Some files that need to be updated are currently in use.")
507 inuse.text("Text", 20, 55, 330, 50, 3,
508 "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.")
509 inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess",
510 None, None, None)
511 c=inuse.back("Exit", "Ignore", name="Exit")
512 c.event("EndDialog", "Exit")
513 c=inuse.next("Ignore", "Retry", name="Ignore")
514 c.event("EndDialog", "Ignore")
515 c=inuse.cancel("Retry", "Exit", name="Retry")
516 c.event("EndDialog","Retry")
517
518 # See "Error Dialog". See "ICE20" for the required names of the controls.
519 error = Dialog(db, "ErrorDlg",
520 50, 10, 330, 101,
521 65543, # Error|Minimize|Modal|Visible
522 title,
523 "ErrorText", None, None)
524 error.text("ErrorText", 50,9,280,48,3, "")
525 #error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None)
526 error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo")
527 error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes")
528 error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort")
529 error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel")
530 error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore")
531 error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk")
532 error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry")
533
534 #####################################################################
535 # Global "Query Cancel" dialog
536 cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title,
537 "No", "No", "No")
538 cancel.text("Text", 48, 15, 194, 30, 3,
539 "Are you sure you want to cancel [ProductName] installation?")
540 #cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
541 # "py.ico", None, None)
542 c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No")
543 c.event("EndDialog", "Exit")
544
545 c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes")
546 c.event("EndDialog", "Return")
547
548 #####################################################################
549 # Global "Wait for costing" dialog
550 costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title,
551 "Return", "Return", "Return")
552 costing.text("Text", 48, 15, 194, 30, 3,
553 "Please wait while the installer finishes determining your disk space requirements.")
554 c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None)
555 c.event("EndDialog", "Exit")
556
557 #####################################################################
558 # Preparation dialog: no user input except cancellation
559 prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title,
560 "Cancel", "Cancel", "Cancel")
561 prep.text("Description", 15, 70, 320, 40, 0x30003,
562 "Please wait while the Installer prepares to guide you through the installation.")
563 prep.title("Welcome to the [ProductName] Installer")
564 c=prep.text("ActionText", 15, 110, 320, 20, 0x30003, "Pondering...")
565 c.mapping("ActionText", "Text")
566 c=prep.text("ActionData", 15, 135, 320, 30, 0x30003, None)
567 c.mapping("ActionData", "Text")
568 prep.back("Back", None, active=0)
569 prep.next("Next", None, active=0)
570 c=prep.cancel("Cancel", None)
571 c.event("SpawnDialog", "CancelDlg")
572
573 #####################################################################
574 # Feature (Python directory) selection
575 seldlg = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal, title,
576 "Next", "Next", "Cancel")
577 seldlg.title("Select Python Installations")
578
579 seldlg.text("Hint", 15, 30, 300, 20, 3,
580 "Select the Python locations where %s should be installed."
581 % self.distribution.get_fullname())
582
583 seldlg.back("< Back", None, active=0)
584 c = seldlg.next("Next >", "Cancel")
585 order = 1
586 c.event("[TARGETDIR]", "[SourceDir]", ordering=order)
587 for version in self.versions + [self.other_version]:
588 order += 1
589 c.event("[TARGETDIR]", "[TARGETDIR%s]" % version,
590 "FEATURE_SELECTED AND &Python%s=3" % version,
591 ordering=order)
592 c.event("SpawnWaitDialog", "WaitForCostingDlg", ordering=order + 1)
593 c.event("EndDialog", "Return", ordering=order + 2)
594 c = seldlg.cancel("Cancel", "Features")
595 c.event("SpawnDialog", "CancelDlg")
596
597 c = seldlg.control("Features", "SelectionTree", 15, 60, 300, 120, 3,
598 "FEATURE", None, "PathEdit", None)
599 c.event("[FEATURE_SELECTED]", "1")
600 ver = self.other_version
601 install_other_cond = "FEATURE_SELECTED AND &Python%s=3" % ver
602 dont_install_other_cond = "FEATURE_SELECTED AND &Python%s<>3" % ver
603
604 c = seldlg.text("Other", 15, 200, 300, 15, 3,
605 "Provide an alternate Python location")
606 c.condition("Enable", install_other_cond)
607 c.condition("Show", install_other_cond)
608 c.condition("Disable", dont_install_other_cond)
609 c.condition("Hide", dont_install_other_cond)
610
611 c = seldlg.control("PathEdit", "PathEdit", 15, 215, 300, 16, 1,
612 "TARGETDIR" + ver, None, "Next", None)
613 c.condition("Enable", install_other_cond)
614 c.condition("Show", install_other_cond)
615 c.condition("Disable", dont_install_other_cond)
616 c.condition("Hide", dont_install_other_cond)
617
618 #####################################################################
619 # Disk cost
620 cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title,
621 "OK", "OK", "OK", bitmap=False)
622 cost.text("Title", 15, 6, 200, 15, 0x30003,
623 "{\DlgFontBold8}Disk Space Requirements")
624 cost.text("Description", 20, 20, 280, 20, 0x30003,
625 "The disk space required for the installation of the selected features.")
626 cost.text("Text", 20, 53, 330, 60, 3,
627 "The highlighted volumes (if any) do not have enough disk space "
628 "available for the currently selected features. You can either "
629 "remove some files from the highlighted volumes, or choose to "
630 "install less features onto local drive(s), or select different "
631 "destination drive(s).")
632 cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223,
633 None, "{120}{70}{70}{70}{70}", None, None)
634 cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return")
635
636 #####################################################################
637 # WhichUsers Dialog. Only available on NT, and for privileged users.
638 # This must be run before FindRelatedProducts, because that will
639 # take into account whether the previous installation was per-user
640 # or per-machine. We currently don't support going back to this
641 # dialog after "Next" was selected; to support this, we would need to
642 # find how to reset the ALLUSERS property, and how to re-run
643 # FindRelatedProducts.
644 # On Windows9x, the ALLUSERS property is ignored on the command line
645 # and in the Property table, but installer fails according to the documentation
646 # if a dialog attempts to set ALLUSERS.
647 whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title,
648 "AdminInstall", "Next", "Cancel")
649 whichusers.title("Select whether to install [ProductName] for all users of this computer.")
650 # A radio group with two options: allusers, justme
651 g = whichusers.radiogroup("AdminInstall", 15, 60, 260, 50, 3,
652 "WhichUsers", "", "Next")
653 g.add("ALL", 0, 5, 150, 20, "Install for all users")
654 g.add("JUSTME", 0, 25, 150, 20, "Install just for me")
655
656 whichusers.back("Back", None, active=0)
657
658 c = whichusers.next("Next >", "Cancel")
659 c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1)
660 c.event("EndDialog", "Return", ordering = 2)
661
662 c = whichusers.cancel("Cancel", "AdminInstall")
663 c.event("SpawnDialog", "CancelDlg")
664
665 #####################################################################
666 # Installation Progress dialog (modeless)
667 progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title,
668 "Cancel", "Cancel", "Cancel", bitmap=False)
669 progress.text("Title", 20, 15, 200, 15, 0x30003,
670 "{\DlgFontBold8}[Progress1] [ProductName]")
671 progress.text("Text", 35, 65, 300, 30, 3,
672 "Please wait while the Installer [Progress2] [ProductName]. "
673 "This may take several minutes.")
674 progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:")
675
676 c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...")
677 c.mapping("ActionText", "Text")
678
679 #c=progress.text("ActionData", 35, 140, 300, 20, 3, None)
680 #c.mapping("ActionData", "Text")
681
682 c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537,
683 None, "Progress done", None, None)
684 c.mapping("SetProgress", "Progress")
685
686 progress.back("< Back", "Next", active=False)
687 progress.next("Next >", "Cancel", active=False)
688 progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg")
689
690 ###################################################################
691 # Maintenance type: repair/uninstall
692 maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title,
693 "Next", "Next", "Cancel")
694 maint.title("Welcome to the [ProductName] Setup Wizard")
695 maint.text("BodyText", 15, 63, 330, 42, 3,
696 "Select whether you want to repair or remove [ProductName].")
697 g=maint.radiogroup("RepairRadioGroup", 15, 108, 330, 60, 3,
698 "MaintenanceForm_Action", "", "Next")
699 #g.add("Change", 0, 0, 200, 17, "&Change [ProductName]")
700 g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]")
701 g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]")
702
703 maint.back("< Back", None, active=False)
704 c=maint.next("Finish", "Cancel")
705 # Change installation: Change progress dialog to "Change", then ask
706 # for feature selection
707 #c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1)
708 #c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2)
709
710 # Reinstall: Change progress dialog to "Repair", then invoke reinstall
711 # Also set list of reinstalled features to "ALL"
712 c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5)
713 c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6)
714 c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7)
715 c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8)
716
717 # Uninstall: Change progress to "Remove", then invoke uninstall
718 # Also set list of removed features to "ALL"
719 c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11)
720 c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12)
721 c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13)
722 c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14)
723
724 # Close dialog when maintenance action scheduled
725 c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20)
726 #c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21)
727
728 maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg")
729
730 def get_installer_filename(self, fullname):
731 # Factored out to allow overriding in subclasses
732 if self.target_version:
733 base_name = "%s.%s-py%s.msi" % (fullname, self.plat_name,
734 self.target_version)
735 else:
736 base_name = "%s.%s.msi" % (fullname, self.plat_name)
737 installer_name = os.path.join(self.dist_dir, base_name)
738 return installer_name