blob: 60ce914cbbe8c9e5264e83f12160b1d95e6ab054 [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)
422 exit_dialog.text("Description", 135, 115, 220, 20, 0x30003,
423 "Click the Finish button to exit the Installer.")
424 c = exit_dialog.next("Finish", "Cancel", name="Finish")
425 c.event("EndDialog", "Return")
426
427 #####################################################################
428 # Required dialog: FilesInUse, ErrorDlg
429 inuse = PyDialog(db, "FilesInUse",
430 x, y, w, h,
431 19, # KeepModeless|Modal|Visible
432 title,
433 "Retry", "Retry", "Retry", bitmap=False)
434 inuse.text("Title", 15, 6, 200, 15, 0x30003,
435 r"{\DlgFontBold8}Files in Use")
436 inuse.text("Description", 20, 23, 280, 20, 0x30003,
437 "Some files that need to be updated are currently in use.")
438 inuse.text("Text", 20, 55, 330, 50, 3,
439 "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.")
440 inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess",
441 None, None, None)
442 c=inuse.back("Exit", "Ignore", name="Exit")
443 c.event("EndDialog", "Exit")
444 c=inuse.next("Ignore", "Retry", name="Ignore")
445 c.event("EndDialog", "Ignore")
446 c=inuse.cancel("Retry", "Exit", name="Retry")
447 c.event("EndDialog","Retry")
448
449
450 # See "Error Dialog". See "ICE20" for the required names of the controls.
451 error = Dialog(db, "ErrorDlg",
452 50, 10, 330, 101,
453 65543, # Error|Minimize|Modal|Visible
454 title,
455 "ErrorText", None, None)
456 error.text("ErrorText", 50,9,280,48,3, "")
457 error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None)
458 error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo")
459 error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes")
460 error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort")
461 error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel")
462 error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore")
463 error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk")
464 error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry")
465
466 #####################################################################
467 # Global "Query Cancel" dialog
468 cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title,
469 "No", "No", "No")
470 cancel.text("Text", 48, 15, 194, 30, 3,
471 "Are you sure you want to cancel [ProductName] installation?")
472 cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
473 "py.ico", None, None)
474 c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No")
475 c.event("EndDialog", "Exit")
476
477 c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes")
478 c.event("EndDialog", "Return")
479
480 #####################################################################
481 # Global "Wait for costing" dialog
482 costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title,
483 "Return", "Return", "Return")
484 costing.text("Text", 48, 15, 194, 30, 3,
485 "Please wait while the installer finishes determining your disk space requirements.")
486 costing.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
487 "py.ico", None, None)
488 c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None)
489 c.event("EndDialog", "Exit")
490
491 #####################################################################
492 # Preparation dialog: no user input except cancellation
493 prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title,
494 "Cancel", "Cancel", "Cancel")
495 prep.text("Description", 135, 70, 220, 40, 0x30003,
496 "Please wait while the Installer prepares to guide you through the installation.")
497 prep.title("Welcome to the [ProductName] Installer")
498 c=prep.text("ActionText", 135, 110, 220, 20, 0x30003, "Pondering...")
499 c.mapping("ActionText", "Text")
500 c=prep.text("ActionData", 135, 135, 220, 30, 0x30003, None)
501 c.mapping("ActionData", "Text")
502 prep.back("Back", None, active=0)
503 prep.next("Next", None, active=0)
504 c=prep.cancel("Cancel", None)
505 c.event("SpawnDialog", "CancelDlg")
506
507 #####################################################################
508 # Target directory selection
509 seldlg = PyDialog(db, "SelectDirectoryDlg", x, y, w, h, modal, title,
510 "Next", "Next", "Cancel")
511 seldlg.title("Select Destination Directory")
512 seldlg.text("Description", 135, 50, 220, 40, 0x30003,
513 "Please select a directory for the [ProductName] files.")
514
515 seldlg.back("< Back", None, active=0)
516 c = seldlg.next("Next >", "Cancel")
517 c.event("DoAction", "CheckDir", "TargetExistsOk<>1", order=1)
518 # If the target exists, but we found that we are going to remove old versions, don't bother
519 # confirming that the target directory exists. Strictly speaking, we should determine that
520 # the target directory is indeed the target of the product that we are going to remove, but
521 # I don't know how to do that.
522 c.event("SpawnDialog", "ExistingDirectoryDlg", 'TargetExists=1 and REMOVEOLDVERSION="" and REMOVEOLDSNAPSHOT=""', 2)
523 c.event("SetTargetPath", "TARGETDIR", 'TargetExists=0 or REMOVEOLDVERSION<>"" or REMOVEOLDSNAPSHOT<>""', 3)
524 c.event("SpawnWaitDialog", "WaitForCostingDlg", "CostingComplete=1", 4)
525 c.event("NewDialog", "SelectFeaturesDlg", 'TargetExists=0 or REMOVEOLDVERSION<>"" or REMOVEOLDSNAPSHOT<>""', 5)
526
527 c = seldlg.cancel("Cancel", "DirectoryCombo")
528 c.event("SpawnDialog", "CancelDlg")
529
530 seldlg.control("DirectoryCombo", "DirectoryCombo", 135, 70, 172, 80, 393219,
531 "TARGETDIR", None, "DirectoryList", None)
532 seldlg.control("DirectoryList", "DirectoryList", 135, 90, 208, 136, 3, "TARGETDIR",
533 None, "PathEdit", None)
534 seldlg.control("PathEdit", "PathEdit", 135, 230, 206, 16, 3, "TARGETDIR", None, "Next", None)
535 c = seldlg.pushbutton("Up", 306, 70, 18, 18, 3, "Up", None)
536 c.event("DirectoryListUp", "0")
537 c = seldlg.pushbutton("NewDir", 324, 70, 30, 18, 3, "New", None)
538 c.event("DirectoryListNew", "0")
539
540 #####################################################################
541 # SelectFeaturesDlg
542 features = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal|track_disk_space,
543 title, "Tree", "Next", "Cancel")
544 features.title("Customize [ProductName]")
545 features.text("Description", 135, 35, 220, 15, 0x30003,
546 "Select the way you want features to be installed.")
547 features.text("Text", 135,45,220,30, 3,
548 "Click on the icons in the tree below to change the way features will be installed.")
549
550 c=features.back("< Back", "Next")
551 c.event("NewDialog", "SelectDirectoryDlg")
552
553 c=features.next("Next >", "Cancel")
554 c.mapping("SelectionNoItems", "Enabled")
555 c.event("SpawnDialog", "DiskCostDlg", "OutOfDiskSpace=1", order=1)
556 c.event("EndDialog", "Return", "OutOfDiskSpace<>1", order=2)
557
558 c=features.cancel("Cancel", "Tree")
559 c.event("SpawnDialog", "CancelDlg")
560
561 # The browse property is not used, since we have only a single target path (selected already)
562 features.control("Tree", "SelectionTree", 135, 75, 220, 95, 7, "_BrowseProperty",
563 "Tree of selections", "Back", None)
564
565 #c=features.pushbutton("Reset", 42, 243, 56, 17, 3, "Reset", "DiskCost")
566 #c.mapping("SelectionNoItems", "Enabled")
567 #c.event("Reset", "0")
568
569 features.control("Box", "GroupBox", 135, 170, 225, 90, 1, None, None, None, None)
570
571 c=features.xbutton("DiskCost", "Disk &Usage", None, 0.10)
572 c.mapping("SelectionNoItems","Enabled")
573 c.event("SpawnDialog", "DiskCostDlg")
574
575 c=features.xbutton("Advanced", "Advanced", None, 0.30)
576 c.event("SpawnDialog", "AdvancedDlg")
577
578 c=features.text("ItemDescription", 140, 180, 210, 30, 3,
579 "Multiline description of the currently selected item.")
580 c.mapping("SelectionDescription","Text")
581
582 c=features.text("ItemSize", 140, 210, 210, 45, 3,
583 "The size of the currently selected item.")
584 c.mapping("SelectionSize", "Text")
585
586 #####################################################################
587 # Disk cost
588 cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title,
589 "OK", "OK", "OK", bitmap=False)
590 cost.text("Title", 15, 6, 200, 15, 0x30003,
591 "{\DlgFontBold8}Disk Space Requirements")
592 cost.text("Description", 20, 20, 280, 20, 0x30003,
593 "The disk space required for the installation of the selected features.")
594 cost.text("Text", 20, 53, 330, 60, 3,
595 "The highlighted volumes (if any) do not have enough disk space "
596 "available for the currently selected features. You can either "
597 "remove some files from the highlighted volumes, or choose to "
598 "install less features onto local drive(s), or select different "
599 "destination drive(s).")
600 cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223,
601 None, "{120}{70}{70}{70}{70}", None, None)
602 cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return")
603
604 #####################################################################
605 # WhichUsers Dialog. Only available on NT, and for privileged users.
606 # This must be run before FindRelatedProducts, because that will
607 # take into account whether the previous installation was per-user
608 # or per-machine. We currently don't support going back to this
609 # dialog after "Next" was selected; to support this, we would need to
610 # find how to reset the ALLUSERS property, and how to re-run
611 # FindRelatedProducts.
612 # On Windows9x, the ALLUSERS property is ignored on the command line
613 # and in the Property table, but installer fails according to the documentation
614 # if a dialog attempts to set ALLUSERS.
615 whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title,
616 "AdminInstall", "Next", "Cancel")
617 whichusers.title("Select whether to install [ProductName] for all users of this computer.")
618 # A radio group with two options: allusers, justme
619 g = whichusers.radiogroup("AdminInstall", 135, 60, 160, 50, 3,
620 "WhichUsers", "", "Next")
621 g.add("ALL", 0, 5, 150, 20, "Install for all users")
622 g.add("JUSTME", 0, 25, 150, 20, "Install just for me")
623
624 whichusers.back("Back", None, active=0)
625
626 c = whichusers.next("Next >", "Cancel")
627 c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1)
628 c.event("EndDialog", "Return", order = 2)
629
630 c = whichusers.cancel("Cancel", "AdminInstall")
631 c.event("SpawnDialog", "CancelDlg")
632
633 #####################################################################
634 # Advanced Dialog.
635 advanced = PyDialog(db, "AdvancedDlg", x, y, w, h, modal, title,
636 "CompilePyc", "Next", "Cancel")
637 advanced.title("Advanced Options for [ProductName]")
638 # A radio group with two options: allusers, justme
639 advanced.checkbox("CompilePyc", 135, 60, 230, 50, 3,
640 "COMPILEALL", "Compile .py files to byte code after installation", "Next")
641
642 c = advanced.next("Finish", "Cancel")
643 c.event("EndDialog", "Return")
644
645 c = advanced.cancel("Cancel", "CompilePyc")
646 c.event("SpawnDialog", "CancelDlg")
647
648 #####################################################################
649 # Existing Directory dialog
650 dlg = Dialog(db, "ExistingDirectoryDlg", 50, 30, 200, 80, modal, title,
651 "No", "No", "No")
652 dlg.text("Title", 10, 20, 180, 40, 3,
653 "[TARGETDIR] exists. Are you sure you want to overwrite existing files?")
654 c=dlg.pushbutton("Yes", 30, 60, 55, 17, 3, "Yes", "No")
655 c.event("[TargetExists]", "0", order=1)
656 c.event("[TargetExistsOk]", "1", order=2)
657 c.event("EndDialog", "Return", order=3)
658 c=dlg.pushbutton("No", 115, 60, 55, 17, 3, "No", "Yes")
659 c.event("EndDialog", "Return")
660
661 #####################################################################
662 # Installation Progress dialog (modeless)
663 progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title,
664 "Cancel", "Cancel", "Cancel", bitmap=False)
665 progress.text("Title", 20, 15, 200, 15, 0x30003,
666 "{\DlgFontBold8}[Progress1] [ProductName]")
667 progress.text("Text", 35, 65, 300, 30, 3,
668 "Please wait while the Installer [Progress2] [ProductName]. "
669 "This may take several minutes.")
670 progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:")
671
672 c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...")
673 c.mapping("ActionText", "Text")
674
675 #c=progress.text("ActionData", 35, 140, 300, 20, 3, None)
676 #c.mapping("ActionData", "Text")
677
678 c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537,
679 None, "Progress done", None, None)
680 c.mapping("SetProgress", "Progress")
681
682 progress.back("< Back", "Next", active=False)
683 progress.next("Next >", "Cancel", active=False)
684 progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg")
685
686 # Maintenance type: repair/uninstall
687 maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title,
688 "Next", "Next", "Cancel")
689 maint.title("Welcome to the [ProductName] Setup Wizard")
690 maint.text("BodyText", 135, 63, 230, 42, 3,
691 "Select whether you want to repair or remove [ProductName].")
692 g=maint.radiogroup("RepairRadioGroup", 135, 108, 230, 60, 3,
693 "MaintenanceForm_Action", "", "Next")
694 g.add("Change", 0, 0, 200, 17, "&Change [ProductName]")
695 g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]")
696 g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]")
697
698 maint.back("< Back", None, active=False)
699 c=maint.next("Finish", "Cancel")
700 # Change installation: Change progress dialog to "Change", then ask
701 # for feature selection
702 c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1)
703 c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2)
704
705 # Reinstall: Change progress dialog to "Repair", then invoke reinstall
706 # Also set list of reinstalled features to "ALL"
707 c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5)
708 c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6)
709 c.event("[Progress2]", "repaires", 'MaintenanceForm_Action="Repair"', 7)
710 c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8)
711
712 # Uninstall: Change progress to "Remove", then invoke uninstall
713 # Also set list of removed features to "ALL"
714 c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11)
715 c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12)
716 c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13)
717 c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14)
718
719 # Close dialog when maintenance action scheduled
720 c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20)
721 c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21)
722
723 maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg")
724
725
726# See "Feature Table". The feature level is 1 for all features,
727# and the feature attributes are 0 for the DefaultFeature, and
728# FollowParent for all other features. The numbers are the Display
729# column.
730def add_features(db):
731 # feature attributes:
732 # msidbFeatureAttributesFollowParent == 2
733 # msidbFeatureAttributesDisallowAdvertise == 8
734 # Features that need to be installed with together with the main feature
735 # (i.e. additional Python libraries) need to follow the parent feature.
736 # Features that have no advertisement trigger (e.g. the test suite)
737 # must not support advertisement
738 global default_feature, tcltk, htmlfiles, tools, testsuite, ext_feature
739 default_feature = Feature(db, "DefaultFeature", "Python",
740 "Python Interpreter and Libraries",
741 1, directory = "TARGETDIR")
742 # The extensions feature is tricky wrt. advertisement and follow parent.
743 # The following combinations must be supported:
744 # default feature extensions effect on extensions
745 # locally/from source locally/from source registered
746 # advertised advertised/locally/from source advertised
747 # locally/from source not installed not installed
748 # advertised not installed not advertised
749 # (only shortcuts are)
750 # The following combination might be considered meaningless, but cannot be excluded
751 # locally/from source advertised registered
752 ext_feature = Feature(db, "Extensions", "Register Extensions",
753 "Make this Python installation the default Python installation", 3,
754 parent = default_feature)
755 tcltk = Feature(db, "TclTk", "Tcl/Tk", "Tkinter, IDLE, pydoc", 5,
756 parent = default_feature, attributes=2)
757 htmlfiles = Feature(db, "Documentation", "Documentation",
758 "Python HTMLHelp File", 7, parent = default_feature)
759 tools = Feature(db, "Tools", "Utility Scripts",
760 "Python utility scripts (Tools/", 9,
761 parent = default_feature, attributes=2)
762 testsuite = Feature(db, "Testsuite", "Test suite",
763 "Python test suite (Lib/test/)", 11,
764 parent = default_feature, attributes=2|8)
765
766
767def extract_msvcr71():
768 import _winreg
769 # Find the location of the merge modules
770 k = _winreg.OpenKey(
771 _winreg.HKEY_LOCAL_MACHINE,
772 r"Software\Microsoft\VisualStudio\7.1\Setup\VS")
773 dir = _winreg.QueryValueEx(k, "MSMDir")[0]
774 _winreg.CloseKey(k)
775 files = glob.glob1(dir, "*CRT71*")
776 assert len(files) == 1
777 file = os.path.join(dir, files[0])
778 # Extract msvcr71.dll
779 m = msilib.MakeMerge2()
780 m.OpenModule(file, 0)
781 m.ExtractFiles(".")
782 m.CloseModule()
783 # Find the version/language of msvcr71.dll
784 installer = msilib.MakeInstaller()
785 return installer.FileVersion("msvcr71.dll", 0), \
786 installer.FileVersion("msvcr71.dll", 1)
787
788class PyDirectory(Directory):
789 """By default, all components in the Python installer
790 can run from source."""
791 def __init__(self, *args, **kw):
792 if not kw.has_key("componentflags"):
793 kw['componentflags'] = 2 #msidbComponentAttributesOptional
794 Directory.__init__(self, *args, **kw)
795
796# See "File Table", "Component Table", "Directory Table",
797# "FeatureComponents Table"
798def add_files(db):
799 cab = CAB("python")
800 tmpfiles = []
801 # Add all executables, icons, text files into the TARGETDIR component
802 root = PyDirectory(db, cab, None, srcdir, "TARGETDIR", "SourceDir")
803 default_feature.set_current()
804 root.add_file("PCBuild/w9xpopen.exe")
805 root.add_file("PC/py.ico")
806 root.add_file("PC/pyc.ico")
807 root.add_file("README.txt", src="README")
808 root.add_file("NEWS.txt", src="Misc/NEWS")
809 root.add_file("LICENSE.txt", src="LICENSE")
810 root.start_component("python.exe", keyfile="python.exe")
811 root.add_file("PCBuild/python.exe")
812 root.start_component("pythonw.exe", keyfile="pythonw.exe")
813 root.add_file("PCBuild/pythonw.exe")
814
815 # msidbComponentAttributesSharedDllRefCount = 8, see "Component Table"
816 dlldir = PyDirectory(db, cab, root, srcdir, "DLLDIR", ".")
817 pydll = "python%s%s.dll" % (major, minor)
818 pydllsrc = srcdir + "/PCBuild/" + pydll
819 dlldir.start_component("DLLDIR", flags = 8, keyfile = pydll)
820 installer = msilib.MakeInstaller()
821 pyversion = installer.FileVersion(pydllsrc, 0)
822 if not snapshot:
823 # For releases, the Python DLL has the same version as the
824 # installer package.
825 assert pyversion.split(".")[:3] == current_version.split(".")
826 dlldir.add_file("PCBuild/python%s%s.dll" % (major, minor),
827 version=pyversion,
828 language=installer.FileVersion(pydllsrc, 1))
829 # XXX determine dependencies
830 version, lang = extract_msvcr71()
831 dlldir.start_component("msvcr71", flags=8, keyfile="msvcr71.dll")
832 dlldir.add_file("msvcr71.dll", src=os.path.abspath("msvcr71.dll"),
833 version=version, language=lang)
834 tmpfiles.append("msvcr71.dll")
835
836 # Add all .py files in Lib, except lib-tk, test
837 dirs={}
838 pydirs = [(root,"Lib")]
839 while pydirs:
840 parent, dir = pydirs.pop()
841 if dir == "CVS" or dir.startswith("plat-"):
842 continue
843 elif dir in ["lib-tk", "idlelib", "Icons"]:
844 tcltk.set_current()
845 elif dir in ['test', 'output']:
846 testsuite.set_current()
847 else:
848 default_feature.set_current()
849 lib = PyDirectory(db, cab, parent, dir, dir, "%s|%s" % (parent.make_short(dir), dir))
850 # Add additional files
851 dirs[dir]=lib
852 lib.glob("*.txt")
853 if dir=='site-packages':
854 continue
855 files = lib.glob("*.py")
856 files += lib.glob("*.pyw")
857 if files:
858 # Add an entry to the RemoveFile table to remove bytecode files.
859 lib.remove_pyc()
860 if dir=='test' and parent.physical=='Lib':
861 lib.add_file("185test.db")
862 lib.add_file("audiotest.au")
863 lib.add_file("cfgparser.1")
864 lib.add_file("test.xml")
865 lib.add_file("test.xml.out")
866 lib.add_file("testtar.tar")
867 lib.glob("*.uue")
868 lib.add_file("readme.txt", src="README")
869 if dir=='decimaltestdata':
870 lib.glob("*.decTest")
871 if dir=='output':
872 lib.glob("test_*")
873 if dir=='idlelib':
874 lib.glob("*.def")
875 lib.add_file("idle.bat")
876 if dir=="Icons":
877 lib.glob("*.gif")
878 lib.add_file("idle.icns")
879 if dir=="command":
880 lib.add_file("wininst-6.exe")
881 lib.add_file("wininst-7.1.exe")
882 if dir=="data" and parent.physical=="test" and parent.basedir.physical=="email":
883 # This should contain all non-CVS files listed in CVS
884 for f in os.listdir(lib.absolute):
885 if f.endswith(".txt") or f=="CVS":continue
886 if f.endswith(".au") or f.endswith(".gif"):
887 lib.add_file(f)
888 else:
889 print "WARNING: New file %s in email/test/data" % f
890 for f in os.listdir(lib.absolute):
891 if os.path.isdir(os.path.join(lib.absolute, f)):
892 pydirs.append((lib, f))
893 # Add DLLs
894 default_feature.set_current()
895 lib = PyDirectory(db, cab, root, srcdir+"/PCBuild", "DLLs", "DLLS|DLLs")
896 dlls = []
897 tclfiles = []
898 for f in extensions:
899 if f=="_tkinter.pyd":
900 continue
901 if not os.path.exists(srcdir+"/PCBuild/"+f):
902 print "WARNING: Missing extension", f
903 continue
904 dlls.append(f)
905 lib.add_file(f)
906 if not os.path.exists(srcdir+"/PCBuild/_tkinter.pyd"):
907 print "WARNING: Missing _tkinter.pyd"
908 else:
909 lib.start_component("TkDLLs", tcltk)
910 lib.add_file("_tkinter.pyd")
911 dlls.append("_tkinter.pyd")
912 tcldir = os.path.normpath(srcdir+"/../tcl84/bin")
913 for f in glob.glob1(tcldir, "*.dll"):
914 lib.add_file(f, src=os.path.join(tcldir, f))
915 # check whether there are any unknown extensions
916 for f in glob.glob1(srcdir+"/PCBuild", "*.pyd"):
917 if f.endswith("_d.pyd"): continue # debug version
918 if f in dlls: continue
919 print "WARNING: Unknown extension", f
920
921 # Add headers
922 default_feature.set_current()
923 lib = PyDirectory(db, cab, root, "include", "include", "INCLUDE|include")
924 lib.glob("*.h")
925 lib.add_file("pyconfig.h", src="../PC/pyconfig.h")
926 # Add import libraries
927 lib = PyDirectory(db, cab, root, "PCBuild", "libs", "LIBS|libs")
928 for f in dlls:
929 lib.add_file(f.replace('pyd','lib'))
930 lib.add_file('python%s%s.lib' % (major, minor))
931 # Add Tcl/Tk
932 tcldirs = [(root, '../tcl84/lib', 'tcl')]
933 tcltk.set_current()
934 while tcldirs:
935 parent, phys, dir = tcldirs.pop()
936 lib = PyDirectory(db, cab, parent, phys, dir, "%s|%s" % (parent.make_short(dir), dir))
937 if not os.path.exists(lib.absolute):
938 continue
939 for f in os.listdir(lib.absolute):
940 if os.path.isdir(os.path.join(lib.absolute, f)):
941 tcldirs.append((lib, f, f))
942 else:
943 lib.add_file(f)
944 # Add tools
945 tools.set_current()
946 tooldir = PyDirectory(db, cab, root, "Tools", "Tools", "TOOLS|Tools")
947 for f in ['i18n', 'pynche', 'Scripts', 'versioncheck', 'webchecker']:
948 lib = PyDirectory(db, cab, tooldir, f, f, "%s|%s" % (tooldir.make_short(f), f))
949 lib.glob("*.py")
950 lib.glob("*.pyw", exclude=['pydocgui.pyw'])
951 lib.remove_pyc()
952 lib.glob("*.txt")
953 if f == "pynche":
954 x = PyDirectory(db, cab, lib, "X", "X", "X|X")
955 x.glob("*.txt")
956 if f == 'Scripts':
957 lib.add_file("README.txt", src="README")
958 lib.start_component("pydocgui.pyw", tcltk, keyfile="pydocgui.pyw")
959 lib.add_file("pydocgui.pyw")
960 # Add documentation
961 htmlfiles.set_current()
962 lib = PyDirectory(db, cab, root, "Doc", "Doc", "DOC|Doc")
963 lib.start_component("documentation", keyfile="Python%s%s.chm" % (major,minor))
964 lib.add_file("Python%s%s.chm" % (major, minor))
965
966 cab.commit(db)
967
968 for f in tmpfiles:
969 os.unlink(f)
970
971# See "Registry Table", "Component Table"
972def add_registry(db):
973 # File extensions, associated with the REGISTRY.def component
974 # IDLE verbs depend on the tcltk feature.
975 # msidbComponentAttributesRegistryKeyPath = 4
976 # -1 for Root specifies "dependent on ALLUSERS property"
977 add_data(db, "Component",
978 # msidbComponentAttributesRegistryKeyPath = 4
979 [("REGISTRY", msilib.gen_uuid(), "TARGETDIR", 4, None,
980 "InstallPath"),
981 ("REGISTRY.def", msilib.gen_uuid(), "TARGETDIR", 4,
982 None, None),
983 ("REGISTRY.tcl", msilib.gen_uuid(), "TARGETDIR", 4,
984 "&%s <> 2" % ext_feature.id, "py.IDLE")])
985 # See "FeatureComponents Table".
986 # The association between TclTk and pythonw.exe is necessary to make ICE59
987 # happy, because the installer otherwise believes that the IDLE and PyDoc
988 # shortcuts might get installed without pythonw.exe being install. This
989 # is not true, since installing TclTk will install the default feature, which
990 # will cause pythonw.exe to be installed.
991 add_data(db, "FeatureComponents",
992 [(default_feature.id, "REGISTRY"),
993 (ext_feature.id, "REGISTRY.def"),
994 (tcltk.id, "REGISTRY.tcl"),
995 (tcltk.id, "pythonw.exe")])
996
997 pat = r"Software\Classes\%sPython.%sFile\shell\%s\command"
998 ewi = "Edit with IDLE"
999 pat2 = r"Software\Classes\%sPython.%sFile\DefaultIcon"
1000 pat3 = r"Software\Classes\%sPython.%sFile"
1001 # Advertised extensions
1002 add_data(db, "Extension",
1003 [("py", "python.exe", "Python.File", None, default_feature.id),
1004 ("pyw", "pythonw.exe", "Python.NoConFile", None, default_feature.id),
1005 ("pyc", "python.exe", "Python.CompiledFile", None, default_feature.id),
1006 ("pyo", "python.exe", "Python.CompiledFile", None, default_feature.id)])
1007 # add_data(db, "MIME") XXX
1008 add_data(db, "Verb",
1009 [("py", "open", 1, None, r'-n -e "%1"'),
1010 ("pyw", "open", 1, None, r'-n -e "%1"'),
1011 ("pyc", "open", 1, None, r'-n -e "%1"'),
1012 ("pyo", "open", 1, None, r'-n -e "%1"')])
1013 add_data(db, "ProgId",
1014 [("Python.File", None, None, "Python File", "python_icon.exe", 0),
1015 ("Python.NoConFile", None, None, "Python File (no console)", "python_icon.exe", 0),
1016 ("Python.CompiledFile", None, None, "Compiled Python File", "python_icon.exe", 1)])
1017
1018 # Non-advertised verbs: for advertised verbs, we would need to invoke the same
1019 # executable for both open and "Edit with IDLE". This cannot work, as we want
1020 # to use pythonw.exe in either case
1021 add_data(db, "Registry",
1022 [#Verbs
1023 ("py.IDLE", -1, pat % (testprefix, "", ewi), "",
1024 r'"[TARGETDIR]pythonw.exe" "[TARGETDIR]Lib\idlelib\idle.pyw" -n -e "%1"',
1025 "REGISTRY.tcl"),
1026 ("pyw.IDLE", -1, pat % (testprefix, "NoCon", ewi), "",
1027 r'"[TARGETDIR]pythonw.exe" "[TARGETDIR]Lib\idlelib\idle.pyw" -n -e "%1"',
1028 "REGISTRY.tcl"),
1029 ])
1030
1031 # Registry keys
1032 prefix = r"Software\%sPython\PythonCore\%s" % (testprefix, short_version)
1033 add_data(db, "Registry",
1034 [("InstallPath", -1, prefix+r"\InstallPath", "", "[TARGETDIR]", "REGISTRY"),
1035 ("InstallGroup", -1, prefix+r"\InstallPath\InstallGroup", "",
1036 "Python %s" % short_version, "REGISTRY"),
1037 ("PythonPath", -1, prefix+r"\PythonPath", "",
1038 "[TARGETDIR]Lib;[TARGETDIR]DLLs;[TARGETDIR]lib-tk", "REGISTRY"),
1039 ("Documentation", -1, prefix+r"\Help\Main Python Documentation", "",
1040 r"[TARGETDIR]Doc\Python%s%s.chm" % (major, minor), "REGISTRY"),
1041 ("Modules", -1, prefix+r"\Modules", "+", None, "REGISTRY"),
1042 ("AppPaths", -1, r"Software\Microsoft\Windows\CurrentVersion\App Paths\Python.exe",
1043 "", r"[TARGETDIR]Python.exe", "REGISTRY.def")
1044 ])
1045 # Shortcuts, see "Shortcut Table"
1046 add_data(db, "Directory",
1047 [("ProgramMenuFolder", "TARGETDIR", "."),
1048 ("MenuDir", "ProgramMenuFolder", "PY%s%s|%sPython %s.%s" % (major,minor,testprefix,major,minor))])
1049 add_data(db, "RemoveFile",
1050 [("MenuDir", "TARGETDIR", None, "MenuDir", 2)])
1051 add_data(db, "Shortcut",
1052 [# Advertised shortcuts: targets are features, not files
1053 ("IDLE", "MenuDir", "IDLE|IDLE (Python GUI)", "pythonw.exe",
1054 tcltk.id, r"[TARGETDIR]Lib\idlelib\idle.pyw", None, None, "python_icon.exe", 0, None, "TARGETDIR"),
1055 ("PyDoc", "MenuDir", "MODDOCS|Module Docs", "pythonw.exe",
1056 tcltk.id, r"[TARGETDIR]Tools\scripts\pydocgui.pyw", None, None, "python_icon.exe", 0, None, "TARGETDIR"),
1057 ("Python", "MenuDir", "PYTHON|Python (command line)", "python.exe",
1058 default_feature.id, None, None, None, "python_icon.exe", 2, None, "TARGETDIR"),
1059 ("Manual", "MenuDir", "MANUAL|Python Manuals", "documentation",
1060 htmlfiles.id, None, None, None, None, None, None, None),
1061 ## Non-advertised shortcuts: must be associated with a registry component
1062 ("Uninstall", "MenuDir", "UNINST|Uninstall Python", "REGISTRY",
1063 SystemFolderName+"msiexec", "/x%s" % product_code,
1064 None, None, None, None, None, None),
1065 ])
1066 db.Commit()
1067
1068db = build_database()
1069try:
1070 add_features(db)
1071 add_ui(db)
1072 add_files(db)
1073 add_registry(db)
1074 remove_old_versions(db)
1075 db.Commit()
1076finally:
1077 del db