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