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