blob: 16f6c990163281bf72ec3ff9db076d5d6acfff9a [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
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000138def build_database():
139 """Generate an empty database, with just the schema and the
140 Summary information stream."""
141 if snapshot:
142 uc = upgrade_code_snapshot
143 else:
144 uc = upgrade_code
145 # schema represents the installer 2.0 database schema.
146 # sequence is the set of standard sequences
147 # (ui/execute, admin/advt/install)
148 if msilib.Win64:
149 w64 = ".ia64"
150 else:
151 w64 = ""
Tim Peters66cb0182004-08-26 05:23:19 +0000152 db = msilib.init_database("python-%s%s.msi" % (full_current_version, w64),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000153 schema, ProductName="Python "+full_current_version,
154 ProductCode=product_code,
155 ProductVersion=current_version,
156 Manufacturer=u"Martin v. L\xf6wis")
157 # The default sequencing of the RemoveExistingProducts action causes
158 # removal of files that got just installed. Place it after
159 # InstallInitialize, so we first uninstall everything, but still roll
160 # back in case the installation is interrupted
161 msilib.change_sequence(sequence.InstallExecuteSequence,
162 "RemoveExistingProducts", 1510)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000163 msilib.add_tables(db, sequence)
164 # We cannot set ALLUSERS in the property table, as this cannot be
165 # reset if the user choses a per-user installation. Instead, we
166 # maintain WhichUsers, which can be "ALL" or "JUSTME". The UI manages
167 # this property, and when the execution starts, ALLUSERS is set
168 # accordingly.
169 add_data(db, "Property", [("UpgradeCode", uc),
170 ("WhichUsers", "ALL"),
171 ])
172 db.Commit()
173 return db
174
175def remove_old_versions(db):
176 "Fill the upgrade table."
177 start = "%s.%s.0" % (major, minor)
178 # This requests that feature selection states of an older
179 # installation should be forwarded into this one. Upgrading
180 # requires that both the old and the new installation are
181 # either both per-machine or per-user.
182 migrate_features = 1
183 # See "Upgrade Table". We remove releases with the same major and
184 # minor version. For an snapshot, we remove all earlier snapshots. For
185 # a release, we remove all snapshots, and all earlier releases.
186 if snapshot:
187 add_data(db, "Upgrade",
Tim Peters66cb0182004-08-26 05:23:19 +0000188 [(upgrade_code_snapshot, start,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000189 current_version,
190 None, # Ignore language
Tim Peters66cb0182004-08-26 05:23:19 +0000191 migrate_features,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000192 None, # Migrate ALL features
193 "REMOVEOLDSNAPSHOT")])
194 props = "REMOVEOLDSNAPSHOT"
195 else:
196 add_data(db, "Upgrade",
197 [(upgrade_code, start, current_version,
198 None, migrate_features, None, "REMOVEOLDVERSION"),
199 (upgrade_code_snapshot, start, "%s.%d.0" % (major, int(minor)+1),
200 None, migrate_features, None, "REMOVEOLDSNAPSHOT")])
201 props = "REMOVEOLDSNAPSHOT;REMOVEOLDVERSION"
202 # Installer collects the product codes of the earlier releases in
203 # these properties. In order to allow modification of the properties,
204 # they must be declared as secure. See "SecureCustomProperties Property"
205 add_data(db, "Property", [("SecureCustomProperties", props)])
206
207class PyDialog(Dialog):
208 """Dialog class with a fixed layout: controls at the top, then a ruler,
209 then a list of buttons: back, next, cancel. Optionally a bitmap at the
210 left."""
211 def __init__(self, *args, **kw):
212 """Dialog(database, name, x, y, w, h, attributes, title, first,
213 default, cancel, bitmap=true)"""
214 Dialog.__init__(self, *args)
215 ruler = self.h - 36
216 bmwidth = 152*ruler/328
217 if kw.get("bitmap", True):
218 self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin")
219 self.line("BottomLine", 0, ruler, self.w, 0)
220
221 def title(self, title):
222 "Set the title text of the dialog at the top."
223 # name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix,
224 # text, in VerdanaBold10
225 self.text("Title", 135, 10, 220, 60, 0x30003,
226 r"{\VerdanaBold10}%s" % title)
227
228 def back(self, title, next, name = "Back", active = 1):
229 """Add a back button with a given title, the tab-next button,
230 its name in the Control table, possibly initially disabled.
231
232 Return the button, so that events can be associated"""
233 if active:
234 flags = 3 # Visible|Enabled
235 else:
236 flags = 1 # Visible
237 return self.pushbutton(name, 180, self.h-27 , 56, 17, flags, title, next)
238
239 def cancel(self, title, next, name = "Cancel", active = 1):
240 """Add a cancel 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, 304, self.h-27, 56, 17, flags, title, next)
249
250 def next(self, title, next, name = "Next", active = 1):
251 """Add a Next 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, 236, self.h-27, 56, 17, flags, title, next)
260
261 def xbutton(self, name, title, next, xpos):
262 """Add a button with a given title, the tab-next button,
263 its name in the Control table, giving its x position; the
264 y-position is aligned with the other buttons.
265
266 Return the button, so that events can be associated"""
267 return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next)
268
269def add_ui(db):
270 x = y = 50
271 w = 370
272 h = 300
273 title = "[ProductName] Setup"
274
275 # see "Dialog Style Bits"
276 modal = 3 # visible | modal
277 modeless = 1 # visible
278 track_disk_space = 32
279
280 add_data(db, 'ActionText', uisample.ActionText)
281 add_data(db, 'UIText', uisample.UIText)
282
283 # Bitmaps
284 if not os.path.exists(srcdir+r"\PC\python_icon.exe"):
285 raise "Run icons.mak in PC directory"
286 add_data(db, "Binary",
287 [("PythonWin", msilib.Binary(srcdir+r"\PCbuild\installer.bmp")), # 152x328 pixels
288 ("py.ico",msilib.Binary(srcdir+r"\PC\py.ico")),
289 ])
290 add_data(db, "Icon",
291 [("python_icon.exe", msilib.Binary(srcdir+r"\PC\python_icon.exe"))])
292
293 # Scripts
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000294 # CheckDir sets TargetExists if TARGETDIR exists.
295 # UpdateEditIDLE sets the REGISTRY.tcl component into
296 # the installed/uninstalled state according to both the
297 # Extensions and TclTk features.
Martin v. Löwiseb68be42004-12-12 15:29:21 +0000298 if os.system("nmake /nologo /c /f msisupport.mak") != 0:
299 raise "'nmake /f msisupport.mak' failed"
300 add_data(db, "Binary", [("Script", msilib.Binary("msisupport.dll"))])
301 # See "Custom Action Type 1"
Tim Peters0e9980f2004-09-12 03:49:31 +0000302 add_data(db, "CustomAction",
Martin v. Löwiseb68be42004-12-12 15:29:21 +0000303 [("CheckDir", 1, "Script", "_CheckDir@4")])
Martin v. Löwiseac02e62004-11-18 08:00:33 +0000304 if have_tcl:
305 add_data(db, "CustomAction",
Martin v. Löwiseb68be42004-12-12 15:29:21 +0000306 [("UpdateEditIDLE", 1, "Script", "_UpdateEditIDLE@4")])
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000307
308 # UI customization properties
309 add_data(db, "Property",
310 # See "DefaultUIFont Property"
311 [("DefaultUIFont", "DlgFont8"),
312 # See "ErrorDialog Style Bit"
313 ("ErrorDialog", "ErrorDlg"),
314 ("Progress1", "Install"), # modified in maintenance type dlg
315 ("Progress2", "installs"),
316 ("MaintenanceForm_Action", "Repair")])
317
318 # Fonts, see "TextStyle Table"
319 add_data(db, "TextStyle",
320 [("DlgFont8", "Tahoma", 9, None, 0),
321 ("DlgFontBold8", "Tahoma", 8, None, 1), #bold
322 ("VerdanaBold10", "Verdana", 10, None, 1),
323 ])
324
Martin v. Löwis7b2563b2004-11-02 22:59:56 +0000325 compileargs = r"-Wi [TARGETDIR]Lib\compileall.py -f -x badsyntax [TARGETDIR]Lib"
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000326 # See "CustomAction Table"
327 add_data(db, "CustomAction", [
328 # msidbCustomActionTypeFirstSequence + msidbCustomActionTypeTextData + msidbCustomActionTypeProperty
329 # See "Custom Action Type 51",
330 # "Custom Action Execution Scheduling Options"
331 ("InitialTargetDir", 307, "TARGETDIR",
332 "[WindowsVolume]Python%s%s" % (major, minor)),
333 ("SetDLLDirToTarget", 307, "DLLDIR", "[TARGETDIR]"),
334 ("SetDLLDirToSystem32", 307, "DLLDIR", SystemFolderName),
335 # msidbCustomActionTypeExe + msidbCustomActionTypeSourceFile
336 # See "Custom Action Type 18"
Martin v. Löwis7b2563b2004-11-02 22:59:56 +0000337 ("CompilePyc", 18, "python.exe", compileargs),
338 ("CompilePyo", 18, "python.exe", "-O "+compileargs),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000339 ])
340
341 # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table"
342 # Numbers indicate sequence; see sequence.py for how these action integrate
343 add_data(db, "InstallUISequence",
344 [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140),
345 ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141),
346 ("InitialTargetDir", 'TARGETDIR=""', 750),
347 # In the user interface, assume all-users installation if privileged.
348 ("SetDLLDirToSystem32", 'DLLDIR="" and ' + sys32cond, 751),
349 ("SetDLLDirToTarget", 'DLLDIR="" and not ' + sys32cond, 752),
350 ("SelectDirectoryDlg", "Not Installed", 1230),
351 # XXX no support for resume installations yet
352 #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240),
353 ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250),
354 ("ProgressDlg", None, 1280)])
355 add_data(db, "AdminUISequence",
356 [("InitialTargetDir", 'TARGETDIR=""', 750),
357 ("SetDLLDirToTarget", 'DLLDIR=""', 751),
358 ])
359
360 # Execute Sequences
361 add_data(db, "InstallExecuteSequence",
362 [("InitialTargetDir", 'TARGETDIR=""', 750),
363 ("SetDLLDirToSystem32", 'DLLDIR="" and ' + sys32cond, 751),
364 ("SetDLLDirToTarget", 'DLLDIR="" and not ' + sys32cond, 752),
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000365 ("UpdateEditIDLE", None, 1050),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000366 ("CompilePyc", "COMPILEALL", 6800),
367 ("CompilePyo", "COMPILEALL", 6801),
368 ])
369 add_data(db, "AdminExecuteSequence",
370 [("InitialTargetDir", 'TARGETDIR=""', 750),
371 ("SetDLLDirToTarget", 'DLLDIR=""', 751),
372 ("CompilePyc", "COMPILEALL", 6800),
373 ("CompilePyo", "COMPILEALL", 6801),
374 ])
375
376 #####################################################################
377 # Standard dialogs: FatalError, UserExit, ExitDialog
378 fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title,
379 "Finish", "Finish", "Finish")
380 fatal.title("[ProductName] Installer ended prematurely")
381 fatal.back("< Back", "Finish", active = 0)
382 fatal.cancel("Cancel", "Back", active = 0)
383 fatal.text("Description1", 135, 70, 220, 80, 0x30003,
384 "[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.")
385 fatal.text("Description2", 135, 155, 220, 20, 0x30003,
386 "Click the Finish button to exit the Installer.")
387 c=fatal.next("Finish", "Cancel", name="Finish")
388 # See "ControlEvent Table". Parameters are the event, the parameter
389 # to the action, and optionally the condition for the event, and the order
390 # of events.
391 c.event("EndDialog", "Exit")
Tim Peters66cb0182004-08-26 05:23:19 +0000392
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000393 user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title,
394 "Finish", "Finish", "Finish")
395 user_exit.title("[ProductName] Installer was interrupted")
396 user_exit.back("< Back", "Finish", active = 0)
397 user_exit.cancel("Cancel", "Back", active = 0)
398 user_exit.text("Description1", 135, 70, 220, 80, 0x30003,
399 "[ProductName] setup was interrupted. Your system has not been modified. "
400 "To install this program at a later time, please run the installation again.")
401 user_exit.text("Description2", 135, 155, 220, 20, 0x30003,
402 "Click the Finish button to exit the Installer.")
403 c = user_exit.next("Finish", "Cancel", name="Finish")
404 c.event("EndDialog", "Exit")
Tim Peters66cb0182004-08-26 05:23:19 +0000405
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000406 exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title,
407 "Finish", "Finish", "Finish")
408 exit_dialog.title("Completing the [ProductName] Installer")
409 exit_dialog.back("< Back", "Finish", active = 0)
410 exit_dialog.cancel("Cancel", "Back", active = 0)
Martin v. Löwis2dd2a282004-08-22 17:10:12 +0000411 exit_dialog.text("Acknowledgements", 135, 95, 220, 120, 0x30003,
412 "Special Windows thanks to:\n"
Martin v. Löwisd3f61a22004-08-30 09:22:30 +0000413 " LettError, Erik van Blokland, for the \n"
414 " Python for Windows graphic.\n"
Martin v. Löwis2dd2a282004-08-22 17:10:12 +0000415 " http://www.letterror.com/\n"
416 "\n"
Martin v. Löwisd3f61a22004-08-30 09:22:30 +0000417 " Mark Hammond, without whose years of freely \n"
418 " shared Windows expertise, Python for Windows \n"
419 " would still be Python for DOS.")
Tim Peters66cb0182004-08-26 05:23:19 +0000420
Martin v. Löwis2dd2a282004-08-22 17:10:12 +0000421 exit_dialog.text("Description", 135, 235, 220, 20, 0x30003,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000422 "Click the Finish button to exit the Installer.")
423 c = exit_dialog.next("Finish", "Cancel", name="Finish")
424 c.event("EndDialog", "Return")
425
426 #####################################################################
427 # Required dialog: FilesInUse, ErrorDlg
428 inuse = PyDialog(db, "FilesInUse",
429 x, y, w, h,
430 19, # KeepModeless|Modal|Visible
431 title,
432 "Retry", "Retry", "Retry", bitmap=False)
433 inuse.text("Title", 15, 6, 200, 15, 0x30003,
434 r"{\DlgFontBold8}Files in Use")
435 inuse.text("Description", 20, 23, 280, 20, 0x30003,
436 "Some files that need to be updated are currently in use.")
437 inuse.text("Text", 20, 55, 330, 50, 3,
438 "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.")
439 inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess",
440 None, None, None)
441 c=inuse.back("Exit", "Ignore", name="Exit")
442 c.event("EndDialog", "Exit")
443 c=inuse.next("Ignore", "Retry", name="Ignore")
444 c.event("EndDialog", "Ignore")
445 c=inuse.cancel("Retry", "Exit", name="Retry")
446 c.event("EndDialog","Retry")
447
448
449 # See "Error Dialog". See "ICE20" for the required names of the controls.
450 error = Dialog(db, "ErrorDlg",
451 50, 10, 330, 101,
452 65543, # Error|Minimize|Modal|Visible
453 title,
454 "ErrorText", None, None)
455 error.text("ErrorText", 50,9,280,48,3, "")
456 error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None)
457 error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo")
458 error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes")
459 error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort")
460 error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel")
461 error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore")
462 error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk")
463 error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry")
464
465 #####################################################################
466 # Global "Query Cancel" dialog
467 cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title,
468 "No", "No", "No")
Tim Peters66cb0182004-08-26 05:23:19 +0000469 cancel.text("Text", 48, 15, 194, 30, 3,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000470 "Are you sure you want to cancel [ProductName] installation?")
471 cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
472 "py.ico", None, None)
473 c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No")
474 c.event("EndDialog", "Exit")
Tim Peters66cb0182004-08-26 05:23:19 +0000475
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000476 c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes")
477 c.event("EndDialog", "Return")
478
479 #####################################################################
480 # Global "Wait for costing" dialog
481 costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title,
482 "Return", "Return", "Return")
483 costing.text("Text", 48, 15, 194, 30, 3,
484 "Please wait while the installer finishes determining your disk space requirements.")
485 costing.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
486 "py.ico", None, None)
487 c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None)
488 c.event("EndDialog", "Exit")
489
490 #####################################################################
491 # Preparation dialog: no user input except cancellation
492 prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title,
493 "Cancel", "Cancel", "Cancel")
494 prep.text("Description", 135, 70, 220, 40, 0x30003,
495 "Please wait while the Installer prepares to guide you through the installation.")
496 prep.title("Welcome to the [ProductName] Installer")
497 c=prep.text("ActionText", 135, 110, 220, 20, 0x30003, "Pondering...")
498 c.mapping("ActionText", "Text")
499 c=prep.text("ActionData", 135, 135, 220, 30, 0x30003, None)
500 c.mapping("ActionData", "Text")
501 prep.back("Back", None, active=0)
502 prep.next("Next", None, active=0)
503 c=prep.cancel("Cancel", None)
504 c.event("SpawnDialog", "CancelDlg")
505
506 #####################################################################
507 # Target directory selection
508 seldlg = PyDialog(db, "SelectDirectoryDlg", x, y, w, h, modal, title,
509 "Next", "Next", "Cancel")
510 seldlg.title("Select Destination Directory")
511 seldlg.text("Description", 135, 50, 220, 40, 0x30003,
512 "Please select a directory for the [ProductName] files.")
513
514 seldlg.back("< Back", None, active=0)
515 c = seldlg.next("Next >", "Cancel")
516 c.event("DoAction", "CheckDir", "TargetExistsOk<>1", order=1)
517 # If the target exists, but we found that we are going to remove old versions, don't bother
518 # confirming that the target directory exists. Strictly speaking, we should determine that
519 # the target directory is indeed the target of the product that we are going to remove, but
520 # I don't know how to do that.
521 c.event("SpawnDialog", "ExistingDirectoryDlg", 'TargetExists=1 and REMOVEOLDVERSION="" and REMOVEOLDSNAPSHOT=""', 2)
522 c.event("SetTargetPath", "TARGETDIR", 'TargetExists=0 or REMOVEOLDVERSION<>"" or REMOVEOLDSNAPSHOT<>""', 3)
523 c.event("SpawnWaitDialog", "WaitForCostingDlg", "CostingComplete=1", 4)
524 c.event("NewDialog", "SelectFeaturesDlg", 'TargetExists=0 or REMOVEOLDVERSION<>"" or REMOVEOLDSNAPSHOT<>""', 5)
525
526 c = seldlg.cancel("Cancel", "DirectoryCombo")
527 c.event("SpawnDialog", "CancelDlg")
528
529 seldlg.control("DirectoryCombo", "DirectoryCombo", 135, 70, 172, 80, 393219,
530 "TARGETDIR", None, "DirectoryList", None)
531 seldlg.control("DirectoryList", "DirectoryList", 135, 90, 208, 136, 3, "TARGETDIR",
532 None, "PathEdit", None)
533 seldlg.control("PathEdit", "PathEdit", 135, 230, 206, 16, 3, "TARGETDIR", None, "Next", None)
534 c = seldlg.pushbutton("Up", 306, 70, 18, 18, 3, "Up", None)
535 c.event("DirectoryListUp", "0")
536 c = seldlg.pushbutton("NewDir", 324, 70, 30, 18, 3, "New", None)
537 c.event("DirectoryListNew", "0")
538
539 #####################################################################
540 # SelectFeaturesDlg
541 features = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal|track_disk_space,
542 title, "Tree", "Next", "Cancel")
543 features.title("Customize [ProductName]")
544 features.text("Description", 135, 35, 220, 15, 0x30003,
545 "Select the way you want features to be installed.")
546 features.text("Text", 135,45,220,30, 3,
547 "Click on the icons in the tree below to change the way features will be installed.")
548
549 c=features.back("< Back", "Next")
550 c.event("NewDialog", "SelectDirectoryDlg")
551
552 c=features.next("Next >", "Cancel")
553 c.mapping("SelectionNoItems", "Enabled")
554 c.event("SpawnDialog", "DiskCostDlg", "OutOfDiskSpace=1", order=1)
555 c.event("EndDialog", "Return", "OutOfDiskSpace<>1", order=2)
556
557 c=features.cancel("Cancel", "Tree")
558 c.event("SpawnDialog", "CancelDlg")
559
Tim Peters66cb0182004-08-26 05:23:19 +0000560 # 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 +0000561 features.control("Tree", "SelectionTree", 135, 75, 220, 95, 7, "_BrowseProperty",
562 "Tree of selections", "Back", None)
563
564 #c=features.pushbutton("Reset", 42, 243, 56, 17, 3, "Reset", "DiskCost")
565 #c.mapping("SelectionNoItems", "Enabled")
566 #c.event("Reset", "0")
Tim Peters66cb0182004-08-26 05:23:19 +0000567
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000568 features.control("Box", "GroupBox", 135, 170, 225, 90, 1, None, None, None, None)
569
570 c=features.xbutton("DiskCost", "Disk &Usage", None, 0.10)
571 c.mapping("SelectionNoItems","Enabled")
572 c.event("SpawnDialog", "DiskCostDlg")
573
574 c=features.xbutton("Advanced", "Advanced", None, 0.30)
575 c.event("SpawnDialog", "AdvancedDlg")
576
577 c=features.text("ItemDescription", 140, 180, 210, 30, 3,
578 "Multiline description of the currently selected item.")
579 c.mapping("SelectionDescription","Text")
Tim Peters66cb0182004-08-26 05:23:19 +0000580
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000581 c=features.text("ItemSize", 140, 210, 210, 45, 3,
582 "The size of the currently selected item.")
583 c.mapping("SelectionSize", "Text")
584
585 #####################################################################
586 # Disk cost
587 cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title,
588 "OK", "OK", "OK", bitmap=False)
589 cost.text("Title", 15, 6, 200, 15, 0x30003,
590 "{\DlgFontBold8}Disk Space Requirements")
591 cost.text("Description", 20, 20, 280, 20, 0x30003,
592 "The disk space required for the installation of the selected features.")
593 cost.text("Text", 20, 53, 330, 60, 3,
594 "The highlighted volumes (if any) do not have enough disk space "
595 "available for the currently selected features. You can either "
596 "remove some files from the highlighted volumes, or choose to "
597 "install less features onto local drive(s), or select different "
598 "destination drive(s).")
599 cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223,
600 None, "{120}{70}{70}{70}{70}", None, None)
601 cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return")
602
603 #####################################################################
604 # WhichUsers Dialog. Only available on NT, and for privileged users.
605 # This must be run before FindRelatedProducts, because that will
606 # take into account whether the previous installation was per-user
607 # or per-machine. We currently don't support going back to this
608 # dialog after "Next" was selected; to support this, we would need to
609 # find how to reset the ALLUSERS property, and how to re-run
610 # FindRelatedProducts.
611 # On Windows9x, the ALLUSERS property is ignored on the command line
612 # and in the Property table, but installer fails according to the documentation
613 # if a dialog attempts to set ALLUSERS.
614 whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title,
615 "AdminInstall", "Next", "Cancel")
616 whichusers.title("Select whether to install [ProductName] for all users of this computer.")
617 # A radio group with two options: allusers, justme
618 g = whichusers.radiogroup("AdminInstall", 135, 60, 160, 50, 3,
619 "WhichUsers", "", "Next")
620 g.add("ALL", 0, 5, 150, 20, "Install for all users")
621 g.add("JUSTME", 0, 25, 150, 20, "Install just for me")
622
Tim Peters66cb0182004-08-26 05:23:19 +0000623 whichusers.back("Back", None, active=0)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000624
625 c = whichusers.next("Next >", "Cancel")
626 c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1)
627 c.event("EndDialog", "Return", order = 2)
628
629 c = whichusers.cancel("Cancel", "AdminInstall")
630 c.event("SpawnDialog", "CancelDlg")
Tim Peters66cb0182004-08-26 05:23:19 +0000631
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000632 #####################################################################
633 # Advanced Dialog.
634 advanced = PyDialog(db, "AdvancedDlg", x, y, w, h, modal, title,
635 "CompilePyc", "Next", "Cancel")
636 advanced.title("Advanced Options for [ProductName]")
637 # A radio group with two options: allusers, justme
638 advanced.checkbox("CompilePyc", 135, 60, 230, 50, 3,
639 "COMPILEALL", "Compile .py files to byte code after installation", "Next")
640
641 c = advanced.next("Finish", "Cancel")
642 c.event("EndDialog", "Return")
643
644 c = advanced.cancel("Cancel", "CompilePyc")
645 c.event("SpawnDialog", "CancelDlg")
Tim Peters66cb0182004-08-26 05:23:19 +0000646
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000647 #####################################################################
Tim Peters66cb0182004-08-26 05:23:19 +0000648 # Existing Directory dialog
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000649 dlg = Dialog(db, "ExistingDirectoryDlg", 50, 30, 200, 80, modal, title,
650 "No", "No", "No")
651 dlg.text("Title", 10, 20, 180, 40, 3,
652 "[TARGETDIR] exists. Are you sure you want to overwrite existing files?")
653 c=dlg.pushbutton("Yes", 30, 60, 55, 17, 3, "Yes", "No")
654 c.event("[TargetExists]", "0", order=1)
655 c.event("[TargetExistsOk]", "1", order=2)
656 c.event("EndDialog", "Return", order=3)
657 c=dlg.pushbutton("No", 115, 60, 55, 17, 3, "No", "Yes")
658 c.event("EndDialog", "Return")
659
660 #####################################################################
661 # Installation Progress dialog (modeless)
662 progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title,
663 "Cancel", "Cancel", "Cancel", bitmap=False)
664 progress.text("Title", 20, 15, 200, 15, 0x30003,
665 "{\DlgFontBold8}[Progress1] [ProductName]")
666 progress.text("Text", 35, 65, 300, 30, 3,
667 "Please wait while the Installer [Progress2] [ProductName]. "
668 "This may take several minutes.")
669 progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:")
670
671 c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...")
672 c.mapping("ActionText", "Text")
673
674 #c=progress.text("ActionData", 35, 140, 300, 20, 3, None)
675 #c.mapping("ActionData", "Text")
676
677 c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537,
678 None, "Progress done", None, None)
679 c.mapping("SetProgress", "Progress")
680
681 progress.back("< Back", "Next", active=False)
682 progress.next("Next >", "Cancel", active=False)
683 progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg")
684
685 # Maintenance type: repair/uninstall
686 maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title,
687 "Next", "Next", "Cancel")
688 maint.title("Welcome to the [ProductName] Setup Wizard")
689 maint.text("BodyText", 135, 63, 230, 42, 3,
690 "Select whether you want to repair or remove [ProductName].")
691 g=maint.radiogroup("RepairRadioGroup", 135, 108, 230, 60, 3,
692 "MaintenanceForm_Action", "", "Next")
693 g.add("Change", 0, 0, 200, 17, "&Change [ProductName]")
694 g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]")
695 g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]")
Tim Peters66cb0182004-08-26 05:23:19 +0000696
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000697 maint.back("< Back", None, active=False)
698 c=maint.next("Finish", "Cancel")
699 # Change installation: Change progress dialog to "Change", then ask
700 # for feature selection
701 c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1)
702 c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2)
703
704 # Reinstall: Change progress dialog to "Repair", then invoke reinstall
705 # Also set list of reinstalled features to "ALL"
706 c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5)
707 c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6)
Raymond Hettinger72f08012004-11-07 07:08:25 +0000708 c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000709 c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8)
710
711 # Uninstall: Change progress to "Remove", then invoke uninstall
712 # Also set list of removed features to "ALL"
713 c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11)
714 c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12)
715 c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13)
716 c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14)
717
Tim Peters66cb0182004-08-26 05:23:19 +0000718 # Close dialog when maintenance action scheduled
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000719 c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20)
720 c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21)
Tim Peters66cb0182004-08-26 05:23:19 +0000721
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000722 maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg")
Tim Peters66cb0182004-08-26 05:23:19 +0000723
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000724
725# See "Feature Table". The feature level is 1 for all features,
726# and the feature attributes are 0 for the DefaultFeature, and
727# FollowParent for all other features. The numbers are the Display
728# column.
729def add_features(db):
730 # feature attributes:
731 # msidbFeatureAttributesFollowParent == 2
732 # msidbFeatureAttributesDisallowAdvertise == 8
733 # Features that need to be installed with together with the main feature
734 # (i.e. additional Python libraries) need to follow the parent feature.
735 # Features that have no advertisement trigger (e.g. the test suite)
736 # must not support advertisement
737 global default_feature, tcltk, htmlfiles, tools, testsuite, ext_feature
738 default_feature = Feature(db, "DefaultFeature", "Python",
739 "Python Interpreter and Libraries",
740 1, directory = "TARGETDIR")
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000741 # We don't support advertisement of extensions
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000742 ext_feature = Feature(db, "Extensions", "Register Extensions",
743 "Make this Python installation the default Python installation", 3,
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000744 parent = default_feature, attributes=2|8)
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000745 if have_tcl:
746 tcltk = Feature(db, "TclTk", "Tcl/Tk", "Tkinter, IDLE, pydoc", 5,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000747 parent = default_feature, attributes=2)
748 htmlfiles = Feature(db, "Documentation", "Documentation",
749 "Python HTMLHelp File", 7, parent = default_feature)
750 tools = Feature(db, "Tools", "Utility Scripts",
Tim Peters66cb0182004-08-26 05:23:19 +0000751 "Python utility scripts (Tools/", 9,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000752 parent = default_feature, attributes=2)
753 testsuite = Feature(db, "Testsuite", "Test suite",
754 "Python test suite (Lib/test/)", 11,
755 parent = default_feature, attributes=2|8)
Tim Peters66cb0182004-08-26 05:23:19 +0000756
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000757def extract_msvcr71():
758 import _winreg
759 # Find the location of the merge modules
760 k = _winreg.OpenKey(
761 _winreg.HKEY_LOCAL_MACHINE,
762 r"Software\Microsoft\VisualStudio\7.1\Setup\VS")
763 dir = _winreg.QueryValueEx(k, "MSMDir")[0]
764 _winreg.CloseKey(k)
765 files = glob.glob1(dir, "*CRT71*")
766 assert len(files) == 1
767 file = os.path.join(dir, files[0])
768 # Extract msvcr71.dll
769 m = msilib.MakeMerge2()
770 m.OpenModule(file, 0)
771 m.ExtractFiles(".")
772 m.CloseModule()
773 # Find the version/language of msvcr71.dll
774 installer = msilib.MakeInstaller()
775 return installer.FileVersion("msvcr71.dll", 0), \
776 installer.FileVersion("msvcr71.dll", 1)
777
778class PyDirectory(Directory):
779 """By default, all components in the Python installer
780 can run from source."""
781 def __init__(self, *args, **kw):
782 if not kw.has_key("componentflags"):
783 kw['componentflags'] = 2 #msidbComponentAttributesOptional
784 Directory.__init__(self, *args, **kw)
785
786# See "File Table", "Component Table", "Directory Table",
787# "FeatureComponents Table"
788def add_files(db):
789 cab = CAB("python")
790 tmpfiles = []
791 # Add all executables, icons, text files into the TARGETDIR component
792 root = PyDirectory(db, cab, None, srcdir, "TARGETDIR", "SourceDir")
793 default_feature.set_current()
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000794 if not msilib.Win64:
795 root.add_file("PCBuild/w9xpopen.exe")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000796 root.add_file("PC/py.ico")
797 root.add_file("PC/pyc.ico")
798 root.add_file("README.txt", src="README")
799 root.add_file("NEWS.txt", src="Misc/NEWS")
800 root.add_file("LICENSE.txt", src="LICENSE")
801 root.start_component("python.exe", keyfile="python.exe")
802 root.add_file("PCBuild/python.exe")
803 root.start_component("pythonw.exe", keyfile="pythonw.exe")
804 root.add_file("PCBuild/pythonw.exe")
805
806 # msidbComponentAttributesSharedDllRefCount = 8, see "Component Table"
807 dlldir = PyDirectory(db, cab, root, srcdir, "DLLDIR", ".")
808 pydll = "python%s%s.dll" % (major, minor)
809 pydllsrc = srcdir + "/PCBuild/" + pydll
810 dlldir.start_component("DLLDIR", flags = 8, keyfile = pydll)
811 installer = msilib.MakeInstaller()
812 pyversion = installer.FileVersion(pydllsrc, 0)
813 if not snapshot:
814 # For releases, the Python DLL has the same version as the
815 # installer package.
816 assert pyversion.split(".")[:3] == current_version.split(".")
817 dlldir.add_file("PCBuild/python%s%s.dll" % (major, minor),
818 version=pyversion,
819 language=installer.FileVersion(pydllsrc, 1))
820 # XXX determine dependencies
821 version, lang = extract_msvcr71()
822 dlldir.start_component("msvcr71", flags=8, keyfile="msvcr71.dll")
823 dlldir.add_file("msvcr71.dll", src=os.path.abspath("msvcr71.dll"),
824 version=version, language=lang)
825 tmpfiles.append("msvcr71.dll")
Tim Peters66cb0182004-08-26 05:23:19 +0000826
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000827 # Add all .py files in Lib, except lib-tk, test
828 dirs={}
829 pydirs = [(root,"Lib")]
830 while pydirs:
831 parent, dir = pydirs.pop()
832 if dir == "CVS" or dir.startswith("plat-"):
833 continue
834 elif dir in ["lib-tk", "idlelib", "Icons"]:
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000835 if not have_tcl:
836 continue
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000837 tcltk.set_current()
838 elif dir in ['test', 'output']:
839 testsuite.set_current()
840 else:
841 default_feature.set_current()
842 lib = PyDirectory(db, cab, parent, dir, dir, "%s|%s" % (parent.make_short(dir), dir))
843 # Add additional files
844 dirs[dir]=lib
845 lib.glob("*.txt")
846 if dir=='site-packages':
Martin v. Löwis6d60c092004-11-21 10:16:26 +0000847 lib.add_file("README.txt", src="README")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000848 continue
849 files = lib.glob("*.py")
850 files += lib.glob("*.pyw")
851 if files:
852 # Add an entry to the RemoveFile table to remove bytecode files.
853 lib.remove_pyc()
854 if dir=='test' and parent.physical=='Lib':
855 lib.add_file("185test.db")
856 lib.add_file("audiotest.au")
857 lib.add_file("cfgparser.1")
858 lib.add_file("test.xml")
859 lib.add_file("test.xml.out")
860 lib.add_file("testtar.tar")
Martin v. Löwis7d3755d2004-09-06 06:31:12 +0000861 lib.add_file("test_difflib_expect.html")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000862 lib.glob("*.uue")
863 lib.add_file("readme.txt", src="README")
864 if dir=='decimaltestdata':
865 lib.glob("*.decTest")
866 if dir=='output':
867 lib.glob("test_*")
868 if dir=='idlelib':
869 lib.glob("*.def")
870 lib.add_file("idle.bat")
871 if dir=="Icons":
872 lib.glob("*.gif")
873 lib.add_file("idle.icns")
874 if dir=="command":
875 lib.add_file("wininst-6.exe")
876 lib.add_file("wininst-7.1.exe")
877 if dir=="data" and parent.physical=="test" and parent.basedir.physical=="email":
878 # This should contain all non-CVS files listed in CVS
879 for f in os.listdir(lib.absolute):
880 if f.endswith(".txt") or f=="CVS":continue
881 if f.endswith(".au") or f.endswith(".gif"):
882 lib.add_file(f)
883 else:
884 print "WARNING: New file %s in email/test/data" % f
885 for f in os.listdir(lib.absolute):
886 if os.path.isdir(os.path.join(lib.absolute, f)):
887 pydirs.append((lib, f))
888 # Add DLLs
889 default_feature.set_current()
890 lib = PyDirectory(db, cab, root, srcdir+"/PCBuild", "DLLs", "DLLS|DLLs")
891 dlls = []
892 tclfiles = []
893 for f in extensions:
894 if f=="_tkinter.pyd":
895 continue
896 if not os.path.exists(srcdir+"/PCBuild/"+f):
897 print "WARNING: Missing extension", f
898 continue
899 dlls.append(f)
900 lib.add_file(f)
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000901 if have_tcl:
902 if not os.path.exists(srcdir+"/PCBuild/_tkinter.pyd"):
903 print "WARNING: Missing _tkinter.pyd"
904 else:
905 lib.start_component("TkDLLs", tcltk)
906 lib.add_file("_tkinter.pyd")
907 dlls.append("_tkinter.pyd")
908 tcldir = os.path.normpath(srcdir+"/../tcltk/bin")
909 for f in glob.glob1(tcldir, "*.dll"):
910 lib.add_file(f, src=os.path.join(tcldir, f))
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000911 # check whether there are any unknown extensions
912 for f in glob.glob1(srcdir+"/PCBuild", "*.pyd"):
913 if f.endswith("_d.pyd"): continue # debug version
914 if f in dlls: continue
915 print "WARNING: Unknown extension", f
Tim Peters66cb0182004-08-26 05:23:19 +0000916
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000917 # Add headers
918 default_feature.set_current()
919 lib = PyDirectory(db, cab, root, "include", "include", "INCLUDE|include")
920 lib.glob("*.h")
921 lib.add_file("pyconfig.h", src="../PC/pyconfig.h")
922 # Add import libraries
923 lib = PyDirectory(db, cab, root, "PCBuild", "libs", "LIBS|libs")
924 for f in dlls:
925 lib.add_file(f.replace('pyd','lib'))
926 lib.add_file('python%s%s.lib' % (major, minor))
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000927 if have_tcl:
928 # Add Tcl/Tk
929 tcldirs = [(root, '../tcltk/lib', 'tcl')]
930 tcltk.set_current()
931 while tcldirs:
932 parent, phys, dir = tcldirs.pop()
933 lib = PyDirectory(db, cab, parent, phys, dir, "%s|%s" % (parent.make_short(dir), dir))
934 if not os.path.exists(lib.absolute):
935 continue
936 for f in os.listdir(lib.absolute):
937 if os.path.isdir(os.path.join(lib.absolute, f)):
938 tcldirs.append((lib, f, f))
939 else:
940 lib.add_file(f)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000941 # Add tools
942 tools.set_current()
943 tooldir = PyDirectory(db, cab, root, "Tools", "Tools", "TOOLS|Tools")
944 for f in ['i18n', 'pynche', 'Scripts', 'versioncheck', 'webchecker']:
945 lib = PyDirectory(db, cab, tooldir, f, f, "%s|%s" % (tooldir.make_short(f), f))
946 lib.glob("*.py")
947 lib.glob("*.pyw", exclude=['pydocgui.pyw'])
948 lib.remove_pyc()
949 lib.glob("*.txt")
950 if f == "pynche":
951 x = PyDirectory(db, cab, lib, "X", "X", "X|X")
952 x.glob("*.txt")
Martin v. Löwis4d930be2004-12-01 21:46:35 +0000953 if os.path.exists(os.path.join(lib.absolute, "README")):
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000954 lib.add_file("README.txt", src="README")
Martin v. Löwis4d930be2004-12-01 21:46:35 +0000955 if f == 'Scripts':
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000956 if have_tcl:
957 lib.start_component("pydocgui.pyw", tcltk, keyfile="pydocgui.pyw")
958 lib.add_file("pydocgui.pyw")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000959 # Add documentation
960 htmlfiles.set_current()
961 lib = PyDirectory(db, cab, root, "Doc", "Doc", "DOC|Doc")
962 lib.start_component("documentation", keyfile="Python%s%s.chm" % (major,minor))
963 lib.add_file("Python%s%s.chm" % (major, minor))
964
965 cab.commit(db)
966
967 for f in tmpfiles:
968 os.unlink(f)
969
970# See "Registry Table", "Component Table"
971def add_registry(db):
972 # File extensions, associated with the REGISTRY.def component
973 # IDLE verbs depend on the tcltk feature.
974 # msidbComponentAttributesRegistryKeyPath = 4
975 # -1 for Root specifies "dependent on ALLUSERS property"
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000976 tcldata = []
977 if have_tcl:
978 tcldata = [
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000979 ("REGISTRY.tcl", msilib.gen_uuid(), "TARGETDIR", 4, None,
980 "py.IDLE")]
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000981 add_data(db, "Component",
982 # msidbComponentAttributesRegistryKeyPath = 4
983 [("REGISTRY", msilib.gen_uuid(), "TARGETDIR", 4, None,
984 "InstallPath"),
985 ("REGISTRY.def", msilib.gen_uuid(), "TARGETDIR", 4,
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000986 None, None)] + tcldata)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000987 # See "FeatureComponents Table".
988 # The association between TclTk and pythonw.exe is necessary to make ICE59
989 # happy, because the installer otherwise believes that the IDLE and PyDoc
990 # shortcuts might get installed without pythonw.exe being install. This
991 # is not true, since installing TclTk will install the default feature, which
992 # will cause pythonw.exe to be installed.
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000993 # REGISTRY.tcl is not associated with any feature, as it will be requested
994 # through a custom action
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000995 tcldata = []
996 if have_tcl:
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000997 tcldata = [(tcltk.id, "pythonw.exe")]
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000998 add_data(db, "FeatureComponents",
999 [(default_feature.id, "REGISTRY"),
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001000 (ext_feature.id, "REGISTRY.def")] +
1001 tcldata
1002 )
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001003 # Extensions are not advertised. For advertised extensions,
1004 # we would need separate binaries that install along with the
1005 # extension.
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001006 pat = r"Software\Classes\%sPython.%sFile\shell\%s\command"
1007 ewi = "Edit with IDLE"
1008 pat2 = r"Software\Classes\%sPython.%sFile\DefaultIcon"
1009 pat3 = r"Software\Classes\%sPython.%sFile"
Martin v. Löwiseac02e62004-11-18 08:00:33 +00001010 tcl_verbs = []
1011 if have_tcl:
1012 tcl_verbs=[
1013 ("py.IDLE", -1, pat % (testprefix, "", ewi), "",
1014 r'"[TARGETDIR]pythonw.exe" "[TARGETDIR]Lib\idlelib\idle.pyw" -n -e "%1"',
1015 "REGISTRY.tcl"),
1016 ("pyw.IDLE", -1, pat % (testprefix, "NoCon", ewi), "",
1017 r'"[TARGETDIR]pythonw.exe" "[TARGETDIR]Lib\idlelib\idle.pyw" -n -e "%1"',
1018 "REGISTRY.tcl"),
1019 ]
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001020 add_data(db, "Registry",
1021 [# Extensions
1022 ("py.ext", -1, r"Software\Classes\."+ext, "",
1023 "Python.File", "REGISTRY.def"),
1024 ("pyw.ext", -1, r"Software\Classes\."+ext+'w', "",
1025 "Python.NoConFile", "REGISTRY.def"),
1026 ("pyc.ext", -1, r"Software\Classes\."+ext+'c', "",
1027 "Python.CompiledFile", "REGISTRY.def"),
1028 ("pyo.ext", -1, r"Software\Classes\."+ext+'o', "",
1029 "Python.CompiledFile", "REGISTRY.def"),
1030 # MIME types
1031 ("py.mime", -1, r"Software\Classes\."+ext, "Content Type",
1032 "text/plain", "REGISTRY.def"),
1033 ("pyw.mime", -1, r"Software\Classes\."+ext+'w', "Content Type",
1034 "text/plain", "REGISTRY.def"),
1035 #Verbs
1036 ("py.open", -1, pat % (testprefix, "", "open"), "",
1037 r'"[TARGETDIR]python.exe" "%1" %*', "REGISTRY.def"),
1038 ("pyw.open", -1, pat % (testprefix, "NoCon", "open"), "",
1039 r'"[TARGETDIR]pythonw.exe" "%1" %*', "REGISTRY.def"),
1040 ("pyc.open", -1, pat % (testprefix, "Compiled", "open"), "",
1041 r'"[TARGETDIR]python.exe" "%1" %*', "REGISTRY.def"),
Martin v. Löwiseac02e62004-11-18 08:00:33 +00001042 ] + tcl_verbs + [
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001043 #Icons
1044 ("py.icon", -1, pat2 % (testprefix, ""), "",
1045 r'[TARGETDIR]py.ico', "REGISTRY.def"),
1046 ("pyw.icon", -1, pat2 % (testprefix, "NoCon"), "",
1047 r'[TARGETDIR]py.ico', "REGISTRY.def"),
1048 ("pyc.icon", -1, pat2 % (testprefix, "Compiled"), "",
1049 r'[TARGETDIR]pyc.ico', "REGISTRY.def"),
1050 # Descriptions
1051 ("py.txt", -1, pat3 % (testprefix, ""), "",
1052 "Python File", "REGISTRY.def"),
1053 ("pyw.txt", -1, pat3 % (testprefix, "NoCon"), "",
1054 "Python File (no console)", "REGISTRY.def"),
1055 ("pyc.txt", -1, pat3 % (testprefix, "Compiled"), "",
1056 "Compiled Python File", "REGISTRY.def"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001057 ])
Tim Peters66cb0182004-08-26 05:23:19 +00001058
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001059 # Registry keys
1060 prefix = r"Software\%sPython\PythonCore\%s" % (testprefix, short_version)
1061 add_data(db, "Registry",
1062 [("InstallPath", -1, prefix+r"\InstallPath", "", "[TARGETDIR]", "REGISTRY"),
1063 ("InstallGroup", -1, prefix+r"\InstallPath\InstallGroup", "",
1064 "Python %s" % short_version, "REGISTRY"),
1065 ("PythonPath", -1, prefix+r"\PythonPath", "",
Martin v. Löwisf13337d2004-09-19 18:36:45 +00001066 r"[TARGETDIR]Lib;[TARGETDIR]DLLs;[TARGETDIR]Lib\lib-tk", "REGISTRY"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001067 ("Documentation", -1, prefix+r"\Help\Main Python Documentation", "",
1068 r"[TARGETDIR]Doc\Python%s%s.chm" % (major, minor), "REGISTRY"),
1069 ("Modules", -1, prefix+r"\Modules", "+", None, "REGISTRY"),
1070 ("AppPaths", -1, r"Software\Microsoft\Windows\CurrentVersion\App Paths\Python.exe",
1071 "", r"[TARGETDIR]Python.exe", "REGISTRY.def")
1072 ])
1073 # Shortcuts, see "Shortcut Table"
1074 add_data(db, "Directory",
1075 [("ProgramMenuFolder", "TARGETDIR", "."),
1076 ("MenuDir", "ProgramMenuFolder", "PY%s%s|%sPython %s.%s" % (major,minor,testprefix,major,minor))])
1077 add_data(db, "RemoveFile",
1078 [("MenuDir", "TARGETDIR", None, "MenuDir", 2)])
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001079 tcltkshortcuts = []
1080 if have_tcl:
1081 tcltkshortcuts = [
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001082 ("IDLE", "MenuDir", "IDLE|IDLE (Python GUI)", "pythonw.exe",
Martin v. Löwisac191da2004-12-22 12:55:44 +00001083 tcltk.id, r'"[TARGETDIR]Lib\idlelib\idle.pyw"', None, None, "python_icon.exe", 0, None, "TARGETDIR"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001084 ("PyDoc", "MenuDir", "MODDOCS|Module Docs", "pythonw.exe",
Martin v. Löwisac191da2004-12-22 12:55:44 +00001085 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 +00001086 ]
1087 add_data(db, "Shortcut",
1088 tcltkshortcuts +
1089 [# Advertised shortcuts: targets are features, not files
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001090 ("Python", "MenuDir", "PYTHON|Python (command line)", "python.exe",
1091 default_feature.id, None, None, None, "python_icon.exe", 2, None, "TARGETDIR"),
1092 ("Manual", "MenuDir", "MANUAL|Python Manuals", "documentation",
1093 htmlfiles.id, None, None, None, None, None, None, None),
1094 ## Non-advertised shortcuts: must be associated with a registry component
1095 ("Uninstall", "MenuDir", "UNINST|Uninstall Python", "REGISTRY",
1096 SystemFolderName+"msiexec", "/x%s" % product_code,
1097 None, None, None, None, None, None),
1098 ])
1099 db.Commit()
1100
1101db = build_database()
1102try:
1103 add_features(db)
1104 add_ui(db)
1105 add_files(db)
1106 add_registry(db)
1107 remove_old_versions(db)
1108 db.Commit()
1109finally:
1110 del db