blob: b831c27914404cd6abceca60d6c3f6c9ef209914 [file] [log] [blame]
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001# Python MSI Generator
2# (C) 2003 Martin v. Loewis
3# See "FOO" in comments refers to MSDN sections with the title FOO.
4import msilib, schema, sequence, os, glob, time
5from msilib import Feature, CAB, Directory, Dialog, Binary, add_data
6import uisample
7from win32com.client import constants
8
9# Settings can be overridden in config.py below
10# 1 for Itanium build
11msilib.Win64 = 0
12# 0 for official python.org releases
13# 1 for intermediate releases by anybody, with
14# a new product code for every package.
15snapshot = 1
16# 1 means that file extension is px, not py,
17# and binaries start with x
18testpackage = 0
19# Location of build tree
20srcdir = os.path.abspath("../..")
21# Text to be displayed as the version in dialogs etc.
22# goes into file name and ProductCode. Defaults to
23# current_version.day for Snapshot, current_version otherwise
24full_current_version = None
25
26try:
27 from config import *
28except ImportError:
29 pass
30
31# Extract current version from Include/patchlevel.h
32lines = open(srcdir + "/Include/patchlevel.h").readlines()
33major = minor = micro = level = serial = None
34levels = {
35 'PY_RELEASE_LEVEL_ALPHA':0xA,
36 'PY_RELEASE_LEVEL_BETA': 0xB,
37 'PY_RELEASE_LEVEL_GAMMA':0xC,
38 'PY_RELEASE_LEVEL_FINAL':0xF
39 }
40for l in lines:
41 if not l.startswith("#define"):
42 continue
43 l = l.split()
44 if len(l) != 3:
45 continue
46 _, name, value = l
47 if name == 'PY_MAJOR_VERSION': major = value
48 if name == 'PY_MINOR_VERSION': minor = value
49 if name == 'PY_MICRO_VERSION': micro = value
50 if name == 'PY_RELEASE_LEVEL': level = levels[value]
51 if name == 'PY_RELEASE_SERIAL': serial = value
52
53short_version = major+"."+minor
54# See PC/make_versioninfo.c
55FIELD3 = 1000*int(micro) + 10*level + int(serial)
56current_version = "%s.%d" % (short_version, FIELD3)
57
58# This should never change. The UpgradeCode of this package can be
59# used in the Upgrade table of future packages to make the future
60# package replace this one. See "UpgradeCode Property".
61upgrade_code_snapshot='{92A24481-3ECB-40FC-8836-04B7966EC0D5}'
62upgrade_code='{65E6DE48-A358-434D-AA4F-4AF72DB4718F}'
63
64# This should be extended for each Python release.
65# The product code must change whenever the name of the MSI file
66# changes, and when new component codes are issued for existing
67# components. See "Changing the Product Code". As we change the
68# component codes with every build, we need a new product code
69# each time. For intermediate (snapshot) releases, they are automatically
70# generated. For official releases, we record the product codes,
71# so people can refer to them.
72product_codes = {
73 '2.4.101': '{0e9b4d8e-6cda-446e-a208-7b92f3ddffa0}', # 2.4a1, released as a snapshot
74 '2.4.102': '{1b998745-4901-4edb-bc52-213689e1b922}', # 2.4a2
75 '2.4.103': '{33fc8bd2-1e8f-4add-a40a-ade2728d5942}', # 2.4a3
76 '2.4.111': '{51a7e2a8-2025-4ef0-86ff-e6aab742d1fa}', # 2.4b1
77 '2.4.112': '{4a5e7c1d-c659-4fe3-b8c9-7c65bd9c95a5}', # 2.4b2
78 '2.4.121': '{75508821-a8e9-40a8-95bd-dbe6033ddbea}', # 2.4c1
79 '2.4.122': '{83a9118b-4bdd-473b-afc3-bcb142feca9e}', # 2.4c2
80 '2.4.150': '{82d9302e-f209-4805-b548-52087047483a}', # 2.4.0
81}
82
83if snapshot:
84 current_version = "%s.%s.%s" % (major, minor, int(time.time()/3600/24))
85 product_code = msilib.gen_uuid()
86else:
87 product_code = product_codes[current_version]
88
89if full_current_version is None:
90 full_current_version = current_version
91
92extensions = [
93 'bz2.pyd',
94 'pyexpat.pyd',
95 'select.pyd',
96 'unicodedata.pyd',
97 'winsound.pyd',
98 'zlib.pyd',
99 '_bsddb.pyd',
100 '_socket.pyd',
101 '_ssl.pyd',
102 '_testcapi.pyd',
103 '_tkinter.pyd',
104]
105
106if major+minor <= "23":
107 extensions.extend([
108 '_csv.pyd',
109 '_sre.pyd',
110 '_symtable.pyd',
111 '_winreg.pyd',
112 'datetime.pyd'
113 'mmap.pyd',
114 'parser.pyd',
115 ])
116
117if testpackage:
118 ext = 'px'
119 testprefix = 'x'
120else:
121 ext = 'py'
122 testprefix = ''
123
124if msilib.Win64:
125 SystemFolderName = "[SystemFolder64]"
126else:
127 SystemFolderName = "[SystemFolder]"
128
129msilib.reset()
130
131# condition in which to install pythonxy.dll in system32:
132# a) it is Windows 9x or
133# b) it is NT, the user is privileged, and has chosen per-machine installation
134sys32cond = "(Windows9x or (Privileged and ALLUSERS))"
135
136# Install extensions if the feature goes into
137# INSTALLSTATE_ADVERTISED or INSTALLSTATE_LOCAL
138want_extensions = "(&Extensions = 1) or (&Extensions = 3)"
139
140def build_database():
141 """Generate an empty database, with just the schema and the
142 Summary information stream."""
143 if snapshot:
144 uc = upgrade_code_snapshot
145 else:
146 uc = upgrade_code
147 # schema represents the installer 2.0 database schema.
148 # sequence is the set of standard sequences
149 # (ui/execute, admin/advt/install)
150 if msilib.Win64:
151 w64 = ".ia64"
152 else:
153 w64 = ""
154 db = msilib.init_database("python-%s%s.msi" % (full_current_version, w64),
155 schema, ProductName="Python "+full_current_version,
156 ProductCode=product_code,
157 ProductVersion=current_version,
158 Manufacturer=u"Martin v. L\xf6wis")
159 # The default sequencing of the RemoveExistingProducts action causes
160 # removal of files that got just installed. Place it after
161 # InstallInitialize, so we first uninstall everything, but still roll
162 # back in case the installation is interrupted
163 msilib.change_sequence(sequence.InstallExecuteSequence,
164 "RemoveExistingProducts", 1510)
165 # Conditionalize Extension information
166 msilib.change_sequence(sequence.InstallExecuteSequence,
167 "RegisterClassInfo", cond=want_extensions)
168 msilib.change_sequence(sequence.InstallExecuteSequence,
169 "RegisterExtensionInfo", cond=want_extensions)
170 msilib.change_sequence(sequence.InstallExecuteSequence,
171 "RegisterProgIdInfo", cond=want_extensions)
172 msilib.change_sequence(sequence.InstallExecuteSequence,
173 "RegisterMIMEInfo", cond=want_extensions)
174 msilib.add_tables(db, sequence)
175 # We cannot set ALLUSERS in the property table, as this cannot be
176 # reset if the user choses a per-user installation. Instead, we
177 # maintain WhichUsers, which can be "ALL" or "JUSTME". The UI manages
178 # this property, and when the execution starts, ALLUSERS is set
179 # accordingly.
180 add_data(db, "Property", [("UpgradeCode", uc),
181 ("WhichUsers", "ALL"),
182 ])
183 db.Commit()
184 return db
185
186def remove_old_versions(db):
187 "Fill the upgrade table."
188 start = "%s.%s.0" % (major, minor)
189 # This requests that feature selection states of an older
190 # installation should be forwarded into this one. Upgrading
191 # requires that both the old and the new installation are
192 # either both per-machine or per-user.
193 migrate_features = 1
194 # See "Upgrade Table". We remove releases with the same major and
195 # minor version. For an snapshot, we remove all earlier snapshots. For
196 # a release, we remove all snapshots, and all earlier releases.
197 if snapshot:
198 add_data(db, "Upgrade",
199 [(upgrade_code_snapshot, start,
200 current_version,
201 None, # Ignore language
202 migrate_features,
203 None, # Migrate ALL features
204 "REMOVEOLDSNAPSHOT")])
205 props = "REMOVEOLDSNAPSHOT"
206 else:
207 add_data(db, "Upgrade",
208 [(upgrade_code, start, current_version,
209 None, migrate_features, None, "REMOVEOLDVERSION"),
210 (upgrade_code_snapshot, start, "%s.%d.0" % (major, int(minor)+1),
211 None, migrate_features, None, "REMOVEOLDSNAPSHOT")])
212 props = "REMOVEOLDSNAPSHOT;REMOVEOLDVERSION"
213 # Installer collects the product codes of the earlier releases in
214 # these properties. In order to allow modification of the properties,
215 # they must be declared as secure. See "SecureCustomProperties Property"
216 add_data(db, "Property", [("SecureCustomProperties", props)])
217
218class PyDialog(Dialog):
219 """Dialog class with a fixed layout: controls at the top, then a ruler,
220 then a list of buttons: back, next, cancel. Optionally a bitmap at the
221 left."""
222 def __init__(self, *args, **kw):
223 """Dialog(database, name, x, y, w, h, attributes, title, first,
224 default, cancel, bitmap=true)"""
225 Dialog.__init__(self, *args)
226 ruler = self.h - 36
227 bmwidth = 152*ruler/328
228 if kw.get("bitmap", True):
229 self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin")
230 self.line("BottomLine", 0, ruler, self.w, 0)
231
232 def title(self, title):
233 "Set the title text of the dialog at the top."
234 # name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix,
235 # text, in VerdanaBold10
236 self.text("Title", 135, 10, 220, 60, 0x30003,
237 r"{\VerdanaBold10}%s" % title)
238
239 def back(self, title, next, name = "Back", active = 1):
240 """Add a back button with a given title, the tab-next button,
241 its name in the Control table, possibly initially disabled.
242
243 Return the button, so that events can be associated"""
244 if active:
245 flags = 3 # Visible|Enabled
246 else:
247 flags = 1 # Visible
248 return self.pushbutton(name, 180, self.h-27 , 56, 17, flags, title, next)
249
250 def cancel(self, title, next, name = "Cancel", active = 1):
251 """Add a cancel button with a given title, the tab-next button,
252 its name in the Control table, possibly initially disabled.
253
254 Return the button, so that events can be associated"""
255 if active:
256 flags = 3 # Visible|Enabled
257 else:
258 flags = 1 # Visible
259 return self.pushbutton(name, 304, self.h-27, 56, 17, flags, title, next)
260
261 def next(self, title, next, name = "Next", active = 1):
262 """Add a Next button with a given title, the tab-next button,
263 its name in the Control table, possibly initially disabled.
264
265 Return the button, so that events can be associated"""
266 if active:
267 flags = 3 # Visible|Enabled
268 else:
269 flags = 1 # Visible
270 return self.pushbutton(name, 236, self.h-27, 56, 17, flags, title, next)
271
272 def xbutton(self, name, title, next, xpos):
273 """Add a button with a given title, the tab-next button,
274 its name in the Control table, giving its x position; the
275 y-position is aligned with the other buttons.
276
277 Return the button, so that events can be associated"""
278 return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next)
279
280def add_ui(db):
281 x = y = 50
282 w = 370
283 h = 300
284 title = "[ProductName] Setup"
285
286 # see "Dialog Style Bits"
287 modal = 3 # visible | modal
288 modeless = 1 # visible
289 track_disk_space = 32
290
291 add_data(db, 'ActionText', uisample.ActionText)
292 add_data(db, 'UIText', uisample.UIText)
293
294 # Bitmaps
295 if not os.path.exists(srcdir+r"\PC\python_icon.exe"):
296 raise "Run icons.mak in PC directory"
297 add_data(db, "Binary",
298 [("PythonWin", msilib.Binary(srcdir+r"\PCbuild\installer.bmp")), # 152x328 pixels
299 ("py.ico",msilib.Binary(srcdir+r"\PC\py.ico")),
300 ])
301 add_data(db, "Icon",
302 [("python_icon.exe", msilib.Binary(srcdir+r"\PC\python_icon.exe"))])
303
304 # Scripts
305 open("inst.vbs","w").write("""
306 Function CheckDir()
307 Set FSO = CreateObject("Scripting.FileSystemObject")
308 if FSO.FolderExists(Session.Property("TARGETDIR")) then
309 Session.Property("TargetExists") = "1"
310 else
311 Session.Property("TargetExists") = "0"
312 end if
313 End Function
314 """)
315 add_data(db, "Binary", [("Script", msilib.Binary("inst.vbs"))])
316 # See "Custom Action Type 6"
317 add_data(db, "CustomAction", [("CheckDir", 6, "Script", "CheckDir")])
318 os.unlink("inst.vbs")
319
320
321 # UI customization properties
322 add_data(db, "Property",
323 # See "DefaultUIFont Property"
324 [("DefaultUIFont", "DlgFont8"),
325 # See "ErrorDialog Style Bit"
326 ("ErrorDialog", "ErrorDlg"),
327 ("Progress1", "Install"), # modified in maintenance type dlg
328 ("Progress2", "installs"),
329 ("MaintenanceForm_Action", "Repair")])
330
331 # Fonts, see "TextStyle Table"
332 add_data(db, "TextStyle",
333 [("DlgFont8", "Tahoma", 9, None, 0),
334 ("DlgFontBold8", "Tahoma", 8, None, 1), #bold
335 ("VerdanaBold10", "Verdana", 10, None, 1),
336 ])
337
338 # See "CustomAction Table"
339 add_data(db, "CustomAction", [
340 # msidbCustomActionTypeFirstSequence + msidbCustomActionTypeTextData + msidbCustomActionTypeProperty
341 # See "Custom Action Type 51",
342 # "Custom Action Execution Scheduling Options"
343 ("InitialTargetDir", 307, "TARGETDIR",
344 "[WindowsVolume]Python%s%s" % (major, minor)),
345 ("SetDLLDirToTarget", 307, "DLLDIR", "[TARGETDIR]"),
346 ("SetDLLDirToSystem32", 307, "DLLDIR", SystemFolderName),
347 # msidbCustomActionTypeExe + msidbCustomActionTypeSourceFile
348 # See "Custom Action Type 18"
349 ("CompilePyc", 18, "python.exe", r"[TARGETDIR]Lib\compileall.py [TARGETDIR]Lib"),
350 ("CompilePyo", 18, "python.exe", r"-O [TARGETDIR]Lib\compileall.py [TARGETDIR]Lib")
351 ])
352
353 # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table"
354 # Numbers indicate sequence; see sequence.py for how these action integrate
355 add_data(db, "InstallUISequence",
356 [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140),
357 ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141),
358 ("InitialTargetDir", 'TARGETDIR=""', 750),
359 # In the user interface, assume all-users installation if privileged.
360 ("SetDLLDirToSystem32", 'DLLDIR="" and ' + sys32cond, 751),
361 ("SetDLLDirToTarget", 'DLLDIR="" and not ' + sys32cond, 752),
362 ("SelectDirectoryDlg", "Not Installed", 1230),
363 # XXX no support for resume installations yet
364 #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240),
365 ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250),
366 ("ProgressDlg", None, 1280)])
367 add_data(db, "AdminUISequence",
368 [("InitialTargetDir", 'TARGETDIR=""', 750),
369 ("SetDLLDirToTarget", 'DLLDIR=""', 751),
370 ])
371
372 # Execute Sequences
373 add_data(db, "InstallExecuteSequence",
374 [("InitialTargetDir", 'TARGETDIR=""', 750),
375 ("SetDLLDirToSystem32", 'DLLDIR="" and ' + sys32cond, 751),
376 ("SetDLLDirToTarget", 'DLLDIR="" and not ' + sys32cond, 752),
377 ("CompilePyc", "COMPILEALL", 6800),
378 ("CompilePyo", "COMPILEALL", 6801),
379 ])
380 add_data(db, "AdminExecuteSequence",
381 [("InitialTargetDir", 'TARGETDIR=""', 750),
382 ("SetDLLDirToTarget", 'DLLDIR=""', 751),
383 ("CompilePyc", "COMPILEALL", 6800),
384 ("CompilePyo", "COMPILEALL", 6801),
385 ])
386
387 #####################################################################
388 # Standard dialogs: FatalError, UserExit, ExitDialog
389 fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title,
390 "Finish", "Finish", "Finish")
391 fatal.title("[ProductName] Installer ended prematurely")
392 fatal.back("< Back", "Finish", active = 0)
393 fatal.cancel("Cancel", "Back", active = 0)
394 fatal.text("Description1", 135, 70, 220, 80, 0x30003,
395 "[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.")
396 fatal.text("Description2", 135, 155, 220, 20, 0x30003,
397 "Click the Finish button to exit the Installer.")
398 c=fatal.next("Finish", "Cancel", name="Finish")
399 # See "ControlEvent Table". Parameters are the event, the parameter
400 # to the action, and optionally the condition for the event, and the order
401 # of events.
402 c.event("EndDialog", "Exit")
403
404 user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title,
405 "Finish", "Finish", "Finish")
406 user_exit.title("[ProductName] Installer was interrupted")
407 user_exit.back("< Back", "Finish", active = 0)
408 user_exit.cancel("Cancel", "Back", active = 0)
409 user_exit.text("Description1", 135, 70, 220, 80, 0x30003,
410 "[ProductName] setup was interrupted. Your system has not been modified. "
411 "To install this program at a later time, please run the installation again.")
412 user_exit.text("Description2", 135, 155, 220, 20, 0x30003,
413 "Click the Finish button to exit the Installer.")
414 c = user_exit.next("Finish", "Cancel", name="Finish")
415 c.event("EndDialog", "Exit")
416
417 exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title,
418 "Finish", "Finish", "Finish")
419 exit_dialog.title("Completing the [ProductName] Installer")
420 exit_dialog.back("< Back", "Finish", active = 0)
421 exit_dialog.cancel("Cancel", "Back", active = 0)
Martin v. Löwis2dd2a282004-08-22 17:10:12 +0000422 exit_dialog.text("Acknowledgements", 135, 95, 220, 120, 0x30003,
423 "Special Windows thanks to:\n"
424 " LettError, Erik van Blokland, for the Python for Windows graphic.\n"
425 " http://www.letterror.com/\n"
426 "\n"
427 " Mark Hammond, without whose years of freely shared Windows\n"
428 " expertise, Python for Windows would still be Python for DOS.")
429
430 exit_dialog.text("Description", 135, 235, 220, 20, 0x30003,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000431 "Click the Finish button to exit the Installer.")
432 c = exit_dialog.next("Finish", "Cancel", name="Finish")
433 c.event("EndDialog", "Return")
434
435 #####################################################################
436 # Required dialog: FilesInUse, ErrorDlg
437 inuse = PyDialog(db, "FilesInUse",
438 x, y, w, h,
439 19, # KeepModeless|Modal|Visible
440 title,
441 "Retry", "Retry", "Retry", bitmap=False)
442 inuse.text("Title", 15, 6, 200, 15, 0x30003,
443 r"{\DlgFontBold8}Files in Use")
444 inuse.text("Description", 20, 23, 280, 20, 0x30003,
445 "Some files that need to be updated are currently in use.")
446 inuse.text("Text", 20, 55, 330, 50, 3,
447 "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.")
448 inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess",
449 None, None, None)
450 c=inuse.back("Exit", "Ignore", name="Exit")
451 c.event("EndDialog", "Exit")
452 c=inuse.next("Ignore", "Retry", name="Ignore")
453 c.event("EndDialog", "Ignore")
454 c=inuse.cancel("Retry", "Exit", name="Retry")
455 c.event("EndDialog","Retry")
456
457
458 # See "Error Dialog". See "ICE20" for the required names of the controls.
459 error = Dialog(db, "ErrorDlg",
460 50, 10, 330, 101,
461 65543, # Error|Minimize|Modal|Visible
462 title,
463 "ErrorText", None, None)
464 error.text("ErrorText", 50,9,280,48,3, "")
465 error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None)
466 error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo")
467 error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes")
468 error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort")
469 error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel")
470 error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore")
471 error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk")
472 error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry")
473
474 #####################################################################
475 # Global "Query Cancel" dialog
476 cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title,
477 "No", "No", "No")
478 cancel.text("Text", 48, 15, 194, 30, 3,
479 "Are you sure you want to cancel [ProductName] installation?")
480 cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
481 "py.ico", None, None)
482 c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No")
483 c.event("EndDialog", "Exit")
484
485 c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes")
486 c.event("EndDialog", "Return")
487
488 #####################################################################
489 # Global "Wait for costing" dialog
490 costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title,
491 "Return", "Return", "Return")
492 costing.text("Text", 48, 15, 194, 30, 3,
493 "Please wait while the installer finishes determining your disk space requirements.")
494 costing.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
495 "py.ico", None, None)
496 c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None)
497 c.event("EndDialog", "Exit")
498
499 #####################################################################
500 # Preparation dialog: no user input except cancellation
501 prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title,
502 "Cancel", "Cancel", "Cancel")
503 prep.text("Description", 135, 70, 220, 40, 0x30003,
504 "Please wait while the Installer prepares to guide you through the installation.")
505 prep.title("Welcome to the [ProductName] Installer")
506 c=prep.text("ActionText", 135, 110, 220, 20, 0x30003, "Pondering...")
507 c.mapping("ActionText", "Text")
508 c=prep.text("ActionData", 135, 135, 220, 30, 0x30003, None)
509 c.mapping("ActionData", "Text")
510 prep.back("Back", None, active=0)
511 prep.next("Next", None, active=0)
512 c=prep.cancel("Cancel", None)
513 c.event("SpawnDialog", "CancelDlg")
514
515 #####################################################################
516 # Target directory selection
517 seldlg = PyDialog(db, "SelectDirectoryDlg", x, y, w, h, modal, title,
518 "Next", "Next", "Cancel")
519 seldlg.title("Select Destination Directory")
520 seldlg.text("Description", 135, 50, 220, 40, 0x30003,
521 "Please select a directory for the [ProductName] files.")
522
523 seldlg.back("< Back", None, active=0)
524 c = seldlg.next("Next >", "Cancel")
525 c.event("DoAction", "CheckDir", "TargetExistsOk<>1", order=1)
526 # If the target exists, but we found that we are going to remove old versions, don't bother
527 # confirming that the target directory exists. Strictly speaking, we should determine that
528 # the target directory is indeed the target of the product that we are going to remove, but
529 # I don't know how to do that.
530 c.event("SpawnDialog", "ExistingDirectoryDlg", 'TargetExists=1 and REMOVEOLDVERSION="" and REMOVEOLDSNAPSHOT=""', 2)
531 c.event("SetTargetPath", "TARGETDIR", 'TargetExists=0 or REMOVEOLDVERSION<>"" or REMOVEOLDSNAPSHOT<>""', 3)
532 c.event("SpawnWaitDialog", "WaitForCostingDlg", "CostingComplete=1", 4)
533 c.event("NewDialog", "SelectFeaturesDlg", 'TargetExists=0 or REMOVEOLDVERSION<>"" or REMOVEOLDSNAPSHOT<>""', 5)
534
535 c = seldlg.cancel("Cancel", "DirectoryCombo")
536 c.event("SpawnDialog", "CancelDlg")
537
538 seldlg.control("DirectoryCombo", "DirectoryCombo", 135, 70, 172, 80, 393219,
539 "TARGETDIR", None, "DirectoryList", None)
540 seldlg.control("DirectoryList", "DirectoryList", 135, 90, 208, 136, 3, "TARGETDIR",
541 None, "PathEdit", None)
542 seldlg.control("PathEdit", "PathEdit", 135, 230, 206, 16, 3, "TARGETDIR", None, "Next", None)
543 c = seldlg.pushbutton("Up", 306, 70, 18, 18, 3, "Up", None)
544 c.event("DirectoryListUp", "0")
545 c = seldlg.pushbutton("NewDir", 324, 70, 30, 18, 3, "New", None)
546 c.event("DirectoryListNew", "0")
547
548 #####################################################################
549 # SelectFeaturesDlg
550 features = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal|track_disk_space,
551 title, "Tree", "Next", "Cancel")
552 features.title("Customize [ProductName]")
553 features.text("Description", 135, 35, 220, 15, 0x30003,
554 "Select the way you want features to be installed.")
555 features.text("Text", 135,45,220,30, 3,
556 "Click on the icons in the tree below to change the way features will be installed.")
557
558 c=features.back("< Back", "Next")
559 c.event("NewDialog", "SelectDirectoryDlg")
560
561 c=features.next("Next >", "Cancel")
562 c.mapping("SelectionNoItems", "Enabled")
563 c.event("SpawnDialog", "DiskCostDlg", "OutOfDiskSpace=1", order=1)
564 c.event("EndDialog", "Return", "OutOfDiskSpace<>1", order=2)
565
566 c=features.cancel("Cancel", "Tree")
567 c.event("SpawnDialog", "CancelDlg")
568
569 # The browse property is not used, since we have only a single target path (selected already)
570 features.control("Tree", "SelectionTree", 135, 75, 220, 95, 7, "_BrowseProperty",
571 "Tree of selections", "Back", None)
572
573 #c=features.pushbutton("Reset", 42, 243, 56, 17, 3, "Reset", "DiskCost")
574 #c.mapping("SelectionNoItems", "Enabled")
575 #c.event("Reset", "0")
576
577 features.control("Box", "GroupBox", 135, 170, 225, 90, 1, None, None, None, None)
578
579 c=features.xbutton("DiskCost", "Disk &Usage", None, 0.10)
580 c.mapping("SelectionNoItems","Enabled")
581 c.event("SpawnDialog", "DiskCostDlg")
582
583 c=features.xbutton("Advanced", "Advanced", None, 0.30)
584 c.event("SpawnDialog", "AdvancedDlg")
585
586 c=features.text("ItemDescription", 140, 180, 210, 30, 3,
587 "Multiline description of the currently selected item.")
588 c.mapping("SelectionDescription","Text")
589
590 c=features.text("ItemSize", 140, 210, 210, 45, 3,
591 "The size of the currently selected item.")
592 c.mapping("SelectionSize", "Text")
593
594 #####################################################################
595 # Disk cost
596 cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title,
597 "OK", "OK", "OK", bitmap=False)
598 cost.text("Title", 15, 6, 200, 15, 0x30003,
599 "{\DlgFontBold8}Disk Space Requirements")
600 cost.text("Description", 20, 20, 280, 20, 0x30003,
601 "The disk space required for the installation of the selected features.")
602 cost.text("Text", 20, 53, 330, 60, 3,
603 "The highlighted volumes (if any) do not have enough disk space "
604 "available for the currently selected features. You can either "
605 "remove some files from the highlighted volumes, or choose to "
606 "install less features onto local drive(s), or select different "
607 "destination drive(s).")
608 cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223,
609 None, "{120}{70}{70}{70}{70}", None, None)
610 cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return")
611
612 #####################################################################
613 # WhichUsers Dialog. Only available on NT, and for privileged users.
614 # This must be run before FindRelatedProducts, because that will
615 # take into account whether the previous installation was per-user
616 # or per-machine. We currently don't support going back to this
617 # dialog after "Next" was selected; to support this, we would need to
618 # find how to reset the ALLUSERS property, and how to re-run
619 # FindRelatedProducts.
620 # On Windows9x, the ALLUSERS property is ignored on the command line
621 # and in the Property table, but installer fails according to the documentation
622 # if a dialog attempts to set ALLUSERS.
623 whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title,
624 "AdminInstall", "Next", "Cancel")
625 whichusers.title("Select whether to install [ProductName] for all users of this computer.")
626 # A radio group with two options: allusers, justme
627 g = whichusers.radiogroup("AdminInstall", 135, 60, 160, 50, 3,
628 "WhichUsers", "", "Next")
629 g.add("ALL", 0, 5, 150, 20, "Install for all users")
630 g.add("JUSTME", 0, 25, 150, 20, "Install just for me")
631
632 whichusers.back("Back", None, active=0)
633
634 c = whichusers.next("Next >", "Cancel")
635 c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1)
636 c.event("EndDialog", "Return", order = 2)
637
638 c = whichusers.cancel("Cancel", "AdminInstall")
639 c.event("SpawnDialog", "CancelDlg")
640
641 #####################################################################
642 # Advanced Dialog.
643 advanced = PyDialog(db, "AdvancedDlg", x, y, w, h, modal, title,
644 "CompilePyc", "Next", "Cancel")
645 advanced.title("Advanced Options for [ProductName]")
646 # A radio group with two options: allusers, justme
647 advanced.checkbox("CompilePyc", 135, 60, 230, 50, 3,
648 "COMPILEALL", "Compile .py files to byte code after installation", "Next")
649
650 c = advanced.next("Finish", "Cancel")
651 c.event("EndDialog", "Return")
652
653 c = advanced.cancel("Cancel", "CompilePyc")
654 c.event("SpawnDialog", "CancelDlg")
655
656 #####################################################################
657 # Existing Directory dialog
658 dlg = Dialog(db, "ExistingDirectoryDlg", 50, 30, 200, 80, modal, title,
659 "No", "No", "No")
660 dlg.text("Title", 10, 20, 180, 40, 3,
661 "[TARGETDIR] exists. Are you sure you want to overwrite existing files?")
662 c=dlg.pushbutton("Yes", 30, 60, 55, 17, 3, "Yes", "No")
663 c.event("[TargetExists]", "0", order=1)
664 c.event("[TargetExistsOk]", "1", order=2)
665 c.event("EndDialog", "Return", order=3)
666 c=dlg.pushbutton("No", 115, 60, 55, 17, 3, "No", "Yes")
667 c.event("EndDialog", "Return")
668
669 #####################################################################
670 # Installation Progress dialog (modeless)
671 progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title,
672 "Cancel", "Cancel", "Cancel", bitmap=False)
673 progress.text("Title", 20, 15, 200, 15, 0x30003,
674 "{\DlgFontBold8}[Progress1] [ProductName]")
675 progress.text("Text", 35, 65, 300, 30, 3,
676 "Please wait while the Installer [Progress2] [ProductName]. "
677 "This may take several minutes.")
678 progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:")
679
680 c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...")
681 c.mapping("ActionText", "Text")
682
683 #c=progress.text("ActionData", 35, 140, 300, 20, 3, None)
684 #c.mapping("ActionData", "Text")
685
686 c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537,
687 None, "Progress done", None, None)
688 c.mapping("SetProgress", "Progress")
689
690 progress.back("< Back", "Next", active=False)
691 progress.next("Next >", "Cancel", active=False)
692 progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg")
693
694 # Maintenance type: repair/uninstall
695 maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title,
696 "Next", "Next", "Cancel")
697 maint.title("Welcome to the [ProductName] Setup Wizard")
698 maint.text("BodyText", 135, 63, 230, 42, 3,
699 "Select whether you want to repair or remove [ProductName].")
700 g=maint.radiogroup("RepairRadioGroup", 135, 108, 230, 60, 3,
701 "MaintenanceForm_Action", "", "Next")
702 g.add("Change", 0, 0, 200, 17, "&Change [ProductName]")
703 g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]")
704 g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]")
705
706 maint.back("< Back", None, active=False)
707 c=maint.next("Finish", "Cancel")
708 # Change installation: Change progress dialog to "Change", then ask
709 # for feature selection
710 c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1)
711 c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2)
712
713 # Reinstall: Change progress dialog to "Repair", then invoke reinstall
714 # Also set list of reinstalled features to "ALL"
715 c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5)
716 c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6)
717 c.event("[Progress2]", "repaires", 'MaintenanceForm_Action="Repair"', 7)
718 c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8)
719
720 # Uninstall: Change progress to "Remove", then invoke uninstall
721 # Also set list of removed features to "ALL"
722 c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11)
723 c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12)
724 c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13)
725 c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14)
726
727 # Close dialog when maintenance action scheduled
728 c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20)
729 c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21)
730
731 maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg")
732
733
734# See "Feature Table". The feature level is 1 for all features,
735# and the feature attributes are 0 for the DefaultFeature, and
736# FollowParent for all other features. The numbers are the Display
737# column.
738def add_features(db):
739 # feature attributes:
740 # msidbFeatureAttributesFollowParent == 2
741 # msidbFeatureAttributesDisallowAdvertise == 8
742 # Features that need to be installed with together with the main feature
743 # (i.e. additional Python libraries) need to follow the parent feature.
744 # Features that have no advertisement trigger (e.g. the test suite)
745 # must not support advertisement
746 global default_feature, tcltk, htmlfiles, tools, testsuite, ext_feature
747 default_feature = Feature(db, "DefaultFeature", "Python",
748 "Python Interpreter and Libraries",
749 1, directory = "TARGETDIR")
750 # The extensions feature is tricky wrt. advertisement and follow parent.
751 # The following combinations must be supported:
752 # default feature extensions effect on extensions
753 # locally/from source locally/from source registered
754 # advertised advertised/locally/from source advertised
755 # locally/from source not installed not installed
756 # advertised not installed not advertised
757 # (only shortcuts are)
758 # The following combination might be considered meaningless, but cannot be excluded
759 # locally/from source advertised registered
760 ext_feature = Feature(db, "Extensions", "Register Extensions",
761 "Make this Python installation the default Python installation", 3,
762 parent = default_feature)
763 tcltk = Feature(db, "TclTk", "Tcl/Tk", "Tkinter, IDLE, pydoc", 5,
764 parent = default_feature, attributes=2)
765 htmlfiles = Feature(db, "Documentation", "Documentation",
766 "Python HTMLHelp File", 7, parent = default_feature)
767 tools = Feature(db, "Tools", "Utility Scripts",
768 "Python utility scripts (Tools/", 9,
769 parent = default_feature, attributes=2)
770 testsuite = Feature(db, "Testsuite", "Test suite",
771 "Python test suite (Lib/test/)", 11,
772 parent = default_feature, attributes=2|8)
773
774
775def extract_msvcr71():
776 import _winreg
777 # Find the location of the merge modules
778 k = _winreg.OpenKey(
779 _winreg.HKEY_LOCAL_MACHINE,
780 r"Software\Microsoft\VisualStudio\7.1\Setup\VS")
781 dir = _winreg.QueryValueEx(k, "MSMDir")[0]
782 _winreg.CloseKey(k)
783 files = glob.glob1(dir, "*CRT71*")
784 assert len(files) == 1
785 file = os.path.join(dir, files[0])
786 # Extract msvcr71.dll
787 m = msilib.MakeMerge2()
788 m.OpenModule(file, 0)
789 m.ExtractFiles(".")
790 m.CloseModule()
791 # Find the version/language of msvcr71.dll
792 installer = msilib.MakeInstaller()
793 return installer.FileVersion("msvcr71.dll", 0), \
794 installer.FileVersion("msvcr71.dll", 1)
795
796class PyDirectory(Directory):
797 """By default, all components in the Python installer
798 can run from source."""
799 def __init__(self, *args, **kw):
800 if not kw.has_key("componentflags"):
801 kw['componentflags'] = 2 #msidbComponentAttributesOptional
802 Directory.__init__(self, *args, **kw)
803
804# See "File Table", "Component Table", "Directory Table",
805# "FeatureComponents Table"
806def add_files(db):
807 cab = CAB("python")
808 tmpfiles = []
809 # Add all executables, icons, text files into the TARGETDIR component
810 root = PyDirectory(db, cab, None, srcdir, "TARGETDIR", "SourceDir")
811 default_feature.set_current()
812 root.add_file("PCBuild/w9xpopen.exe")
813 root.add_file("PC/py.ico")
814 root.add_file("PC/pyc.ico")
815 root.add_file("README.txt", src="README")
816 root.add_file("NEWS.txt", src="Misc/NEWS")
817 root.add_file("LICENSE.txt", src="LICENSE")
818 root.start_component("python.exe", keyfile="python.exe")
819 root.add_file("PCBuild/python.exe")
820 root.start_component("pythonw.exe", keyfile="pythonw.exe")
821 root.add_file("PCBuild/pythonw.exe")
822
823 # msidbComponentAttributesSharedDllRefCount = 8, see "Component Table"
824 dlldir = PyDirectory(db, cab, root, srcdir, "DLLDIR", ".")
825 pydll = "python%s%s.dll" % (major, minor)
826 pydllsrc = srcdir + "/PCBuild/" + pydll
827 dlldir.start_component("DLLDIR", flags = 8, keyfile = pydll)
828 installer = msilib.MakeInstaller()
829 pyversion = installer.FileVersion(pydllsrc, 0)
830 if not snapshot:
831 # For releases, the Python DLL has the same version as the
832 # installer package.
833 assert pyversion.split(".")[:3] == current_version.split(".")
834 dlldir.add_file("PCBuild/python%s%s.dll" % (major, minor),
835 version=pyversion,
836 language=installer.FileVersion(pydllsrc, 1))
837 # XXX determine dependencies
838 version, lang = extract_msvcr71()
839 dlldir.start_component("msvcr71", flags=8, keyfile="msvcr71.dll")
840 dlldir.add_file("msvcr71.dll", src=os.path.abspath("msvcr71.dll"),
841 version=version, language=lang)
842 tmpfiles.append("msvcr71.dll")
843
844 # Add all .py files in Lib, except lib-tk, test
845 dirs={}
846 pydirs = [(root,"Lib")]
847 while pydirs:
848 parent, dir = pydirs.pop()
849 if dir == "CVS" or dir.startswith("plat-"):
850 continue
851 elif dir in ["lib-tk", "idlelib", "Icons"]:
852 tcltk.set_current()
853 elif dir in ['test', 'output']:
854 testsuite.set_current()
855 else:
856 default_feature.set_current()
857 lib = PyDirectory(db, cab, parent, dir, dir, "%s|%s" % (parent.make_short(dir), dir))
858 # Add additional files
859 dirs[dir]=lib
860 lib.glob("*.txt")
861 if dir=='site-packages':
862 continue
863 files = lib.glob("*.py")
864 files += lib.glob("*.pyw")
865 if files:
866 # Add an entry to the RemoveFile table to remove bytecode files.
867 lib.remove_pyc()
868 if dir=='test' and parent.physical=='Lib':
869 lib.add_file("185test.db")
870 lib.add_file("audiotest.au")
871 lib.add_file("cfgparser.1")
872 lib.add_file("test.xml")
873 lib.add_file("test.xml.out")
874 lib.add_file("testtar.tar")
875 lib.glob("*.uue")
876 lib.add_file("readme.txt", src="README")
877 if dir=='decimaltestdata':
878 lib.glob("*.decTest")
879 if dir=='output':
880 lib.glob("test_*")
881 if dir=='idlelib':
882 lib.glob("*.def")
883 lib.add_file("idle.bat")
884 if dir=="Icons":
885 lib.glob("*.gif")
886 lib.add_file("idle.icns")
887 if dir=="command":
888 lib.add_file("wininst-6.exe")
889 lib.add_file("wininst-7.1.exe")
890 if dir=="data" and parent.physical=="test" and parent.basedir.physical=="email":
891 # This should contain all non-CVS files listed in CVS
892 for f in os.listdir(lib.absolute):
893 if f.endswith(".txt") or f=="CVS":continue
894 if f.endswith(".au") or f.endswith(".gif"):
895 lib.add_file(f)
896 else:
897 print "WARNING: New file %s in email/test/data" % f
898 for f in os.listdir(lib.absolute):
899 if os.path.isdir(os.path.join(lib.absolute, f)):
900 pydirs.append((lib, f))
901 # Add DLLs
902 default_feature.set_current()
903 lib = PyDirectory(db, cab, root, srcdir+"/PCBuild", "DLLs", "DLLS|DLLs")
904 dlls = []
905 tclfiles = []
906 for f in extensions:
907 if f=="_tkinter.pyd":
908 continue
909 if not os.path.exists(srcdir+"/PCBuild/"+f):
910 print "WARNING: Missing extension", f
911 continue
912 dlls.append(f)
913 lib.add_file(f)
914 if not os.path.exists(srcdir+"/PCBuild/_tkinter.pyd"):
915 print "WARNING: Missing _tkinter.pyd"
916 else:
917 lib.start_component("TkDLLs", tcltk)
918 lib.add_file("_tkinter.pyd")
919 dlls.append("_tkinter.pyd")
920 tcldir = os.path.normpath(srcdir+"/../tcl84/bin")
921 for f in glob.glob1(tcldir, "*.dll"):
922 lib.add_file(f, src=os.path.join(tcldir, f))
923 # check whether there are any unknown extensions
924 for f in glob.glob1(srcdir+"/PCBuild", "*.pyd"):
925 if f.endswith("_d.pyd"): continue # debug version
926 if f in dlls: continue
927 print "WARNING: Unknown extension", f
928
929 # Add headers
930 default_feature.set_current()
931 lib = PyDirectory(db, cab, root, "include", "include", "INCLUDE|include")
932 lib.glob("*.h")
933 lib.add_file("pyconfig.h", src="../PC/pyconfig.h")
934 # Add import libraries
935 lib = PyDirectory(db, cab, root, "PCBuild", "libs", "LIBS|libs")
936 for f in dlls:
937 lib.add_file(f.replace('pyd','lib'))
938 lib.add_file('python%s%s.lib' % (major, minor))
939 # Add Tcl/Tk
940 tcldirs = [(root, '../tcl84/lib', 'tcl')]
941 tcltk.set_current()
942 while tcldirs:
943 parent, phys, dir = tcldirs.pop()
944 lib = PyDirectory(db, cab, parent, phys, dir, "%s|%s" % (parent.make_short(dir), dir))
945 if not os.path.exists(lib.absolute):
946 continue
947 for f in os.listdir(lib.absolute):
948 if os.path.isdir(os.path.join(lib.absolute, f)):
949 tcldirs.append((lib, f, f))
950 else:
951 lib.add_file(f)
952 # Add tools
953 tools.set_current()
954 tooldir = PyDirectory(db, cab, root, "Tools", "Tools", "TOOLS|Tools")
955 for f in ['i18n', 'pynche', 'Scripts', 'versioncheck', 'webchecker']:
956 lib = PyDirectory(db, cab, tooldir, f, f, "%s|%s" % (tooldir.make_short(f), f))
957 lib.glob("*.py")
958 lib.glob("*.pyw", exclude=['pydocgui.pyw'])
959 lib.remove_pyc()
960 lib.glob("*.txt")
961 if f == "pynche":
962 x = PyDirectory(db, cab, lib, "X", "X", "X|X")
963 x.glob("*.txt")
964 if f == 'Scripts':
965 lib.add_file("README.txt", src="README")
966 lib.start_component("pydocgui.pyw", tcltk, keyfile="pydocgui.pyw")
967 lib.add_file("pydocgui.pyw")
968 # Add documentation
969 htmlfiles.set_current()
970 lib = PyDirectory(db, cab, root, "Doc", "Doc", "DOC|Doc")
971 lib.start_component("documentation", keyfile="Python%s%s.chm" % (major,minor))
972 lib.add_file("Python%s%s.chm" % (major, minor))
973
974 cab.commit(db)
975
976 for f in tmpfiles:
977 os.unlink(f)
978
979# See "Registry Table", "Component Table"
980def add_registry(db):
981 # File extensions, associated with the REGISTRY.def component
982 # IDLE verbs depend on the tcltk feature.
983 # msidbComponentAttributesRegistryKeyPath = 4
984 # -1 for Root specifies "dependent on ALLUSERS property"
985 add_data(db, "Component",
986 # msidbComponentAttributesRegistryKeyPath = 4
987 [("REGISTRY", msilib.gen_uuid(), "TARGETDIR", 4, None,
988 "InstallPath"),
989 ("REGISTRY.def", msilib.gen_uuid(), "TARGETDIR", 4,
990 None, None),
991 ("REGISTRY.tcl", msilib.gen_uuid(), "TARGETDIR", 4,
992 "&%s <> 2" % ext_feature.id, "py.IDLE")])
993 # See "FeatureComponents Table".
994 # The association between TclTk and pythonw.exe is necessary to make ICE59
995 # happy, because the installer otherwise believes that the IDLE and PyDoc
996 # shortcuts might get installed without pythonw.exe being install. This
997 # is not true, since installing TclTk will install the default feature, which
998 # will cause pythonw.exe to be installed.
999 add_data(db, "FeatureComponents",
1000 [(default_feature.id, "REGISTRY"),
1001 (ext_feature.id, "REGISTRY.def"),
1002 (tcltk.id, "REGISTRY.tcl"),
1003 (tcltk.id, "pythonw.exe")])
1004
1005 pat = r"Software\Classes\%sPython.%sFile\shell\%s\command"
1006 ewi = "Edit with IDLE"
1007 pat2 = r"Software\Classes\%sPython.%sFile\DefaultIcon"
1008 pat3 = r"Software\Classes\%sPython.%sFile"
1009 # Advertised extensions
1010 add_data(db, "Extension",
1011 [("py", "python.exe", "Python.File", None, default_feature.id),
1012 ("pyw", "pythonw.exe", "Python.NoConFile", None, default_feature.id),
1013 ("pyc", "python.exe", "Python.CompiledFile", None, default_feature.id),
1014 ("pyo", "python.exe", "Python.CompiledFile", None, default_feature.id)])
1015 # add_data(db, "MIME") XXX
1016 add_data(db, "Verb",
1017 [("py", "open", 1, None, r'-n -e "%1"'),
1018 ("pyw", "open", 1, None, r'-n -e "%1"'),
1019 ("pyc", "open", 1, None, r'-n -e "%1"'),
1020 ("pyo", "open", 1, None, r'-n -e "%1"')])
1021 add_data(db, "ProgId",
1022 [("Python.File", None, None, "Python File", "python_icon.exe", 0),
1023 ("Python.NoConFile", None, None, "Python File (no console)", "python_icon.exe", 0),
1024 ("Python.CompiledFile", None, None, "Compiled Python File", "python_icon.exe", 1)])
1025
1026 # Non-advertised verbs: for advertised verbs, we would need to invoke the same
1027 # executable for both open and "Edit with IDLE". This cannot work, as we want
1028 # to use pythonw.exe in either case
1029 add_data(db, "Registry",
1030 [#Verbs
1031 ("py.IDLE", -1, pat % (testprefix, "", ewi), "",
1032 r'"[TARGETDIR]pythonw.exe" "[TARGETDIR]Lib\idlelib\idle.pyw" -n -e "%1"',
1033 "REGISTRY.tcl"),
1034 ("pyw.IDLE", -1, pat % (testprefix, "NoCon", ewi), "",
1035 r'"[TARGETDIR]pythonw.exe" "[TARGETDIR]Lib\idlelib\idle.pyw" -n -e "%1"',
1036 "REGISTRY.tcl"),
1037 ])
1038
1039 # Registry keys
1040 prefix = r"Software\%sPython\PythonCore\%s" % (testprefix, short_version)
1041 add_data(db, "Registry",
1042 [("InstallPath", -1, prefix+r"\InstallPath", "", "[TARGETDIR]", "REGISTRY"),
1043 ("InstallGroup", -1, prefix+r"\InstallPath\InstallGroup", "",
1044 "Python %s" % short_version, "REGISTRY"),
1045 ("PythonPath", -1, prefix+r"\PythonPath", "",
1046 "[TARGETDIR]Lib;[TARGETDIR]DLLs;[TARGETDIR]lib-tk", "REGISTRY"),
1047 ("Documentation", -1, prefix+r"\Help\Main Python Documentation", "",
1048 r"[TARGETDIR]Doc\Python%s%s.chm" % (major, minor), "REGISTRY"),
1049 ("Modules", -1, prefix+r"\Modules", "+", None, "REGISTRY"),
1050 ("AppPaths", -1, r"Software\Microsoft\Windows\CurrentVersion\App Paths\Python.exe",
1051 "", r"[TARGETDIR]Python.exe", "REGISTRY.def")
1052 ])
1053 # Shortcuts, see "Shortcut Table"
1054 add_data(db, "Directory",
1055 [("ProgramMenuFolder", "TARGETDIR", "."),
1056 ("MenuDir", "ProgramMenuFolder", "PY%s%s|%sPython %s.%s" % (major,minor,testprefix,major,minor))])
1057 add_data(db, "RemoveFile",
1058 [("MenuDir", "TARGETDIR", None, "MenuDir", 2)])
1059 add_data(db, "Shortcut",
1060 [# Advertised shortcuts: targets are features, not files
1061 ("IDLE", "MenuDir", "IDLE|IDLE (Python GUI)", "pythonw.exe",
1062 tcltk.id, r"[TARGETDIR]Lib\idlelib\idle.pyw", None, None, "python_icon.exe", 0, None, "TARGETDIR"),
1063 ("PyDoc", "MenuDir", "MODDOCS|Module Docs", "pythonw.exe",
1064 tcltk.id, r"[TARGETDIR]Tools\scripts\pydocgui.pyw", None, None, "python_icon.exe", 0, None, "TARGETDIR"),
1065 ("Python", "MenuDir", "PYTHON|Python (command line)", "python.exe",
1066 default_feature.id, None, None, None, "python_icon.exe", 2, None, "TARGETDIR"),
1067 ("Manual", "MenuDir", "MANUAL|Python Manuals", "documentation",
1068 htmlfiles.id, None, None, None, None, None, None, None),
1069 ## Non-advertised shortcuts: must be associated with a registry component
1070 ("Uninstall", "MenuDir", "UNINST|Uninstall Python", "REGISTRY",
1071 SystemFolderName+"msiexec", "/x%s" % product_code,
1072 None, None, None, None, None, None),
1073 ])
1074 db.Commit()
1075
1076db = build_database()
1077try:
1078 add_features(db)
1079 add_ui(db)
1080 add_files(db)
1081 add_registry(db)
1082 remove_old_versions(db)
1083 db.Commit()
1084finally:
1085 del db