blob: 8d21f6c123a6675bf536b2ca3d56e90001c62a5f [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öwis8ffe9ab2004-08-22 13:34:34 +0000298 open("inst.vbs","w").write("""
299 Function CheckDir()
300 Set FSO = CreateObject("Scripting.FileSystemObject")
301 if FSO.FolderExists(Session.Property("TARGETDIR")) then
302 Session.Property("TargetExists") = "1"
303 else
304 Session.Property("TargetExists") = "0"
305 end if
306 End Function
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000307 Function UpdateEditIDLE()
308 Dim ext_new, tcl_new, regtcl_old
309 ext_new = Session.FeatureRequestState("Extensions")
310 tcl_new = Session.FeatureRequestState("TclTk")
311 if ext_new=-1 then
312 ext_new = Session.FeatureCurrentState("Extensions")
313 end if
314 if tcl_new=-1 then
315 tcl_new = Session.FeatureCurrentState("TclTk")
316 end if
317 regtcl_old = Session.ComponentCurrentState("REGISTRY.tcl")
318 if ext_new=3 and (tcl_new=3 or tcl_new=4) and regtcl_old<>3 then
319 Session.ComponentRequestState("REGISTRY.tcl")=3
320 end if
321 if (ext_new=2 or tcl_new=2) and regtcl_old<>2 then
322 Session.ComponentRequestState("REGISTRY.tcl")=2
Tim Peters0e9980f2004-09-12 03:49:31 +0000323 end if
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000324 End Function
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000325 """)
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000326 # To add debug messages into scripts, the following fragment can be used
327 # set objRec = Session.Installer.CreateRecord(1)
328 # objRec.StringData(1) = "Debug message"
329 # Session.message &H04000000, objRec
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000330 add_data(db, "Binary", [("Script", msilib.Binary("inst.vbs"))])
331 # See "Custom Action Type 6"
Tim Peters0e9980f2004-09-12 03:49:31 +0000332 add_data(db, "CustomAction",
Martin v. Löwiseac02e62004-11-18 08:00:33 +0000333 [("CheckDir", 6, "Script", "CheckDir")])
334 if have_tcl:
335 add_data(db, "CustomAction",
336 [("UpdateEditIDLE", 6, "Script", "UpdateEditIDLE")])
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000337 os.unlink("inst.vbs")
338
339
340 # UI customization properties
341 add_data(db, "Property",
342 # See "DefaultUIFont Property"
343 [("DefaultUIFont", "DlgFont8"),
344 # See "ErrorDialog Style Bit"
345 ("ErrorDialog", "ErrorDlg"),
346 ("Progress1", "Install"), # modified in maintenance type dlg
347 ("Progress2", "installs"),
348 ("MaintenanceForm_Action", "Repair")])
349
350 # Fonts, see "TextStyle Table"
351 add_data(db, "TextStyle",
352 [("DlgFont8", "Tahoma", 9, None, 0),
353 ("DlgFontBold8", "Tahoma", 8, None, 1), #bold
354 ("VerdanaBold10", "Verdana", 10, None, 1),
355 ])
356
Martin v. Löwis7b2563b2004-11-02 22:59:56 +0000357 compileargs = r"-Wi [TARGETDIR]Lib\compileall.py -f -x badsyntax [TARGETDIR]Lib"
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000358 # See "CustomAction Table"
359 add_data(db, "CustomAction", [
360 # msidbCustomActionTypeFirstSequence + msidbCustomActionTypeTextData + msidbCustomActionTypeProperty
361 # See "Custom Action Type 51",
362 # "Custom Action Execution Scheduling Options"
363 ("InitialTargetDir", 307, "TARGETDIR",
364 "[WindowsVolume]Python%s%s" % (major, minor)),
365 ("SetDLLDirToTarget", 307, "DLLDIR", "[TARGETDIR]"),
366 ("SetDLLDirToSystem32", 307, "DLLDIR", SystemFolderName),
367 # msidbCustomActionTypeExe + msidbCustomActionTypeSourceFile
368 # See "Custom Action Type 18"
Martin v. Löwis7b2563b2004-11-02 22:59:56 +0000369 ("CompilePyc", 18, "python.exe", compileargs),
370 ("CompilePyo", 18, "python.exe", "-O "+compileargs),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000371 ])
372
373 # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table"
374 # Numbers indicate sequence; see sequence.py for how these action integrate
375 add_data(db, "InstallUISequence",
376 [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140),
377 ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141),
378 ("InitialTargetDir", 'TARGETDIR=""', 750),
379 # In the user interface, assume all-users installation if privileged.
380 ("SetDLLDirToSystem32", 'DLLDIR="" and ' + sys32cond, 751),
381 ("SetDLLDirToTarget", 'DLLDIR="" and not ' + sys32cond, 752),
382 ("SelectDirectoryDlg", "Not Installed", 1230),
383 # XXX no support for resume installations yet
384 #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240),
385 ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250),
386 ("ProgressDlg", None, 1280)])
387 add_data(db, "AdminUISequence",
388 [("InitialTargetDir", 'TARGETDIR=""', 750),
389 ("SetDLLDirToTarget", 'DLLDIR=""', 751),
390 ])
391
392 # Execute Sequences
393 add_data(db, "InstallExecuteSequence",
394 [("InitialTargetDir", 'TARGETDIR=""', 750),
395 ("SetDLLDirToSystem32", 'DLLDIR="" and ' + sys32cond, 751),
396 ("SetDLLDirToTarget", 'DLLDIR="" and not ' + sys32cond, 752),
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000397 ("UpdateEditIDLE", None, 1050),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000398 ("CompilePyc", "COMPILEALL", 6800),
399 ("CompilePyo", "COMPILEALL", 6801),
400 ])
401 add_data(db, "AdminExecuteSequence",
402 [("InitialTargetDir", 'TARGETDIR=""', 750),
403 ("SetDLLDirToTarget", 'DLLDIR=""', 751),
404 ("CompilePyc", "COMPILEALL", 6800),
405 ("CompilePyo", "COMPILEALL", 6801),
406 ])
407
408 #####################################################################
409 # Standard dialogs: FatalError, UserExit, ExitDialog
410 fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title,
411 "Finish", "Finish", "Finish")
412 fatal.title("[ProductName] Installer ended prematurely")
413 fatal.back("< Back", "Finish", active = 0)
414 fatal.cancel("Cancel", "Back", active = 0)
415 fatal.text("Description1", 135, 70, 220, 80, 0x30003,
416 "[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.")
417 fatal.text("Description2", 135, 155, 220, 20, 0x30003,
418 "Click the Finish button to exit the Installer.")
419 c=fatal.next("Finish", "Cancel", name="Finish")
420 # See "ControlEvent Table". Parameters are the event, the parameter
421 # to the action, and optionally the condition for the event, and the order
422 # of events.
423 c.event("EndDialog", "Exit")
Tim Peters66cb0182004-08-26 05:23:19 +0000424
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000425 user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title,
426 "Finish", "Finish", "Finish")
427 user_exit.title("[ProductName] Installer was interrupted")
428 user_exit.back("< Back", "Finish", active = 0)
429 user_exit.cancel("Cancel", "Back", active = 0)
430 user_exit.text("Description1", 135, 70, 220, 80, 0x30003,
431 "[ProductName] setup was interrupted. Your system has not been modified. "
432 "To install this program at a later time, please run the installation again.")
433 user_exit.text("Description2", 135, 155, 220, 20, 0x30003,
434 "Click the Finish button to exit the Installer.")
435 c = user_exit.next("Finish", "Cancel", name="Finish")
436 c.event("EndDialog", "Exit")
Tim Peters66cb0182004-08-26 05:23:19 +0000437
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000438 exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title,
439 "Finish", "Finish", "Finish")
440 exit_dialog.title("Completing the [ProductName] Installer")
441 exit_dialog.back("< Back", "Finish", active = 0)
442 exit_dialog.cancel("Cancel", "Back", active = 0)
Martin v. Löwis2dd2a282004-08-22 17:10:12 +0000443 exit_dialog.text("Acknowledgements", 135, 95, 220, 120, 0x30003,
444 "Special Windows thanks to:\n"
Martin v. Löwisd3f61a22004-08-30 09:22:30 +0000445 " LettError, Erik van Blokland, for the \n"
446 " Python for Windows graphic.\n"
Martin v. Löwis2dd2a282004-08-22 17:10:12 +0000447 " http://www.letterror.com/\n"
448 "\n"
Martin v. Löwisd3f61a22004-08-30 09:22:30 +0000449 " Mark Hammond, without whose years of freely \n"
450 " shared Windows expertise, Python for Windows \n"
451 " would still be Python for DOS.")
Tim Peters66cb0182004-08-26 05:23:19 +0000452
Martin v. Löwis2dd2a282004-08-22 17:10:12 +0000453 exit_dialog.text("Description", 135, 235, 220, 20, 0x30003,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000454 "Click the Finish button to exit the Installer.")
455 c = exit_dialog.next("Finish", "Cancel", name="Finish")
456 c.event("EndDialog", "Return")
457
458 #####################################################################
459 # Required dialog: FilesInUse, ErrorDlg
460 inuse = PyDialog(db, "FilesInUse",
461 x, y, w, h,
462 19, # KeepModeless|Modal|Visible
463 title,
464 "Retry", "Retry", "Retry", bitmap=False)
465 inuse.text("Title", 15, 6, 200, 15, 0x30003,
466 r"{\DlgFontBold8}Files in Use")
467 inuse.text("Description", 20, 23, 280, 20, 0x30003,
468 "Some files that need to be updated are currently in use.")
469 inuse.text("Text", 20, 55, 330, 50, 3,
470 "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.")
471 inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess",
472 None, None, None)
473 c=inuse.back("Exit", "Ignore", name="Exit")
474 c.event("EndDialog", "Exit")
475 c=inuse.next("Ignore", "Retry", name="Ignore")
476 c.event("EndDialog", "Ignore")
477 c=inuse.cancel("Retry", "Exit", name="Retry")
478 c.event("EndDialog","Retry")
479
480
481 # See "Error Dialog". See "ICE20" for the required names of the controls.
482 error = Dialog(db, "ErrorDlg",
483 50, 10, 330, 101,
484 65543, # Error|Minimize|Modal|Visible
485 title,
486 "ErrorText", None, None)
487 error.text("ErrorText", 50,9,280,48,3, "")
488 error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None)
489 error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo")
490 error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes")
491 error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort")
492 error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel")
493 error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore")
494 error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk")
495 error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry")
496
497 #####################################################################
498 # Global "Query Cancel" dialog
499 cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title,
500 "No", "No", "No")
Tim Peters66cb0182004-08-26 05:23:19 +0000501 cancel.text("Text", 48, 15, 194, 30, 3,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000502 "Are you sure you want to cancel [ProductName] installation?")
503 cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
504 "py.ico", None, None)
505 c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No")
506 c.event("EndDialog", "Exit")
Tim Peters66cb0182004-08-26 05:23:19 +0000507
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000508 c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes")
509 c.event("EndDialog", "Return")
510
511 #####################################################################
512 # Global "Wait for costing" dialog
513 costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title,
514 "Return", "Return", "Return")
515 costing.text("Text", 48, 15, 194, 30, 3,
516 "Please wait while the installer finishes determining your disk space requirements.")
517 costing.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
518 "py.ico", None, None)
519 c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None)
520 c.event("EndDialog", "Exit")
521
522 #####################################################################
523 # Preparation dialog: no user input except cancellation
524 prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title,
525 "Cancel", "Cancel", "Cancel")
526 prep.text("Description", 135, 70, 220, 40, 0x30003,
527 "Please wait while the Installer prepares to guide you through the installation.")
528 prep.title("Welcome to the [ProductName] Installer")
529 c=prep.text("ActionText", 135, 110, 220, 20, 0x30003, "Pondering...")
530 c.mapping("ActionText", "Text")
531 c=prep.text("ActionData", 135, 135, 220, 30, 0x30003, None)
532 c.mapping("ActionData", "Text")
533 prep.back("Back", None, active=0)
534 prep.next("Next", None, active=0)
535 c=prep.cancel("Cancel", None)
536 c.event("SpawnDialog", "CancelDlg")
537
538 #####################################################################
539 # Target directory selection
540 seldlg = PyDialog(db, "SelectDirectoryDlg", x, y, w, h, modal, title,
541 "Next", "Next", "Cancel")
542 seldlg.title("Select Destination Directory")
543 seldlg.text("Description", 135, 50, 220, 40, 0x30003,
544 "Please select a directory for the [ProductName] files.")
545
546 seldlg.back("< Back", None, active=0)
547 c = seldlg.next("Next >", "Cancel")
548 c.event("DoAction", "CheckDir", "TargetExistsOk<>1", order=1)
549 # If the target exists, but we found that we are going to remove old versions, don't bother
550 # confirming that the target directory exists. Strictly speaking, we should determine that
551 # the target directory is indeed the target of the product that we are going to remove, but
552 # I don't know how to do that.
553 c.event("SpawnDialog", "ExistingDirectoryDlg", 'TargetExists=1 and REMOVEOLDVERSION="" and REMOVEOLDSNAPSHOT=""', 2)
554 c.event("SetTargetPath", "TARGETDIR", 'TargetExists=0 or REMOVEOLDVERSION<>"" or REMOVEOLDSNAPSHOT<>""', 3)
555 c.event("SpawnWaitDialog", "WaitForCostingDlg", "CostingComplete=1", 4)
556 c.event("NewDialog", "SelectFeaturesDlg", 'TargetExists=0 or REMOVEOLDVERSION<>"" or REMOVEOLDSNAPSHOT<>""', 5)
557
558 c = seldlg.cancel("Cancel", "DirectoryCombo")
559 c.event("SpawnDialog", "CancelDlg")
560
561 seldlg.control("DirectoryCombo", "DirectoryCombo", 135, 70, 172, 80, 393219,
562 "TARGETDIR", None, "DirectoryList", None)
563 seldlg.control("DirectoryList", "DirectoryList", 135, 90, 208, 136, 3, "TARGETDIR",
564 None, "PathEdit", None)
565 seldlg.control("PathEdit", "PathEdit", 135, 230, 206, 16, 3, "TARGETDIR", None, "Next", None)
566 c = seldlg.pushbutton("Up", 306, 70, 18, 18, 3, "Up", None)
567 c.event("DirectoryListUp", "0")
568 c = seldlg.pushbutton("NewDir", 324, 70, 30, 18, 3, "New", None)
569 c.event("DirectoryListNew", "0")
570
571 #####################################################################
572 # SelectFeaturesDlg
573 features = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal|track_disk_space,
574 title, "Tree", "Next", "Cancel")
575 features.title("Customize [ProductName]")
576 features.text("Description", 135, 35, 220, 15, 0x30003,
577 "Select the way you want features to be installed.")
578 features.text("Text", 135,45,220,30, 3,
579 "Click on the icons in the tree below to change the way features will be installed.")
580
581 c=features.back("< Back", "Next")
582 c.event("NewDialog", "SelectDirectoryDlg")
583
584 c=features.next("Next >", "Cancel")
585 c.mapping("SelectionNoItems", "Enabled")
586 c.event("SpawnDialog", "DiskCostDlg", "OutOfDiskSpace=1", order=1)
587 c.event("EndDialog", "Return", "OutOfDiskSpace<>1", order=2)
588
589 c=features.cancel("Cancel", "Tree")
590 c.event("SpawnDialog", "CancelDlg")
591
Tim Peters66cb0182004-08-26 05:23:19 +0000592 # 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 +0000593 features.control("Tree", "SelectionTree", 135, 75, 220, 95, 7, "_BrowseProperty",
594 "Tree of selections", "Back", None)
595
596 #c=features.pushbutton("Reset", 42, 243, 56, 17, 3, "Reset", "DiskCost")
597 #c.mapping("SelectionNoItems", "Enabled")
598 #c.event("Reset", "0")
Tim Peters66cb0182004-08-26 05:23:19 +0000599
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000600 features.control("Box", "GroupBox", 135, 170, 225, 90, 1, None, None, None, None)
601
602 c=features.xbutton("DiskCost", "Disk &Usage", None, 0.10)
603 c.mapping("SelectionNoItems","Enabled")
604 c.event("SpawnDialog", "DiskCostDlg")
605
606 c=features.xbutton("Advanced", "Advanced", None, 0.30)
607 c.event("SpawnDialog", "AdvancedDlg")
608
609 c=features.text("ItemDescription", 140, 180, 210, 30, 3,
610 "Multiline description of the currently selected item.")
611 c.mapping("SelectionDescription","Text")
Tim Peters66cb0182004-08-26 05:23:19 +0000612
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000613 c=features.text("ItemSize", 140, 210, 210, 45, 3,
614 "The size of the currently selected item.")
615 c.mapping("SelectionSize", "Text")
616
617 #####################################################################
618 # Disk cost
619 cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title,
620 "OK", "OK", "OK", bitmap=False)
621 cost.text("Title", 15, 6, 200, 15, 0x30003,
622 "{\DlgFontBold8}Disk Space Requirements")
623 cost.text("Description", 20, 20, 280, 20, 0x30003,
624 "The disk space required for the installation of the selected features.")
625 cost.text("Text", 20, 53, 330, 60, 3,
626 "The highlighted volumes (if any) do not have enough disk space "
627 "available for the currently selected features. You can either "
628 "remove some files from the highlighted volumes, or choose to "
629 "install less features onto local drive(s), or select different "
630 "destination drive(s).")
631 cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223,
632 None, "{120}{70}{70}{70}{70}", None, None)
633 cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return")
634
635 #####################################################################
636 # WhichUsers Dialog. Only available on NT, and for privileged users.
637 # This must be run before FindRelatedProducts, because that will
638 # take into account whether the previous installation was per-user
639 # or per-machine. We currently don't support going back to this
640 # dialog after "Next" was selected; to support this, we would need to
641 # find how to reset the ALLUSERS property, and how to re-run
642 # FindRelatedProducts.
643 # On Windows9x, the ALLUSERS property is ignored on the command line
644 # and in the Property table, but installer fails according to the documentation
645 # if a dialog attempts to set ALLUSERS.
646 whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title,
647 "AdminInstall", "Next", "Cancel")
648 whichusers.title("Select whether to install [ProductName] for all users of this computer.")
649 # A radio group with two options: allusers, justme
650 g = whichusers.radiogroup("AdminInstall", 135, 60, 160, 50, 3,
651 "WhichUsers", "", "Next")
652 g.add("ALL", 0, 5, 150, 20, "Install for all users")
653 g.add("JUSTME", 0, 25, 150, 20, "Install just for me")
654
Tim Peters66cb0182004-08-26 05:23:19 +0000655 whichusers.back("Back", None, active=0)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000656
657 c = whichusers.next("Next >", "Cancel")
658 c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1)
659 c.event("EndDialog", "Return", order = 2)
660
661 c = whichusers.cancel("Cancel", "AdminInstall")
662 c.event("SpawnDialog", "CancelDlg")
Tim Peters66cb0182004-08-26 05:23:19 +0000663
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000664 #####################################################################
665 # Advanced Dialog.
666 advanced = PyDialog(db, "AdvancedDlg", x, y, w, h, modal, title,
667 "CompilePyc", "Next", "Cancel")
668 advanced.title("Advanced Options for [ProductName]")
669 # A radio group with two options: allusers, justme
670 advanced.checkbox("CompilePyc", 135, 60, 230, 50, 3,
671 "COMPILEALL", "Compile .py files to byte code after installation", "Next")
672
673 c = advanced.next("Finish", "Cancel")
674 c.event("EndDialog", "Return")
675
676 c = advanced.cancel("Cancel", "CompilePyc")
677 c.event("SpawnDialog", "CancelDlg")
Tim Peters66cb0182004-08-26 05:23:19 +0000678
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000679 #####################################################################
Tim Peters66cb0182004-08-26 05:23:19 +0000680 # Existing Directory dialog
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000681 dlg = Dialog(db, "ExistingDirectoryDlg", 50, 30, 200, 80, modal, title,
682 "No", "No", "No")
683 dlg.text("Title", 10, 20, 180, 40, 3,
684 "[TARGETDIR] exists. Are you sure you want to overwrite existing files?")
685 c=dlg.pushbutton("Yes", 30, 60, 55, 17, 3, "Yes", "No")
686 c.event("[TargetExists]", "0", order=1)
687 c.event("[TargetExistsOk]", "1", order=2)
688 c.event("EndDialog", "Return", order=3)
689 c=dlg.pushbutton("No", 115, 60, 55, 17, 3, "No", "Yes")
690 c.event("EndDialog", "Return")
691
692 #####################################################################
693 # Installation Progress dialog (modeless)
694 progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title,
695 "Cancel", "Cancel", "Cancel", bitmap=False)
696 progress.text("Title", 20, 15, 200, 15, 0x30003,
697 "{\DlgFontBold8}[Progress1] [ProductName]")
698 progress.text("Text", 35, 65, 300, 30, 3,
699 "Please wait while the Installer [Progress2] [ProductName]. "
700 "This may take several minutes.")
701 progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:")
702
703 c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...")
704 c.mapping("ActionText", "Text")
705
706 #c=progress.text("ActionData", 35, 140, 300, 20, 3, None)
707 #c.mapping("ActionData", "Text")
708
709 c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537,
710 None, "Progress done", None, None)
711 c.mapping("SetProgress", "Progress")
712
713 progress.back("< Back", "Next", active=False)
714 progress.next("Next >", "Cancel", active=False)
715 progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg")
716
717 # Maintenance type: repair/uninstall
718 maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title,
719 "Next", "Next", "Cancel")
720 maint.title("Welcome to the [ProductName] Setup Wizard")
721 maint.text("BodyText", 135, 63, 230, 42, 3,
722 "Select whether you want to repair or remove [ProductName].")
723 g=maint.radiogroup("RepairRadioGroup", 135, 108, 230, 60, 3,
724 "MaintenanceForm_Action", "", "Next")
725 g.add("Change", 0, 0, 200, 17, "&Change [ProductName]")
726 g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]")
727 g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]")
Tim Peters66cb0182004-08-26 05:23:19 +0000728
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000729 maint.back("< Back", None, active=False)
730 c=maint.next("Finish", "Cancel")
731 # Change installation: Change progress dialog to "Change", then ask
732 # for feature selection
733 c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1)
734 c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2)
735
736 # Reinstall: Change progress dialog to "Repair", then invoke reinstall
737 # Also set list of reinstalled features to "ALL"
738 c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5)
739 c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6)
Raymond Hettinger72f08012004-11-07 07:08:25 +0000740 c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000741 c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8)
742
743 # Uninstall: Change progress to "Remove", then invoke uninstall
744 # Also set list of removed features to "ALL"
745 c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11)
746 c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12)
747 c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13)
748 c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14)
749
Tim Peters66cb0182004-08-26 05:23:19 +0000750 # Close dialog when maintenance action scheduled
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000751 c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20)
752 c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21)
Tim Peters66cb0182004-08-26 05:23:19 +0000753
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000754 maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg")
Tim Peters66cb0182004-08-26 05:23:19 +0000755
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000756
757# See "Feature Table". The feature level is 1 for all features,
758# and the feature attributes are 0 for the DefaultFeature, and
759# FollowParent for all other features. The numbers are the Display
760# column.
761def add_features(db):
762 # feature attributes:
763 # msidbFeatureAttributesFollowParent == 2
764 # msidbFeatureAttributesDisallowAdvertise == 8
765 # Features that need to be installed with together with the main feature
766 # (i.e. additional Python libraries) need to follow the parent feature.
767 # Features that have no advertisement trigger (e.g. the test suite)
768 # must not support advertisement
769 global default_feature, tcltk, htmlfiles, tools, testsuite, ext_feature
770 default_feature = Feature(db, "DefaultFeature", "Python",
771 "Python Interpreter and Libraries",
772 1, directory = "TARGETDIR")
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000773 # We don't support advertisement of extensions
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000774 ext_feature = Feature(db, "Extensions", "Register Extensions",
775 "Make this Python installation the default Python installation", 3,
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000776 parent = default_feature, attributes=2|8)
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000777 if have_tcl:
778 tcltk = Feature(db, "TclTk", "Tcl/Tk", "Tkinter, IDLE, pydoc", 5,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000779 parent = default_feature, attributes=2)
780 htmlfiles = Feature(db, "Documentation", "Documentation",
781 "Python HTMLHelp File", 7, parent = default_feature)
782 tools = Feature(db, "Tools", "Utility Scripts",
Tim Peters66cb0182004-08-26 05:23:19 +0000783 "Python utility scripts (Tools/", 9,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000784 parent = default_feature, attributes=2)
785 testsuite = Feature(db, "Testsuite", "Test suite",
786 "Python test suite (Lib/test/)", 11,
787 parent = default_feature, attributes=2|8)
Tim Peters66cb0182004-08-26 05:23:19 +0000788
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000789def extract_msvcr71():
790 import _winreg
791 # Find the location of the merge modules
792 k = _winreg.OpenKey(
793 _winreg.HKEY_LOCAL_MACHINE,
794 r"Software\Microsoft\VisualStudio\7.1\Setup\VS")
795 dir = _winreg.QueryValueEx(k, "MSMDir")[0]
796 _winreg.CloseKey(k)
797 files = glob.glob1(dir, "*CRT71*")
798 assert len(files) == 1
799 file = os.path.join(dir, files[0])
800 # Extract msvcr71.dll
801 m = msilib.MakeMerge2()
802 m.OpenModule(file, 0)
803 m.ExtractFiles(".")
804 m.CloseModule()
805 # Find the version/language of msvcr71.dll
806 installer = msilib.MakeInstaller()
807 return installer.FileVersion("msvcr71.dll", 0), \
808 installer.FileVersion("msvcr71.dll", 1)
809
810class PyDirectory(Directory):
811 """By default, all components in the Python installer
812 can run from source."""
813 def __init__(self, *args, **kw):
814 if not kw.has_key("componentflags"):
815 kw['componentflags'] = 2 #msidbComponentAttributesOptional
816 Directory.__init__(self, *args, **kw)
817
818# See "File Table", "Component Table", "Directory Table",
819# "FeatureComponents Table"
820def add_files(db):
821 cab = CAB("python")
822 tmpfiles = []
823 # Add all executables, icons, text files into the TARGETDIR component
824 root = PyDirectory(db, cab, None, srcdir, "TARGETDIR", "SourceDir")
825 default_feature.set_current()
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000826 if not msilib.Win64:
827 root.add_file("PCBuild/w9xpopen.exe")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000828 root.add_file("PC/py.ico")
829 root.add_file("PC/pyc.ico")
830 root.add_file("README.txt", src="README")
831 root.add_file("NEWS.txt", src="Misc/NEWS")
832 root.add_file("LICENSE.txt", src="LICENSE")
833 root.start_component("python.exe", keyfile="python.exe")
834 root.add_file("PCBuild/python.exe")
835 root.start_component("pythonw.exe", keyfile="pythonw.exe")
836 root.add_file("PCBuild/pythonw.exe")
837
838 # msidbComponentAttributesSharedDllRefCount = 8, see "Component Table"
839 dlldir = PyDirectory(db, cab, root, srcdir, "DLLDIR", ".")
840 pydll = "python%s%s.dll" % (major, minor)
841 pydllsrc = srcdir + "/PCBuild/" + pydll
842 dlldir.start_component("DLLDIR", flags = 8, keyfile = pydll)
843 installer = msilib.MakeInstaller()
844 pyversion = installer.FileVersion(pydllsrc, 0)
845 if not snapshot:
846 # For releases, the Python DLL has the same version as the
847 # installer package.
848 assert pyversion.split(".")[:3] == current_version.split(".")
849 dlldir.add_file("PCBuild/python%s%s.dll" % (major, minor),
850 version=pyversion,
851 language=installer.FileVersion(pydllsrc, 1))
852 # XXX determine dependencies
853 version, lang = extract_msvcr71()
854 dlldir.start_component("msvcr71", flags=8, keyfile="msvcr71.dll")
855 dlldir.add_file("msvcr71.dll", src=os.path.abspath("msvcr71.dll"),
856 version=version, language=lang)
857 tmpfiles.append("msvcr71.dll")
Tim Peters66cb0182004-08-26 05:23:19 +0000858
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000859 # Add all .py files in Lib, except lib-tk, test
860 dirs={}
861 pydirs = [(root,"Lib")]
862 while pydirs:
863 parent, dir = pydirs.pop()
864 if dir == "CVS" or dir.startswith("plat-"):
865 continue
866 elif dir in ["lib-tk", "idlelib", "Icons"]:
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000867 if not have_tcl:
868 continue
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000869 tcltk.set_current()
870 elif dir in ['test', 'output']:
871 testsuite.set_current()
872 else:
873 default_feature.set_current()
874 lib = PyDirectory(db, cab, parent, dir, dir, "%s|%s" % (parent.make_short(dir), dir))
875 # Add additional files
876 dirs[dir]=lib
877 lib.glob("*.txt")
878 if dir=='site-packages':
Martin v. Löwis6d60c092004-11-21 10:16:26 +0000879 lib.add_file("README.txt", src="README")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000880 continue
881 files = lib.glob("*.py")
882 files += lib.glob("*.pyw")
883 if files:
884 # Add an entry to the RemoveFile table to remove bytecode files.
885 lib.remove_pyc()
886 if dir=='test' and parent.physical=='Lib':
887 lib.add_file("185test.db")
888 lib.add_file("audiotest.au")
889 lib.add_file("cfgparser.1")
890 lib.add_file("test.xml")
891 lib.add_file("test.xml.out")
892 lib.add_file("testtar.tar")
Martin v. Löwis7d3755d2004-09-06 06:31:12 +0000893 lib.add_file("test_difflib_expect.html")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000894 lib.glob("*.uue")
895 lib.add_file("readme.txt", src="README")
896 if dir=='decimaltestdata':
897 lib.glob("*.decTest")
898 if dir=='output':
899 lib.glob("test_*")
900 if dir=='idlelib':
901 lib.glob("*.def")
902 lib.add_file("idle.bat")
903 if dir=="Icons":
904 lib.glob("*.gif")
905 lib.add_file("idle.icns")
906 if dir=="command":
907 lib.add_file("wininst-6.exe")
908 lib.add_file("wininst-7.1.exe")
909 if dir=="data" and parent.physical=="test" and parent.basedir.physical=="email":
910 # This should contain all non-CVS files listed in CVS
911 for f in os.listdir(lib.absolute):
912 if f.endswith(".txt") or f=="CVS":continue
913 if f.endswith(".au") or f.endswith(".gif"):
914 lib.add_file(f)
915 else:
916 print "WARNING: New file %s in email/test/data" % f
917 for f in os.listdir(lib.absolute):
918 if os.path.isdir(os.path.join(lib.absolute, f)):
919 pydirs.append((lib, f))
920 # Add DLLs
921 default_feature.set_current()
922 lib = PyDirectory(db, cab, root, srcdir+"/PCBuild", "DLLs", "DLLS|DLLs")
923 dlls = []
924 tclfiles = []
925 for f in extensions:
926 if f=="_tkinter.pyd":
927 continue
928 if not os.path.exists(srcdir+"/PCBuild/"+f):
929 print "WARNING: Missing extension", f
930 continue
931 dlls.append(f)
932 lib.add_file(f)
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000933 if have_tcl:
934 if not os.path.exists(srcdir+"/PCBuild/_tkinter.pyd"):
935 print "WARNING: Missing _tkinter.pyd"
936 else:
937 lib.start_component("TkDLLs", tcltk)
938 lib.add_file("_tkinter.pyd")
939 dlls.append("_tkinter.pyd")
940 tcldir = os.path.normpath(srcdir+"/../tcltk/bin")
941 for f in glob.glob1(tcldir, "*.dll"):
942 lib.add_file(f, src=os.path.join(tcldir, f))
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000943 # check whether there are any unknown extensions
944 for f in glob.glob1(srcdir+"/PCBuild", "*.pyd"):
945 if f.endswith("_d.pyd"): continue # debug version
946 if f in dlls: continue
947 print "WARNING: Unknown extension", f
Tim Peters66cb0182004-08-26 05:23:19 +0000948
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000949 # Add headers
950 default_feature.set_current()
951 lib = PyDirectory(db, cab, root, "include", "include", "INCLUDE|include")
952 lib.glob("*.h")
953 lib.add_file("pyconfig.h", src="../PC/pyconfig.h")
954 # Add import libraries
955 lib = PyDirectory(db, cab, root, "PCBuild", "libs", "LIBS|libs")
956 for f in dlls:
957 lib.add_file(f.replace('pyd','lib'))
958 lib.add_file('python%s%s.lib' % (major, minor))
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000959 if have_tcl:
960 # Add Tcl/Tk
961 tcldirs = [(root, '../tcltk/lib', 'tcl')]
962 tcltk.set_current()
963 while tcldirs:
964 parent, phys, dir = tcldirs.pop()
965 lib = PyDirectory(db, cab, parent, phys, dir, "%s|%s" % (parent.make_short(dir), dir))
966 if not os.path.exists(lib.absolute):
967 continue
968 for f in os.listdir(lib.absolute):
969 if os.path.isdir(os.path.join(lib.absolute, f)):
970 tcldirs.append((lib, f, f))
971 else:
972 lib.add_file(f)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000973 # Add tools
974 tools.set_current()
975 tooldir = PyDirectory(db, cab, root, "Tools", "Tools", "TOOLS|Tools")
976 for f in ['i18n', 'pynche', 'Scripts', 'versioncheck', 'webchecker']:
977 lib = PyDirectory(db, cab, tooldir, f, f, "%s|%s" % (tooldir.make_short(f), f))
978 lib.glob("*.py")
979 lib.glob("*.pyw", exclude=['pydocgui.pyw'])
980 lib.remove_pyc()
981 lib.glob("*.txt")
982 if f == "pynche":
983 x = PyDirectory(db, cab, lib, "X", "X", "X|X")
984 x.glob("*.txt")
985 if f == 'Scripts':
986 lib.add_file("README.txt", src="README")
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000987 if have_tcl:
988 lib.start_component("pydocgui.pyw", tcltk, keyfile="pydocgui.pyw")
989 lib.add_file("pydocgui.pyw")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000990 # Add documentation
991 htmlfiles.set_current()
992 lib = PyDirectory(db, cab, root, "Doc", "Doc", "DOC|Doc")
993 lib.start_component("documentation", keyfile="Python%s%s.chm" % (major,minor))
994 lib.add_file("Python%s%s.chm" % (major, minor))
995
996 cab.commit(db)
997
998 for f in tmpfiles:
999 os.unlink(f)
1000
1001# See "Registry Table", "Component Table"
1002def add_registry(db):
1003 # File extensions, associated with the REGISTRY.def component
1004 # IDLE verbs depend on the tcltk feature.
1005 # msidbComponentAttributesRegistryKeyPath = 4
1006 # -1 for Root specifies "dependent on ALLUSERS property"
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001007 tcldata = []
1008 if have_tcl:
1009 tcldata = [
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001010 ("REGISTRY.tcl", msilib.gen_uuid(), "TARGETDIR", 4, None,
1011 "py.IDLE")]
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001012 add_data(db, "Component",
1013 # msidbComponentAttributesRegistryKeyPath = 4
1014 [("REGISTRY", msilib.gen_uuid(), "TARGETDIR", 4, None,
1015 "InstallPath"),
1016 ("REGISTRY.def", msilib.gen_uuid(), "TARGETDIR", 4,
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001017 None, None)] + tcldata)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001018 # See "FeatureComponents Table".
1019 # The association between TclTk and pythonw.exe is necessary to make ICE59
1020 # happy, because the installer otherwise believes that the IDLE and PyDoc
1021 # shortcuts might get installed without pythonw.exe being install. This
1022 # is not true, since installing TclTk will install the default feature, which
1023 # will cause pythonw.exe to be installed.
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001024 # REGISTRY.tcl is not associated with any feature, as it will be requested
1025 # through a custom action
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001026 tcldata = []
1027 if have_tcl:
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001028 tcldata = [(tcltk.id, "pythonw.exe")]
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001029 add_data(db, "FeatureComponents",
1030 [(default_feature.id, "REGISTRY"),
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001031 (ext_feature.id, "REGISTRY.def")] +
1032 tcldata
1033 )
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001034 # Extensions are not advertised. For advertised extensions,
1035 # we would need separate binaries that install along with the
1036 # extension.
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001037 pat = r"Software\Classes\%sPython.%sFile\shell\%s\command"
1038 ewi = "Edit with IDLE"
1039 pat2 = r"Software\Classes\%sPython.%sFile\DefaultIcon"
1040 pat3 = r"Software\Classes\%sPython.%sFile"
Martin v. Löwiseac02e62004-11-18 08:00:33 +00001041 tcl_verbs = []
1042 if have_tcl:
1043 tcl_verbs=[
1044 ("py.IDLE", -1, pat % (testprefix, "", ewi), "",
1045 r'"[TARGETDIR]pythonw.exe" "[TARGETDIR]Lib\idlelib\idle.pyw" -n -e "%1"',
1046 "REGISTRY.tcl"),
1047 ("pyw.IDLE", -1, pat % (testprefix, "NoCon", ewi), "",
1048 r'"[TARGETDIR]pythonw.exe" "[TARGETDIR]Lib\idlelib\idle.pyw" -n -e "%1"',
1049 "REGISTRY.tcl"),
1050 ]
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001051 add_data(db, "Registry",
1052 [# Extensions
1053 ("py.ext", -1, r"Software\Classes\."+ext, "",
1054 "Python.File", "REGISTRY.def"),
1055 ("pyw.ext", -1, r"Software\Classes\."+ext+'w', "",
1056 "Python.NoConFile", "REGISTRY.def"),
1057 ("pyc.ext", -1, r"Software\Classes\."+ext+'c', "",
1058 "Python.CompiledFile", "REGISTRY.def"),
1059 ("pyo.ext", -1, r"Software\Classes\."+ext+'o', "",
1060 "Python.CompiledFile", "REGISTRY.def"),
1061 # MIME types
1062 ("py.mime", -1, r"Software\Classes\."+ext, "Content Type",
1063 "text/plain", "REGISTRY.def"),
1064 ("pyw.mime", -1, r"Software\Classes\."+ext+'w', "Content Type",
1065 "text/plain", "REGISTRY.def"),
1066 #Verbs
1067 ("py.open", -1, pat % (testprefix, "", "open"), "",
1068 r'"[TARGETDIR]python.exe" "%1" %*', "REGISTRY.def"),
1069 ("pyw.open", -1, pat % (testprefix, "NoCon", "open"), "",
1070 r'"[TARGETDIR]pythonw.exe" "%1" %*', "REGISTRY.def"),
1071 ("pyc.open", -1, pat % (testprefix, "Compiled", "open"), "",
1072 r'"[TARGETDIR]python.exe" "%1" %*', "REGISTRY.def"),
Martin v. Löwiseac02e62004-11-18 08:00:33 +00001073 ] + tcl_verbs + [
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001074 #Icons
1075 ("py.icon", -1, pat2 % (testprefix, ""), "",
1076 r'[TARGETDIR]py.ico', "REGISTRY.def"),
1077 ("pyw.icon", -1, pat2 % (testprefix, "NoCon"), "",
1078 r'[TARGETDIR]py.ico', "REGISTRY.def"),
1079 ("pyc.icon", -1, pat2 % (testprefix, "Compiled"), "",
1080 r'[TARGETDIR]pyc.ico', "REGISTRY.def"),
1081 # Descriptions
1082 ("py.txt", -1, pat3 % (testprefix, ""), "",
1083 "Python File", "REGISTRY.def"),
1084 ("pyw.txt", -1, pat3 % (testprefix, "NoCon"), "",
1085 "Python File (no console)", "REGISTRY.def"),
1086 ("pyc.txt", -1, pat3 % (testprefix, "Compiled"), "",
1087 "Compiled Python File", "REGISTRY.def"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001088 ])
Tim Peters66cb0182004-08-26 05:23:19 +00001089
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001090 # Registry keys
1091 prefix = r"Software\%sPython\PythonCore\%s" % (testprefix, short_version)
1092 add_data(db, "Registry",
1093 [("InstallPath", -1, prefix+r"\InstallPath", "", "[TARGETDIR]", "REGISTRY"),
1094 ("InstallGroup", -1, prefix+r"\InstallPath\InstallGroup", "",
1095 "Python %s" % short_version, "REGISTRY"),
1096 ("PythonPath", -1, prefix+r"\PythonPath", "",
Martin v. Löwisf13337d2004-09-19 18:36:45 +00001097 r"[TARGETDIR]Lib;[TARGETDIR]DLLs;[TARGETDIR]Lib\lib-tk", "REGISTRY"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001098 ("Documentation", -1, prefix+r"\Help\Main Python Documentation", "",
1099 r"[TARGETDIR]Doc\Python%s%s.chm" % (major, minor), "REGISTRY"),
1100 ("Modules", -1, prefix+r"\Modules", "+", None, "REGISTRY"),
1101 ("AppPaths", -1, r"Software\Microsoft\Windows\CurrentVersion\App Paths\Python.exe",
1102 "", r"[TARGETDIR]Python.exe", "REGISTRY.def")
1103 ])
1104 # Shortcuts, see "Shortcut Table"
1105 add_data(db, "Directory",
1106 [("ProgramMenuFolder", "TARGETDIR", "."),
1107 ("MenuDir", "ProgramMenuFolder", "PY%s%s|%sPython %s.%s" % (major,minor,testprefix,major,minor))])
1108 add_data(db, "RemoveFile",
1109 [("MenuDir", "TARGETDIR", None, "MenuDir", 2)])
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001110 tcltkshortcuts = []
1111 if have_tcl:
1112 tcltkshortcuts = [
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001113 ("IDLE", "MenuDir", "IDLE|IDLE (Python GUI)", "pythonw.exe",
1114 tcltk.id, r"[TARGETDIR]Lib\idlelib\idle.pyw", None, None, "python_icon.exe", 0, None, "TARGETDIR"),
1115 ("PyDoc", "MenuDir", "MODDOCS|Module Docs", "pythonw.exe",
1116 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 +00001117 ]
1118 add_data(db, "Shortcut",
1119 tcltkshortcuts +
1120 [# Advertised shortcuts: targets are features, not files
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001121 ("Python", "MenuDir", "PYTHON|Python (command line)", "python.exe",
1122 default_feature.id, None, None, None, "python_icon.exe", 2, None, "TARGETDIR"),
1123 ("Manual", "MenuDir", "MANUAL|Python Manuals", "documentation",
1124 htmlfiles.id, None, None, None, None, None, None, None),
1125 ## Non-advertised shortcuts: must be associated with a registry component
1126 ("Uninstall", "MenuDir", "UNINST|Uninstall Python", "REGISTRY",
1127 SystemFolderName+"msiexec", "/x%s" % product_code,
1128 None, None, None, None, None, None),
1129 ])
1130 db.Commit()
1131
1132db = build_database()
1133try:
1134 add_features(db)
1135 add_ui(db)
1136 add_files(db)
1137 add_registry(db)
1138 remove_old_versions(db)
1139 db.Commit()
1140finally:
1141 del db