blob: a6d4d05b9160e328fb1c70cd9c214d19b90d125c [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.
Martin v. Löwis9fda9312004-12-22 13:41:49 +00004import msilib, schema, sequence, os, glob, time, re
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00005from msilib import Feature, CAB, Directory, Dialog, Binary, add_data
6import uisample
7from win32com.client import constants
Martin v. Löwis9fda9312004-12-22 13:41:49 +00008from distutils.spawn import find_executable
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00009
10# Settings can be overridden in config.py below
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +000011# 0 for official python.org releases
12# 1 for intermediate releases by anybody, with
13# a new product code for every package.
14snapshot = 1
15# 1 means that file extension is px, not py,
16# and binaries start with x
17testpackage = 0
18# Location of build tree
19srcdir = os.path.abspath("../..")
20# Text to be displayed as the version in dialogs etc.
21# goes into file name and ProductCode. Defaults to
22# current_version.day for Snapshot, current_version otherwise
23full_current_version = None
Martin v. Löwise0f780d2004-09-01 14:51:06 +000024# Is Tcl available at all?
25have_tcl = True
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +000026
27try:
28 from config import *
29except ImportError:
30 pass
31
32# Extract current version from Include/patchlevel.h
33lines = open(srcdir + "/Include/patchlevel.h").readlines()
34major = minor = micro = level = serial = None
35levels = {
36 'PY_RELEASE_LEVEL_ALPHA':0xA,
37 'PY_RELEASE_LEVEL_BETA': 0xB,
38 'PY_RELEASE_LEVEL_GAMMA':0xC,
39 'PY_RELEASE_LEVEL_FINAL':0xF
40 }
41for l in lines:
42 if not l.startswith("#define"):
43 continue
44 l = l.split()
45 if len(l) != 3:
46 continue
47 _, name, value = l
48 if name == 'PY_MAJOR_VERSION': major = value
49 if name == 'PY_MINOR_VERSION': minor = value
50 if name == 'PY_MICRO_VERSION': micro = value
51 if name == 'PY_RELEASE_LEVEL': level = levels[value]
52 if name == 'PY_RELEASE_SERIAL': serial = value
53
54short_version = major+"."+minor
55# See PC/make_versioninfo.c
56FIELD3 = 1000*int(micro) + 10*level + int(serial)
57current_version = "%s.%d" % (short_version, FIELD3)
58
59# This should never change. The UpgradeCode of this package can be
60# used in the Upgrade table of future packages to make the future
61# package replace this one. See "UpgradeCode Property".
62upgrade_code_snapshot='{92A24481-3ECB-40FC-8836-04B7966EC0D5}'
63upgrade_code='{65E6DE48-A358-434D-AA4F-4AF72DB4718F}'
64
65# This should be extended for each Python release.
66# The product code must change whenever the name of the MSI file
67# changes, and when new component codes are issued for existing
68# components. See "Changing the Product Code". As we change the
69# component codes with every build, we need a new product code
70# each time. For intermediate (snapshot) releases, they are automatically
71# generated. For official releases, we record the product codes,
72# so people can refer to them.
73product_codes = {
74 '2.4.101': '{0e9b4d8e-6cda-446e-a208-7b92f3ddffa0}', # 2.4a1, released as a snapshot
75 '2.4.102': '{1b998745-4901-4edb-bc52-213689e1b922}', # 2.4a2
76 '2.4.103': '{33fc8bd2-1e8f-4add-a40a-ade2728d5942}', # 2.4a3
77 '2.4.111': '{51a7e2a8-2025-4ef0-86ff-e6aab742d1fa}', # 2.4b1
78 '2.4.112': '{4a5e7c1d-c659-4fe3-b8c9-7c65bd9c95a5}', # 2.4b2
79 '2.4.121': '{75508821-a8e9-40a8-95bd-dbe6033ddbea}', # 2.4c1
80 '2.4.122': '{83a9118b-4bdd-473b-afc3-bcb142feca9e}', # 2.4c2
81 '2.4.150': '{82d9302e-f209-4805-b548-52087047483a}', # 2.4.0
Martin v. Löwis3390d332005-03-14 17:20:13 +000082 '2.4.1121':'{be027411-8e6b-4440-a29b-b07df0690230}', # 2.4.1c1
83 '2.4.1122':'{02818752-48bf-4074-a281-7a4114c4f1b1}', # 2.4.1c2
84 '2.4.1150':'{4d4f5346-7e4a-40b5-9387-fdb6181357fc}', # 2.4.1
85 '2.4.2121':'{5ef9d6b6-df78-45d2-ab09-14786a3c5a99}', # 2.4.2c1
86 '2.4.2150':'{b191e49c-ea23-43b2-b28a-14e0784069b8}', # 2.4.2
Martin v. Löwis51ef6f92006-03-23 19:21:52 +000087 '2.4.3121':'{f669ed4d-1dce-41c4-9617-d985397187a1}', # 2.4.3c1
88 '2.4.3150':'{75e71add-042c-4f30-bfac-a9ec42351313}', # 2.4.3
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +000089}
90
91if snapshot:
92 current_version = "%s.%s.%s" % (major, minor, int(time.time()/3600/24))
93 product_code = msilib.gen_uuid()
94else:
95 product_code = product_codes[current_version]
96
97if full_current_version is None:
98 full_current_version = current_version
99
100extensions = [
101 'bz2.pyd',
102 'pyexpat.pyd',
103 'select.pyd',
104 'unicodedata.pyd',
105 'winsound.pyd',
Trent Micke97e5a72005-12-15 22:08:46 +0000106 '_elementtree.pyd',
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000107 '_bsddb.pyd',
108 '_socket.pyd',
109 '_ssl.pyd',
110 '_testcapi.pyd',
111 '_tkinter.pyd',
Martin v. Löwis8c7c56e2006-03-05 14:04:26 +0000112 '_msi.pyd',
Martin v. Löwisa09655e2006-03-10 15:36:28 +0000113 '_ctypes.pyd',
114 '_ctypes_test.pyd'
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000115]
116
Martin v. Löwis4e6aff52006-01-03 07:10:14 +0000117if major+minor <= "24":
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000118 extensions.extend([
Martin v. Löwis4e6aff52006-01-03 07:10:14 +0000119 'zlib.pyd',
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000120 ])
121
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000122# Well-known component UUIDs
123# These are needed for SharedDLLs reference counter; if
124# a different UUID was used for each incarnation of, say,
125# python24.dll, an upgrade would set the reference counter
126# from 1 to 2 (due to what I consider a bug in MSI)
127# Using the same UUID is fine since these files are versioned,
128# so Installer will always keep the newest version.
129msvcr71_uuid = "{8666C8DD-D0B4-4B42-928E-A69E32FA5D4D}"
130pythondll_uuid = {
131 "24":"{9B81E618-2301-4035-AC77-75D9ABEB7301}",
132 "25":"{2e41b118-38bd-4c1b-a840-6977efd1b911}"
133 } [major+minor]
Tim Peterseba28be2005-03-28 01:08:02 +0000134
Martin v. Löwis9fda9312004-12-22 13:41:49 +0000135# Build the mingw import library, libpythonXY.a
136# This requires 'nm' and 'dlltool' executables on your PATH
137def build_mingw_lib(lib_file, def_file, dll_file, mingw_lib):
138 warning = "WARNING: %s - libpythonXX.a not built"
139 nm = find_executable('nm')
140 dlltool = find_executable('dlltool')
141
142 if not nm or not dlltool:
143 print warning % "nm and/or dlltool were not found"
144 return False
145
146 nm_command = '%s -Cs %s' % (nm, lib_file)
147 dlltool_command = "%s --dllname %s --def %s --output-lib %s" % \
148 (dlltool, dll_file, def_file, mingw_lib)
149 export_match = re.compile(r"^_imp__(.*) in python\d+\.dll").match
150
151 f = open(def_file,'w')
152 print >>f, "LIBRARY %s" % dll_file
153 print >>f, "EXPORTS"
154
155 nm_pipe = os.popen(nm_command)
156 for line in nm_pipe.readlines():
157 m = export_match(line)
158 if m:
159 print >>f, m.group(1)
160 f.close()
161 exit = nm_pipe.close()
162
163 if exit:
164 print warning % "nm did not run successfully"
165 return False
166
167 if os.system(dlltool_command) != 0:
168 print warning % "dlltool did not run successfully"
169 return False
170
171 return True
172
173# Target files (.def and .a) go in PCBuild directory
174lib_file = os.path.join(srcdir, "PCBuild", "python%s%s.lib" % (major, minor))
175def_file = os.path.join(srcdir, "PCBuild", "python%s%s.def" % (major, minor))
176dll_file = "python%s%s.dll" % (major, minor)
177mingw_lib = os.path.join(srcdir, "PCBuild", "libpython%s%s.a" % (major, minor))
178
179have_mingw = build_mingw_lib(lib_file, def_file, dll_file, mingw_lib)
180
Martin v. Löwis856bf9a2006-02-14 20:42:55 +0000181# Determine the target architechture
182dll_path = os.path.join(srcdir, "PCBuild", dll_file)
183msilib.set_arch_from_file(dll_path)
184if msilib.pe_type(dll_path) != msilib.pe_type("msisupport.dll"):
185 raise SystemError, "msisupport.dll for incorrect architecture"
186
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000187if testpackage:
188 ext = 'px'
189 testprefix = 'x'
190else:
191 ext = 'py'
192 testprefix = ''
193
194if msilib.Win64:
195 SystemFolderName = "[SystemFolder64]"
196else:
197 SystemFolderName = "[SystemFolder]"
198
199msilib.reset()
200
201# condition in which to install pythonxy.dll in system32:
202# a) it is Windows 9x or
203# b) it is NT, the user is privileged, and has chosen per-machine installation
204sys32cond = "(Windows9x or (Privileged and ALLUSERS))"
205
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000206def build_database():
207 """Generate an empty database, with just the schema and the
208 Summary information stream."""
209 if snapshot:
210 uc = upgrade_code_snapshot
211 else:
212 uc = upgrade_code
213 # schema represents the installer 2.0 database schema.
214 # sequence is the set of standard sequences
215 # (ui/execute, admin/advt/install)
Martin v. Löwis856bf9a2006-02-14 20:42:55 +0000216 db = msilib.init_database("python-%s%s.msi" % (full_current_version, msilib.arch_ext),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000217 schema, ProductName="Python "+full_current_version,
218 ProductCode=product_code,
219 ProductVersion=current_version,
220 Manufacturer=u"Martin v. L\xf6wis")
221 # The default sequencing of the RemoveExistingProducts action causes
222 # removal of files that got just installed. Place it after
223 # InstallInitialize, so we first uninstall everything, but still roll
224 # back in case the installation is interrupted
225 msilib.change_sequence(sequence.InstallExecuteSequence,
226 "RemoveExistingProducts", 1510)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000227 msilib.add_tables(db, sequence)
228 # We cannot set ALLUSERS in the property table, as this cannot be
229 # reset if the user choses a per-user installation. Instead, we
230 # maintain WhichUsers, which can be "ALL" or "JUSTME". The UI manages
231 # this property, and when the execution starts, ALLUSERS is set
232 # accordingly.
233 add_data(db, "Property", [("UpgradeCode", uc),
234 ("WhichUsers", "ALL"),
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000235 ("ProductLine", "Python%s%s" % (major, minor)),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000236 ])
237 db.Commit()
238 return db
239
240def remove_old_versions(db):
241 "Fill the upgrade table."
242 start = "%s.%s.0" % (major, minor)
243 # This requests that feature selection states of an older
244 # installation should be forwarded into this one. Upgrading
245 # requires that both the old and the new installation are
246 # either both per-machine or per-user.
247 migrate_features = 1
248 # See "Upgrade Table". We remove releases with the same major and
249 # minor version. For an snapshot, we remove all earlier snapshots. For
250 # a release, we remove all snapshots, and all earlier releases.
251 if snapshot:
252 add_data(db, "Upgrade",
Tim Peters66cb0182004-08-26 05:23:19 +0000253 [(upgrade_code_snapshot, start,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000254 current_version,
255 None, # Ignore language
Tim Peters66cb0182004-08-26 05:23:19 +0000256 migrate_features,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000257 None, # Migrate ALL features
258 "REMOVEOLDSNAPSHOT")])
259 props = "REMOVEOLDSNAPSHOT"
260 else:
261 add_data(db, "Upgrade",
262 [(upgrade_code, start, current_version,
263 None, migrate_features, None, "REMOVEOLDVERSION"),
264 (upgrade_code_snapshot, start, "%s.%d.0" % (major, int(minor)+1),
265 None, migrate_features, None, "REMOVEOLDSNAPSHOT")])
266 props = "REMOVEOLDSNAPSHOT;REMOVEOLDVERSION"
267 # Installer collects the product codes of the earlier releases in
268 # these properties. In order to allow modification of the properties,
269 # they must be declared as secure. See "SecureCustomProperties Property"
270 add_data(db, "Property", [("SecureCustomProperties", props)])
271
272class PyDialog(Dialog):
273 """Dialog class with a fixed layout: controls at the top, then a ruler,
274 then a list of buttons: back, next, cancel. Optionally a bitmap at the
275 left."""
276 def __init__(self, *args, **kw):
277 """Dialog(database, name, x, y, w, h, attributes, title, first,
278 default, cancel, bitmap=true)"""
279 Dialog.__init__(self, *args)
280 ruler = self.h - 36
281 bmwidth = 152*ruler/328
282 if kw.get("bitmap", True):
283 self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin")
284 self.line("BottomLine", 0, ruler, self.w, 0)
285
286 def title(self, title):
287 "Set the title text of the dialog at the top."
288 # name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix,
289 # text, in VerdanaBold10
290 self.text("Title", 135, 10, 220, 60, 0x30003,
291 r"{\VerdanaBold10}%s" % title)
292
293 def back(self, title, next, name = "Back", active = 1):
294 """Add a back button with a given title, the tab-next button,
295 its name in the Control table, possibly initially disabled.
296
297 Return the button, so that events can be associated"""
298 if active:
299 flags = 3 # Visible|Enabled
300 else:
301 flags = 1 # Visible
302 return self.pushbutton(name, 180, self.h-27 , 56, 17, flags, title, next)
303
304 def cancel(self, title, next, name = "Cancel", active = 1):
305 """Add a cancel button with a given title, the tab-next button,
306 its name in the Control table, possibly initially disabled.
307
308 Return the button, so that events can be associated"""
309 if active:
310 flags = 3 # Visible|Enabled
311 else:
312 flags = 1 # Visible
313 return self.pushbutton(name, 304, self.h-27, 56, 17, flags, title, next)
314
315 def next(self, title, next, name = "Next", active = 1):
316 """Add a Next button with a given title, the tab-next button,
317 its name in the Control table, possibly initially disabled.
318
319 Return the button, so that events can be associated"""
320 if active:
321 flags = 3 # Visible|Enabled
322 else:
323 flags = 1 # Visible
324 return self.pushbutton(name, 236, self.h-27, 56, 17, flags, title, next)
325
326 def xbutton(self, name, title, next, xpos):
327 """Add a button with a given title, the tab-next button,
328 its name in the Control table, giving its x position; the
329 y-position is aligned with the other buttons.
330
331 Return the button, so that events can be associated"""
332 return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next)
333
334def add_ui(db):
335 x = y = 50
336 w = 370
337 h = 300
338 title = "[ProductName] Setup"
339
340 # see "Dialog Style Bits"
341 modal = 3 # visible | modal
342 modeless = 1 # visible
343 track_disk_space = 32
344
345 add_data(db, 'ActionText', uisample.ActionText)
346 add_data(db, 'UIText', uisample.UIText)
347
348 # Bitmaps
349 if not os.path.exists(srcdir+r"\PC\python_icon.exe"):
350 raise "Run icons.mak in PC directory"
351 add_data(db, "Binary",
352 [("PythonWin", msilib.Binary(srcdir+r"\PCbuild\installer.bmp")), # 152x328 pixels
353 ("py.ico",msilib.Binary(srcdir+r"\PC\py.ico")),
354 ])
355 add_data(db, "Icon",
356 [("python_icon.exe", msilib.Binary(srcdir+r"\PC\python_icon.exe"))])
357
358 # Scripts
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000359 # CheckDir sets TargetExists if TARGETDIR exists.
360 # UpdateEditIDLE sets the REGISTRY.tcl component into
361 # the installed/uninstalled state according to both the
362 # Extensions and TclTk features.
Martin v. Löwiseb68be42004-12-12 15:29:21 +0000363 if os.system("nmake /nologo /c /f msisupport.mak") != 0:
364 raise "'nmake /f msisupport.mak' failed"
365 add_data(db, "Binary", [("Script", msilib.Binary("msisupport.dll"))])
366 # See "Custom Action Type 1"
Martin v. Löwis3390d332005-03-14 17:20:13 +0000367 if msilib.Win64:
368 CheckDir = "CheckDir"
Martin v. Löwisdf40ce32006-02-16 14:38:30 +0000369 UpdateEditIDLE = "UpdateEditIDLE"
Martin v. Löwis3390d332005-03-14 17:20:13 +0000370 else:
371 CheckDir = "_CheckDir@4"
372 UpdateEditIDLE = "_UpdateEditIDLE@4"
Tim Peters0e9980f2004-09-12 03:49:31 +0000373 add_data(db, "CustomAction",
Martin v. Löwis3390d332005-03-14 17:20:13 +0000374 [("CheckDir", 1, "Script", CheckDir)])
Martin v. Löwiseac02e62004-11-18 08:00:33 +0000375 if have_tcl:
376 add_data(db, "CustomAction",
Martin v. Löwis3390d332005-03-14 17:20:13 +0000377 [("UpdateEditIDLE", 1, "Script", UpdateEditIDLE)])
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000378
379 # UI customization properties
380 add_data(db, "Property",
381 # See "DefaultUIFont Property"
382 [("DefaultUIFont", "DlgFont8"),
383 # See "ErrorDialog Style Bit"
384 ("ErrorDialog", "ErrorDlg"),
385 ("Progress1", "Install"), # modified in maintenance type dlg
386 ("Progress2", "installs"),
387 ("MaintenanceForm_Action", "Repair")])
388
389 # Fonts, see "TextStyle Table"
390 add_data(db, "TextStyle",
391 [("DlgFont8", "Tahoma", 9, None, 0),
392 ("DlgFontBold8", "Tahoma", 8, None, 1), #bold
393 ("VerdanaBold10", "Verdana", 10, None, 1),
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000394 ("VerdanaRed9", "Verdana", 9, 255, 0),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000395 ])
396
Martin v. Löwis7b2563b2004-11-02 22:59:56 +0000397 compileargs = r"-Wi [TARGETDIR]Lib\compileall.py -f -x badsyntax [TARGETDIR]Lib"
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000398 # See "CustomAction Table"
399 add_data(db, "CustomAction", [
400 # msidbCustomActionTypeFirstSequence + msidbCustomActionTypeTextData + msidbCustomActionTypeProperty
401 # See "Custom Action Type 51",
402 # "Custom Action Execution Scheduling Options"
403 ("InitialTargetDir", 307, "TARGETDIR",
404 "[WindowsVolume]Python%s%s" % (major, minor)),
405 ("SetDLLDirToTarget", 307, "DLLDIR", "[TARGETDIR]"),
406 ("SetDLLDirToSystem32", 307, "DLLDIR", SystemFolderName),
407 # msidbCustomActionTypeExe + msidbCustomActionTypeSourceFile
408 # See "Custom Action Type 18"
Martin v. Löwis7b2563b2004-11-02 22:59:56 +0000409 ("CompilePyc", 18, "python.exe", compileargs),
410 ("CompilePyo", 18, "python.exe", "-O "+compileargs),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000411 ])
412
413 # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table"
414 # Numbers indicate sequence; see sequence.py for how these action integrate
415 add_data(db, "InstallUISequence",
416 [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140),
417 ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141),
418 ("InitialTargetDir", 'TARGETDIR=""', 750),
419 # In the user interface, assume all-users installation if privileged.
420 ("SetDLLDirToSystem32", 'DLLDIR="" and ' + sys32cond, 751),
421 ("SetDLLDirToTarget", 'DLLDIR="" and not ' + sys32cond, 752),
422 ("SelectDirectoryDlg", "Not Installed", 1230),
423 # XXX no support for resume installations yet
424 #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240),
425 ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250),
426 ("ProgressDlg", None, 1280)])
427 add_data(db, "AdminUISequence",
428 [("InitialTargetDir", 'TARGETDIR=""', 750),
429 ("SetDLLDirToTarget", 'DLLDIR=""', 751),
430 ])
431
432 # Execute Sequences
433 add_data(db, "InstallExecuteSequence",
434 [("InitialTargetDir", 'TARGETDIR=""', 750),
435 ("SetDLLDirToSystem32", 'DLLDIR="" and ' + sys32cond, 751),
436 ("SetDLLDirToTarget", 'DLLDIR="" and not ' + sys32cond, 752),
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000437 ("UpdateEditIDLE", None, 1050),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000438 ("CompilePyc", "COMPILEALL", 6800),
439 ("CompilePyo", "COMPILEALL", 6801),
440 ])
441 add_data(db, "AdminExecuteSequence",
442 [("InitialTargetDir", 'TARGETDIR=""', 750),
443 ("SetDLLDirToTarget", 'DLLDIR=""', 751),
444 ("CompilePyc", "COMPILEALL", 6800),
445 ("CompilePyo", "COMPILEALL", 6801),
446 ])
447
448 #####################################################################
449 # Standard dialogs: FatalError, UserExit, ExitDialog
450 fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title,
451 "Finish", "Finish", "Finish")
452 fatal.title("[ProductName] Installer ended prematurely")
453 fatal.back("< Back", "Finish", active = 0)
454 fatal.cancel("Cancel", "Back", active = 0)
455 fatal.text("Description1", 135, 70, 220, 80, 0x30003,
456 "[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.")
457 fatal.text("Description2", 135, 155, 220, 20, 0x30003,
458 "Click the Finish button to exit the Installer.")
459 c=fatal.next("Finish", "Cancel", name="Finish")
460 # See "ControlEvent Table". Parameters are the event, the parameter
461 # to the action, and optionally the condition for the event, and the order
462 # of events.
463 c.event("EndDialog", "Exit")
Tim Peters66cb0182004-08-26 05:23:19 +0000464
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000465 user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title,
466 "Finish", "Finish", "Finish")
467 user_exit.title("[ProductName] Installer was interrupted")
468 user_exit.back("< Back", "Finish", active = 0)
469 user_exit.cancel("Cancel", "Back", active = 0)
470 user_exit.text("Description1", 135, 70, 220, 80, 0x30003,
471 "[ProductName] setup was interrupted. Your system has not been modified. "
472 "To install this program at a later time, please run the installation again.")
473 user_exit.text("Description2", 135, 155, 220, 20, 0x30003,
474 "Click the Finish button to exit the Installer.")
475 c = user_exit.next("Finish", "Cancel", name="Finish")
476 c.event("EndDialog", "Exit")
Tim Peters66cb0182004-08-26 05:23:19 +0000477
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000478 exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title,
479 "Finish", "Finish", "Finish")
480 exit_dialog.title("Completing the [ProductName] Installer")
481 exit_dialog.back("< Back", "Finish", active = 0)
482 exit_dialog.cancel("Cancel", "Back", active = 0)
Martin v. Löwis2dd2a282004-08-22 17:10:12 +0000483 exit_dialog.text("Acknowledgements", 135, 95, 220, 120, 0x30003,
484 "Special Windows thanks to:\n"
Martin v. Löwisd3f61a22004-08-30 09:22:30 +0000485 " LettError, Erik van Blokland, for the \n"
486 " Python for Windows graphic.\n"
Martin v. Löwis2dd2a282004-08-22 17:10:12 +0000487 " http://www.letterror.com/\n"
488 "\n"
Martin v. Löwisd3f61a22004-08-30 09:22:30 +0000489 " Mark Hammond, without whose years of freely \n"
490 " shared Windows expertise, Python for Windows \n"
491 " would still be Python for DOS.")
Tim Peters66cb0182004-08-26 05:23:19 +0000492
Martin v. Löwis8c7c56e2006-03-05 14:04:26 +0000493 c = exit_dialog.text("warning", 135, 200, 220, 40, 0x30003,
494 "{\\VerdanaRed9}Warning: Python 2.5.x is the last "
495 "Python release for Windows 9x.")
496 c.condition("Hide", "NOT Version9x")
497
Martin v. Löwis2dd2a282004-08-22 17:10:12 +0000498 exit_dialog.text("Description", 135, 235, 220, 20, 0x30003,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000499 "Click the Finish button to exit the Installer.")
500 c = exit_dialog.next("Finish", "Cancel", name="Finish")
501 c.event("EndDialog", "Return")
502
503 #####################################################################
504 # Required dialog: FilesInUse, ErrorDlg
505 inuse = PyDialog(db, "FilesInUse",
506 x, y, w, h,
507 19, # KeepModeless|Modal|Visible
508 title,
509 "Retry", "Retry", "Retry", bitmap=False)
510 inuse.text("Title", 15, 6, 200, 15, 0x30003,
511 r"{\DlgFontBold8}Files in Use")
512 inuse.text("Description", 20, 23, 280, 20, 0x30003,
513 "Some files that need to be updated are currently in use.")
514 inuse.text("Text", 20, 55, 330, 50, 3,
515 "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.")
516 inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess",
517 None, None, None)
518 c=inuse.back("Exit", "Ignore", name="Exit")
519 c.event("EndDialog", "Exit")
520 c=inuse.next("Ignore", "Retry", name="Ignore")
521 c.event("EndDialog", "Ignore")
522 c=inuse.cancel("Retry", "Exit", name="Retry")
523 c.event("EndDialog","Retry")
524
525
526 # See "Error Dialog". See "ICE20" for the required names of the controls.
527 error = Dialog(db, "ErrorDlg",
528 50, 10, 330, 101,
529 65543, # Error|Minimize|Modal|Visible
530 title,
531 "ErrorText", None, None)
532 error.text("ErrorText", 50,9,280,48,3, "")
533 error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None)
534 error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo")
535 error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes")
536 error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort")
537 error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel")
538 error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore")
539 error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk")
540 error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry")
541
542 #####################################################################
543 # Global "Query Cancel" dialog
544 cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title,
545 "No", "No", "No")
Tim Peters66cb0182004-08-26 05:23:19 +0000546 cancel.text("Text", 48, 15, 194, 30, 3,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000547 "Are you sure you want to cancel [ProductName] installation?")
548 cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
549 "py.ico", None, None)
550 c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No")
551 c.event("EndDialog", "Exit")
Tim Peters66cb0182004-08-26 05:23:19 +0000552
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000553 c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes")
554 c.event("EndDialog", "Return")
555
556 #####################################################################
557 # Global "Wait for costing" dialog
558 costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title,
559 "Return", "Return", "Return")
560 costing.text("Text", 48, 15, 194, 30, 3,
561 "Please wait while the installer finishes determining your disk space requirements.")
562 costing.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
563 "py.ico", None, None)
564 c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None)
565 c.event("EndDialog", "Exit")
566
567 #####################################################################
568 # Preparation dialog: no user input except cancellation
569 prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title,
570 "Cancel", "Cancel", "Cancel")
571 prep.text("Description", 135, 70, 220, 40, 0x30003,
572 "Please wait while the Installer prepares to guide you through the installation.")
573 prep.title("Welcome to the [ProductName] Installer")
574 c=prep.text("ActionText", 135, 110, 220, 20, 0x30003, "Pondering...")
575 c.mapping("ActionText", "Text")
576 c=prep.text("ActionData", 135, 135, 220, 30, 0x30003, None)
577 c.mapping("ActionData", "Text")
578 prep.back("Back", None, active=0)
579 prep.next("Next", None, active=0)
580 c=prep.cancel("Cancel", None)
581 c.event("SpawnDialog", "CancelDlg")
582
583 #####################################################################
584 # Target directory selection
585 seldlg = PyDialog(db, "SelectDirectoryDlg", x, y, w, h, modal, title,
586 "Next", "Next", "Cancel")
587 seldlg.title("Select Destination Directory")
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000588 c = seldlg.text("Existing", 135, 25, 235, 30, 0x30003,
589 "{\VerdanaRed9}This update will replace your existing [ProductLine] installation.")
590 c.condition("Hide", 'REMOVEOLDVERSION="" and REMOVEOLDSNAPSHOT=""')
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000591 seldlg.text("Description", 135, 50, 220, 40, 0x30003,
592 "Please select a directory for the [ProductName] files.")
593
594 seldlg.back("< Back", None, active=0)
595 c = seldlg.next("Next >", "Cancel")
596 c.event("DoAction", "CheckDir", "TargetExistsOk<>1", order=1)
597 # If the target exists, but we found that we are going to remove old versions, don't bother
598 # confirming that the target directory exists. Strictly speaking, we should determine that
599 # the target directory is indeed the target of the product that we are going to remove, but
600 # I don't know how to do that.
601 c.event("SpawnDialog", "ExistingDirectoryDlg", 'TargetExists=1 and REMOVEOLDVERSION="" and REMOVEOLDSNAPSHOT=""', 2)
602 c.event("SetTargetPath", "TARGETDIR", 'TargetExists=0 or REMOVEOLDVERSION<>"" or REMOVEOLDSNAPSHOT<>""', 3)
603 c.event("SpawnWaitDialog", "WaitForCostingDlg", "CostingComplete=1", 4)
604 c.event("NewDialog", "SelectFeaturesDlg", 'TargetExists=0 or REMOVEOLDVERSION<>"" or REMOVEOLDSNAPSHOT<>""', 5)
605
606 c = seldlg.cancel("Cancel", "DirectoryCombo")
607 c.event("SpawnDialog", "CancelDlg")
608
609 seldlg.control("DirectoryCombo", "DirectoryCombo", 135, 70, 172, 80, 393219,
610 "TARGETDIR", None, "DirectoryList", None)
611 seldlg.control("DirectoryList", "DirectoryList", 135, 90, 208, 136, 3, "TARGETDIR",
612 None, "PathEdit", None)
613 seldlg.control("PathEdit", "PathEdit", 135, 230, 206, 16, 3, "TARGETDIR", None, "Next", None)
614 c = seldlg.pushbutton("Up", 306, 70, 18, 18, 3, "Up", None)
615 c.event("DirectoryListUp", "0")
616 c = seldlg.pushbutton("NewDir", 324, 70, 30, 18, 3, "New", None)
617 c.event("DirectoryListNew", "0")
618
619 #####################################################################
620 # SelectFeaturesDlg
621 features = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal|track_disk_space,
622 title, "Tree", "Next", "Cancel")
623 features.title("Customize [ProductName]")
624 features.text("Description", 135, 35, 220, 15, 0x30003,
625 "Select the way you want features to be installed.")
626 features.text("Text", 135,45,220,30, 3,
627 "Click on the icons in the tree below to change the way features will be installed.")
628
629 c=features.back("< Back", "Next")
630 c.event("NewDialog", "SelectDirectoryDlg")
631
632 c=features.next("Next >", "Cancel")
633 c.mapping("SelectionNoItems", "Enabled")
634 c.event("SpawnDialog", "DiskCostDlg", "OutOfDiskSpace=1", order=1)
635 c.event("EndDialog", "Return", "OutOfDiskSpace<>1", order=2)
636
637 c=features.cancel("Cancel", "Tree")
638 c.event("SpawnDialog", "CancelDlg")
639
Tim Peters66cb0182004-08-26 05:23:19 +0000640 # 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 +0000641 features.control("Tree", "SelectionTree", 135, 75, 220, 95, 7, "_BrowseProperty",
642 "Tree of selections", "Back", None)
643
644 #c=features.pushbutton("Reset", 42, 243, 56, 17, 3, "Reset", "DiskCost")
645 #c.mapping("SelectionNoItems", "Enabled")
646 #c.event("Reset", "0")
Tim Peters66cb0182004-08-26 05:23:19 +0000647
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000648 features.control("Box", "GroupBox", 135, 170, 225, 90, 1, None, None, None, None)
649
650 c=features.xbutton("DiskCost", "Disk &Usage", None, 0.10)
651 c.mapping("SelectionNoItems","Enabled")
652 c.event("SpawnDialog", "DiskCostDlg")
653
654 c=features.xbutton("Advanced", "Advanced", None, 0.30)
655 c.event("SpawnDialog", "AdvancedDlg")
656
657 c=features.text("ItemDescription", 140, 180, 210, 30, 3,
658 "Multiline description of the currently selected item.")
659 c.mapping("SelectionDescription","Text")
Tim Peters66cb0182004-08-26 05:23:19 +0000660
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000661 c=features.text("ItemSize", 140, 210, 210, 45, 3,
662 "The size of the currently selected item.")
663 c.mapping("SelectionSize", "Text")
664
665 #####################################################################
666 # Disk cost
667 cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title,
668 "OK", "OK", "OK", bitmap=False)
669 cost.text("Title", 15, 6, 200, 15, 0x30003,
670 "{\DlgFontBold8}Disk Space Requirements")
671 cost.text("Description", 20, 20, 280, 20, 0x30003,
672 "The disk space required for the installation of the selected features.")
673 cost.text("Text", 20, 53, 330, 60, 3,
674 "The highlighted volumes (if any) do not have enough disk space "
675 "available for the currently selected features. You can either "
676 "remove some files from the highlighted volumes, or choose to "
677 "install less features onto local drive(s), or select different "
678 "destination drive(s).")
679 cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223,
680 None, "{120}{70}{70}{70}{70}", None, None)
681 cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return")
682
683 #####################################################################
684 # WhichUsers Dialog. Only available on NT, and for privileged users.
685 # This must be run before FindRelatedProducts, because that will
686 # take into account whether the previous installation was per-user
687 # or per-machine. We currently don't support going back to this
688 # dialog after "Next" was selected; to support this, we would need to
689 # find how to reset the ALLUSERS property, and how to re-run
690 # FindRelatedProducts.
691 # On Windows9x, the ALLUSERS property is ignored on the command line
692 # and in the Property table, but installer fails according to the documentation
693 # if a dialog attempts to set ALLUSERS.
694 whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title,
695 "AdminInstall", "Next", "Cancel")
696 whichusers.title("Select whether to install [ProductName] for all users of this computer.")
697 # A radio group with two options: allusers, justme
698 g = whichusers.radiogroup("AdminInstall", 135, 60, 160, 50, 3,
699 "WhichUsers", "", "Next")
700 g.add("ALL", 0, 5, 150, 20, "Install for all users")
701 g.add("JUSTME", 0, 25, 150, 20, "Install just for me")
702
Tim Peters66cb0182004-08-26 05:23:19 +0000703 whichusers.back("Back", None, active=0)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000704
705 c = whichusers.next("Next >", "Cancel")
706 c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1)
707 c.event("EndDialog", "Return", order = 2)
708
709 c = whichusers.cancel("Cancel", "AdminInstall")
710 c.event("SpawnDialog", "CancelDlg")
Tim Peters66cb0182004-08-26 05:23:19 +0000711
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000712 #####################################################################
713 # Advanced Dialog.
714 advanced = PyDialog(db, "AdvancedDlg", x, y, w, h, modal, title,
715 "CompilePyc", "Next", "Cancel")
716 advanced.title("Advanced Options for [ProductName]")
717 # A radio group with two options: allusers, justme
718 advanced.checkbox("CompilePyc", 135, 60, 230, 50, 3,
719 "COMPILEALL", "Compile .py files to byte code after installation", "Next")
720
721 c = advanced.next("Finish", "Cancel")
722 c.event("EndDialog", "Return")
723
724 c = advanced.cancel("Cancel", "CompilePyc")
725 c.event("SpawnDialog", "CancelDlg")
Tim Peters66cb0182004-08-26 05:23:19 +0000726
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000727 #####################################################################
Tim Peters66cb0182004-08-26 05:23:19 +0000728 # Existing Directory dialog
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000729 dlg = Dialog(db, "ExistingDirectoryDlg", 50, 30, 200, 80, modal, title,
730 "No", "No", "No")
731 dlg.text("Title", 10, 20, 180, 40, 3,
732 "[TARGETDIR] exists. Are you sure you want to overwrite existing files?")
733 c=dlg.pushbutton("Yes", 30, 60, 55, 17, 3, "Yes", "No")
734 c.event("[TargetExists]", "0", order=1)
735 c.event("[TargetExistsOk]", "1", order=2)
736 c.event("EndDialog", "Return", order=3)
737 c=dlg.pushbutton("No", 115, 60, 55, 17, 3, "No", "Yes")
738 c.event("EndDialog", "Return")
739
740 #####################################################################
741 # Installation Progress dialog (modeless)
742 progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title,
743 "Cancel", "Cancel", "Cancel", bitmap=False)
744 progress.text("Title", 20, 15, 200, 15, 0x30003,
745 "{\DlgFontBold8}[Progress1] [ProductName]")
746 progress.text("Text", 35, 65, 300, 30, 3,
747 "Please wait while the Installer [Progress2] [ProductName]. "
748 "This may take several minutes.")
749 progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:")
750
751 c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...")
752 c.mapping("ActionText", "Text")
753
754 #c=progress.text("ActionData", 35, 140, 300, 20, 3, None)
755 #c.mapping("ActionData", "Text")
756
757 c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537,
758 None, "Progress done", None, None)
759 c.mapping("SetProgress", "Progress")
760
761 progress.back("< Back", "Next", active=False)
762 progress.next("Next >", "Cancel", active=False)
763 progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg")
764
765 # Maintenance type: repair/uninstall
766 maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title,
767 "Next", "Next", "Cancel")
768 maint.title("Welcome to the [ProductName] Setup Wizard")
769 maint.text("BodyText", 135, 63, 230, 42, 3,
770 "Select whether you want to repair or remove [ProductName].")
771 g=maint.radiogroup("RepairRadioGroup", 135, 108, 230, 60, 3,
772 "MaintenanceForm_Action", "", "Next")
773 g.add("Change", 0, 0, 200, 17, "&Change [ProductName]")
774 g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]")
775 g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]")
Tim Peters66cb0182004-08-26 05:23:19 +0000776
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000777 maint.back("< Back", None, active=False)
778 c=maint.next("Finish", "Cancel")
779 # Change installation: Change progress dialog to "Change", then ask
780 # for feature selection
781 c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1)
782 c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2)
783
784 # Reinstall: Change progress dialog to "Repair", then invoke reinstall
785 # Also set list of reinstalled features to "ALL"
786 c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5)
787 c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6)
Raymond Hettinger72f08012004-11-07 07:08:25 +0000788 c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000789 c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8)
790
791 # Uninstall: Change progress to "Remove", then invoke uninstall
792 # Also set list of removed features to "ALL"
793 c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11)
794 c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12)
795 c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13)
796 c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14)
797
Tim Peters66cb0182004-08-26 05:23:19 +0000798 # Close dialog when maintenance action scheduled
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000799 c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20)
800 c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21)
Tim Peters66cb0182004-08-26 05:23:19 +0000801
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000802 maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg")
Tim Peters66cb0182004-08-26 05:23:19 +0000803
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000804
805# See "Feature Table". The feature level is 1 for all features,
806# and the feature attributes are 0 for the DefaultFeature, and
807# FollowParent for all other features. The numbers are the Display
808# column.
809def add_features(db):
810 # feature attributes:
811 # msidbFeatureAttributesFollowParent == 2
812 # msidbFeatureAttributesDisallowAdvertise == 8
813 # Features that need to be installed with together with the main feature
814 # (i.e. additional Python libraries) need to follow the parent feature.
815 # Features that have no advertisement trigger (e.g. the test suite)
816 # must not support advertisement
817 global default_feature, tcltk, htmlfiles, tools, testsuite, ext_feature
818 default_feature = Feature(db, "DefaultFeature", "Python",
819 "Python Interpreter and Libraries",
820 1, directory = "TARGETDIR")
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000821 # We don't support advertisement of extensions
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000822 ext_feature = Feature(db, "Extensions", "Register Extensions",
823 "Make this Python installation the default Python installation", 3,
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000824 parent = default_feature, attributes=2|8)
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000825 if have_tcl:
826 tcltk = Feature(db, "TclTk", "Tcl/Tk", "Tkinter, IDLE, pydoc", 5,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000827 parent = default_feature, attributes=2)
828 htmlfiles = Feature(db, "Documentation", "Documentation",
829 "Python HTMLHelp File", 7, parent = default_feature)
830 tools = Feature(db, "Tools", "Utility Scripts",
Tim Peters66cb0182004-08-26 05:23:19 +0000831 "Python utility scripts (Tools/", 9,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000832 parent = default_feature, attributes=2)
833 testsuite = Feature(db, "Testsuite", "Test suite",
834 "Python test suite (Lib/test/)", 11,
835 parent = default_feature, attributes=2|8)
Tim Peters66cb0182004-08-26 05:23:19 +0000836
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000837def extract_msvcr71():
838 import _winreg
839 # Find the location of the merge modules
840 k = _winreg.OpenKey(
841 _winreg.HKEY_LOCAL_MACHINE,
842 r"Software\Microsoft\VisualStudio\7.1\Setup\VS")
843 dir = _winreg.QueryValueEx(k, "MSMDir")[0]
844 _winreg.CloseKey(k)
845 files = glob.glob1(dir, "*CRT71*")
846 assert len(files) == 1
847 file = os.path.join(dir, files[0])
848 # Extract msvcr71.dll
849 m = msilib.MakeMerge2()
850 m.OpenModule(file, 0)
851 m.ExtractFiles(".")
852 m.CloseModule()
853 # Find the version/language of msvcr71.dll
854 installer = msilib.MakeInstaller()
855 return installer.FileVersion("msvcr71.dll", 0), \
856 installer.FileVersion("msvcr71.dll", 1)
857
858class PyDirectory(Directory):
859 """By default, all components in the Python installer
860 can run from source."""
861 def __init__(self, *args, **kw):
862 if not kw.has_key("componentflags"):
863 kw['componentflags'] = 2 #msidbComponentAttributesOptional
864 Directory.__init__(self, *args, **kw)
865
866# See "File Table", "Component Table", "Directory Table",
867# "FeatureComponents Table"
868def add_files(db):
869 cab = CAB("python")
870 tmpfiles = []
871 # Add all executables, icons, text files into the TARGETDIR component
872 root = PyDirectory(db, cab, None, srcdir, "TARGETDIR", "SourceDir")
873 default_feature.set_current()
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000874 if not msilib.Win64:
875 root.add_file("PCBuild/w9xpopen.exe")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000876 root.add_file("PC/py.ico")
877 root.add_file("PC/pyc.ico")
878 root.add_file("README.txt", src="README")
879 root.add_file("NEWS.txt", src="Misc/NEWS")
880 root.add_file("LICENSE.txt", src="LICENSE")
881 root.start_component("python.exe", keyfile="python.exe")
882 root.add_file("PCBuild/python.exe")
883 root.start_component("pythonw.exe", keyfile="pythonw.exe")
884 root.add_file("PCBuild/pythonw.exe")
885
886 # msidbComponentAttributesSharedDllRefCount = 8, see "Component Table"
887 dlldir = PyDirectory(db, cab, root, srcdir, "DLLDIR", ".")
888 pydll = "python%s%s.dll" % (major, minor)
889 pydllsrc = srcdir + "/PCBuild/" + pydll
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000890 dlldir.start_component("DLLDIR", flags = 8, keyfile = pydll, uuid = pythondll_uuid)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000891 installer = msilib.MakeInstaller()
892 pyversion = installer.FileVersion(pydllsrc, 0)
893 if not snapshot:
894 # For releases, the Python DLL has the same version as the
895 # installer package.
896 assert pyversion.split(".")[:3] == current_version.split(".")
897 dlldir.add_file("PCBuild/python%s%s.dll" % (major, minor),
898 version=pyversion,
899 language=installer.FileVersion(pydllsrc, 1))
900 # XXX determine dependencies
901 version, lang = extract_msvcr71()
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000902 dlldir.start_component("msvcr71", flags=8, keyfile="msvcr71.dll", uuid=msvcr71_uuid)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000903 dlldir.add_file("msvcr71.dll", src=os.path.abspath("msvcr71.dll"),
904 version=version, language=lang)
905 tmpfiles.append("msvcr71.dll")
Tim Peters66cb0182004-08-26 05:23:19 +0000906
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000907 # Add all .py files in Lib, except lib-tk, test
908 dirs={}
909 pydirs = [(root,"Lib")]
910 while pydirs:
911 parent, dir = pydirs.pop()
Martin v. Löwis9ca9f562006-01-03 06:29:53 +0000912 if dir == ".svn" or dir.startswith("plat-"):
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000913 continue
914 elif dir in ["lib-tk", "idlelib", "Icons"]:
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000915 if not have_tcl:
916 continue
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000917 tcltk.set_current()
Martin v. Löwis5c9e55e2004-12-30 14:08:18 +0000918 elif dir in ['test', 'tests', 'data', 'output']:
919 # test: Lib, Lib/email, Lib/bsddb
920 # tests: Lib/distutils
921 # data: Lib/email/test
922 # output: Lib/test
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000923 testsuite.set_current()
924 else:
925 default_feature.set_current()
926 lib = PyDirectory(db, cab, parent, dir, dir, "%s|%s" % (parent.make_short(dir), dir))
927 # Add additional files
928 dirs[dir]=lib
929 lib.glob("*.txt")
930 if dir=='site-packages':
Martin v. Löwis6d60c092004-11-21 10:16:26 +0000931 lib.add_file("README.txt", src="README")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000932 continue
933 files = lib.glob("*.py")
934 files += lib.glob("*.pyw")
935 if files:
936 # Add an entry to the RemoveFile table to remove bytecode files.
937 lib.remove_pyc()
938 if dir=='test' and parent.physical=='Lib':
939 lib.add_file("185test.db")
940 lib.add_file("audiotest.au")
941 lib.add_file("cfgparser.1")
942 lib.add_file("test.xml")
943 lib.add_file("test.xml.out")
944 lib.add_file("testtar.tar")
Martin v. Löwis7d3755d2004-09-06 06:31:12 +0000945 lib.add_file("test_difflib_expect.html")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000946 lib.glob("*.uue")
947 lib.add_file("readme.txt", src="README")
948 if dir=='decimaltestdata':
949 lib.glob("*.decTest")
950 if dir=='output':
951 lib.glob("test_*")
952 if dir=='idlelib':
953 lib.glob("*.def")
954 lib.add_file("idle.bat")
955 if dir=="Icons":
956 lib.glob("*.gif")
957 lib.add_file("idle.icns")
958 if dir=="command":
959 lib.add_file("wininst-6.exe")
960 lib.add_file("wininst-7.1.exe")
961 if dir=="data" and parent.physical=="test" and parent.basedir.physical=="email":
Martin v. Löwis9ca9f562006-01-03 06:29:53 +0000962 # This should contain all non-.svn files listed in subversion
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000963 for f in os.listdir(lib.absolute):
Martin v. Löwis9ca9f562006-01-03 06:29:53 +0000964 if f.endswith(".txt") or f==".svn":continue
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000965 if f.endswith(".au") or f.endswith(".gif"):
966 lib.add_file(f)
967 else:
968 print "WARNING: New file %s in email/test/data" % f
969 for f in os.listdir(lib.absolute):
970 if os.path.isdir(os.path.join(lib.absolute, f)):
971 pydirs.append((lib, f))
972 # Add DLLs
973 default_feature.set_current()
974 lib = PyDirectory(db, cab, root, srcdir+"/PCBuild", "DLLs", "DLLS|DLLs")
975 dlls = []
976 tclfiles = []
977 for f in extensions:
978 if f=="_tkinter.pyd":
979 continue
980 if not os.path.exists(srcdir+"/PCBuild/"+f):
981 print "WARNING: Missing extension", f
982 continue
983 dlls.append(f)
984 lib.add_file(f)
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000985 if have_tcl:
986 if not os.path.exists(srcdir+"/PCBuild/_tkinter.pyd"):
987 print "WARNING: Missing _tkinter.pyd"
988 else:
989 lib.start_component("TkDLLs", tcltk)
990 lib.add_file("_tkinter.pyd")
991 dlls.append("_tkinter.pyd")
992 tcldir = os.path.normpath(srcdir+"/../tcltk/bin")
993 for f in glob.glob1(tcldir, "*.dll"):
994 lib.add_file(f, src=os.path.join(tcldir, f))
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000995 # check whether there are any unknown extensions
996 for f in glob.glob1(srcdir+"/PCBuild", "*.pyd"):
997 if f.endswith("_d.pyd"): continue # debug version
998 if f in dlls: continue
999 print "WARNING: Unknown extension", f
Tim Peters66cb0182004-08-26 05:23:19 +00001000
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001001 # Add headers
1002 default_feature.set_current()
1003 lib = PyDirectory(db, cab, root, "include", "include", "INCLUDE|include")
1004 lib.glob("*.h")
1005 lib.add_file("pyconfig.h", src="../PC/pyconfig.h")
1006 # Add import libraries
1007 lib = PyDirectory(db, cab, root, "PCBuild", "libs", "LIBS|libs")
1008 for f in dlls:
1009 lib.add_file(f.replace('pyd','lib'))
1010 lib.add_file('python%s%s.lib' % (major, minor))
Martin v. Löwis9fda9312004-12-22 13:41:49 +00001011 # Add the mingw-format library
1012 if have_mingw:
Tim Peters5a9fb3c2005-01-07 16:01:32 +00001013 lib.add_file('libpython%s%s.a' % (major, minor))
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001014 if have_tcl:
1015 # Add Tcl/Tk
1016 tcldirs = [(root, '../tcltk/lib', 'tcl')]
1017 tcltk.set_current()
1018 while tcldirs:
1019 parent, phys, dir = tcldirs.pop()
1020 lib = PyDirectory(db, cab, parent, phys, dir, "%s|%s" % (parent.make_short(dir), dir))
1021 if not os.path.exists(lib.absolute):
1022 continue
1023 for f in os.listdir(lib.absolute):
1024 if os.path.isdir(os.path.join(lib.absolute, f)):
1025 tcldirs.append((lib, f, f))
1026 else:
1027 lib.add_file(f)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001028 # Add tools
1029 tools.set_current()
1030 tooldir = PyDirectory(db, cab, root, "Tools", "Tools", "TOOLS|Tools")
1031 for f in ['i18n', 'pynche', 'Scripts', 'versioncheck', 'webchecker']:
1032 lib = PyDirectory(db, cab, tooldir, f, f, "%s|%s" % (tooldir.make_short(f), f))
1033 lib.glob("*.py")
1034 lib.glob("*.pyw", exclude=['pydocgui.pyw'])
1035 lib.remove_pyc()
1036 lib.glob("*.txt")
1037 if f == "pynche":
1038 x = PyDirectory(db, cab, lib, "X", "X", "X|X")
1039 x.glob("*.txt")
Martin v. Löwis4d930be2004-12-01 21:46:35 +00001040 if os.path.exists(os.path.join(lib.absolute, "README")):
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001041 lib.add_file("README.txt", src="README")
Martin v. Löwis4d930be2004-12-01 21:46:35 +00001042 if f == 'Scripts':
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001043 if have_tcl:
1044 lib.start_component("pydocgui.pyw", tcltk, keyfile="pydocgui.pyw")
1045 lib.add_file("pydocgui.pyw")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001046 # Add documentation
1047 htmlfiles.set_current()
1048 lib = PyDirectory(db, cab, root, "Doc", "Doc", "DOC|Doc")
1049 lib.start_component("documentation", keyfile="Python%s%s.chm" % (major,minor))
1050 lib.add_file("Python%s%s.chm" % (major, minor))
1051
1052 cab.commit(db)
1053
1054 for f in tmpfiles:
1055 os.unlink(f)
1056
1057# See "Registry Table", "Component Table"
1058def add_registry(db):
1059 # File extensions, associated with the REGISTRY.def component
1060 # IDLE verbs depend on the tcltk feature.
1061 # msidbComponentAttributesRegistryKeyPath = 4
1062 # -1 for Root specifies "dependent on ALLUSERS property"
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001063 tcldata = []
1064 if have_tcl:
1065 tcldata = [
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001066 ("REGISTRY.tcl", msilib.gen_uuid(), "TARGETDIR", 4, None,
1067 "py.IDLE")]
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001068 add_data(db, "Component",
1069 # msidbComponentAttributesRegistryKeyPath = 4
1070 [("REGISTRY", msilib.gen_uuid(), "TARGETDIR", 4, None,
1071 "InstallPath"),
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001072 ("REGISTRY.doc", msilib.gen_uuid(), "TARGETDIR", 4, None,
1073 "Documentation"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001074 ("REGISTRY.def", msilib.gen_uuid(), "TARGETDIR", 4,
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001075 None, None)] + tcldata)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001076 # See "FeatureComponents Table".
1077 # The association between TclTk and pythonw.exe is necessary to make ICE59
1078 # happy, because the installer otherwise believes that the IDLE and PyDoc
1079 # shortcuts might get installed without pythonw.exe being install. This
1080 # is not true, since installing TclTk will install the default feature, which
1081 # will cause pythonw.exe to be installed.
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001082 # REGISTRY.tcl is not associated with any feature, as it will be requested
1083 # through a custom action
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001084 tcldata = []
1085 if have_tcl:
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001086 tcldata = [(tcltk.id, "pythonw.exe")]
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001087 add_data(db, "FeatureComponents",
1088 [(default_feature.id, "REGISTRY"),
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001089 (htmlfiles.id, "REGISTRY.doc"),
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001090 (ext_feature.id, "REGISTRY.def")] +
1091 tcldata
1092 )
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001093 # Extensions are not advertised. For advertised extensions,
1094 # we would need separate binaries that install along with the
1095 # extension.
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001096 pat = r"Software\Classes\%sPython.%sFile\shell\%s\command"
1097 ewi = "Edit with IDLE"
1098 pat2 = r"Software\Classes\%sPython.%sFile\DefaultIcon"
1099 pat3 = r"Software\Classes\%sPython.%sFile"
Martin v. Löwiseac02e62004-11-18 08:00:33 +00001100 tcl_verbs = []
1101 if have_tcl:
1102 tcl_verbs=[
1103 ("py.IDLE", -1, pat % (testprefix, "", ewi), "",
1104 r'"[TARGETDIR]pythonw.exe" "[TARGETDIR]Lib\idlelib\idle.pyw" -n -e "%1"',
1105 "REGISTRY.tcl"),
1106 ("pyw.IDLE", -1, pat % (testprefix, "NoCon", ewi), "",
1107 r'"[TARGETDIR]pythonw.exe" "[TARGETDIR]Lib\idlelib\idle.pyw" -n -e "%1"',
1108 "REGISTRY.tcl"),
1109 ]
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001110 add_data(db, "Registry",
1111 [# Extensions
1112 ("py.ext", -1, r"Software\Classes\."+ext, "",
1113 "Python.File", "REGISTRY.def"),
1114 ("pyw.ext", -1, r"Software\Classes\."+ext+'w', "",
1115 "Python.NoConFile", "REGISTRY.def"),
1116 ("pyc.ext", -1, r"Software\Classes\."+ext+'c', "",
1117 "Python.CompiledFile", "REGISTRY.def"),
1118 ("pyo.ext", -1, r"Software\Classes\."+ext+'o', "",
1119 "Python.CompiledFile", "REGISTRY.def"),
1120 # MIME types
1121 ("py.mime", -1, r"Software\Classes\."+ext, "Content Type",
1122 "text/plain", "REGISTRY.def"),
1123 ("pyw.mime", -1, r"Software\Classes\."+ext+'w', "Content Type",
1124 "text/plain", "REGISTRY.def"),
1125 #Verbs
1126 ("py.open", -1, pat % (testprefix, "", "open"), "",
1127 r'"[TARGETDIR]python.exe" "%1" %*', "REGISTRY.def"),
1128 ("pyw.open", -1, pat % (testprefix, "NoCon", "open"), "",
1129 r'"[TARGETDIR]pythonw.exe" "%1" %*', "REGISTRY.def"),
1130 ("pyc.open", -1, pat % (testprefix, "Compiled", "open"), "",
1131 r'"[TARGETDIR]python.exe" "%1" %*', "REGISTRY.def"),
Martin v. Löwiseac02e62004-11-18 08:00:33 +00001132 ] + tcl_verbs + [
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001133 #Icons
1134 ("py.icon", -1, pat2 % (testprefix, ""), "",
1135 r'[TARGETDIR]py.ico', "REGISTRY.def"),
1136 ("pyw.icon", -1, pat2 % (testprefix, "NoCon"), "",
1137 r'[TARGETDIR]py.ico', "REGISTRY.def"),
1138 ("pyc.icon", -1, pat2 % (testprefix, "Compiled"), "",
1139 r'[TARGETDIR]pyc.ico', "REGISTRY.def"),
1140 # Descriptions
1141 ("py.txt", -1, pat3 % (testprefix, ""), "",
1142 "Python File", "REGISTRY.def"),
1143 ("pyw.txt", -1, pat3 % (testprefix, "NoCon"), "",
1144 "Python File (no console)", "REGISTRY.def"),
1145 ("pyc.txt", -1, pat3 % (testprefix, "Compiled"), "",
1146 "Compiled Python File", "REGISTRY.def"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001147 ])
Tim Peters66cb0182004-08-26 05:23:19 +00001148
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001149 # Registry keys
1150 prefix = r"Software\%sPython\PythonCore\%s" % (testprefix, short_version)
1151 add_data(db, "Registry",
1152 [("InstallPath", -1, prefix+r"\InstallPath", "", "[TARGETDIR]", "REGISTRY"),
1153 ("InstallGroup", -1, prefix+r"\InstallPath\InstallGroup", "",
1154 "Python %s" % short_version, "REGISTRY"),
1155 ("PythonPath", -1, prefix+r"\PythonPath", "",
Martin v. Löwisf13337d2004-09-19 18:36:45 +00001156 r"[TARGETDIR]Lib;[TARGETDIR]DLLs;[TARGETDIR]Lib\lib-tk", "REGISTRY"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001157 ("Documentation", -1, prefix+r"\Help\Main Python Documentation", "",
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001158 r"[TARGETDIR]Doc\Python%s%s.chm" % (major, minor), "REGISTRY.doc"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001159 ("Modules", -1, prefix+r"\Modules", "+", None, "REGISTRY"),
1160 ("AppPaths", -1, r"Software\Microsoft\Windows\CurrentVersion\App Paths\Python.exe",
1161 "", r"[TARGETDIR]Python.exe", "REGISTRY.def")
1162 ])
1163 # Shortcuts, see "Shortcut Table"
1164 add_data(db, "Directory",
1165 [("ProgramMenuFolder", "TARGETDIR", "."),
1166 ("MenuDir", "ProgramMenuFolder", "PY%s%s|%sPython %s.%s" % (major,minor,testprefix,major,minor))])
1167 add_data(db, "RemoveFile",
1168 [("MenuDir", "TARGETDIR", None, "MenuDir", 2)])
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001169 tcltkshortcuts = []
1170 if have_tcl:
1171 tcltkshortcuts = [
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001172 ("IDLE", "MenuDir", "IDLE|IDLE (Python GUI)", "pythonw.exe",
Martin v. Löwisac191da2004-12-22 12:55:44 +00001173 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 +00001174 ("PyDoc", "MenuDir", "MODDOCS|Module Docs", "pythonw.exe",
Martin v. Löwisac191da2004-12-22 12:55:44 +00001175 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 +00001176 ]
1177 add_data(db, "Shortcut",
1178 tcltkshortcuts +
1179 [# Advertised shortcuts: targets are features, not files
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001180 ("Python", "MenuDir", "PYTHON|Python (command line)", "python.exe",
1181 default_feature.id, None, None, None, "python_icon.exe", 2, None, "TARGETDIR"),
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001182 # Advertising the Manual breaks on (some?) Win98, and the shortcut lacks an
1183 # icon first.
1184 #("Manual", "MenuDir", "MANUAL|Python Manuals", "documentation",
1185 # htmlfiles.id, None, None, None, None, None, None, None),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001186 ## Non-advertised shortcuts: must be associated with a registry component
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001187 ("Manual", "MenuDir", "MANUAL|Python Manuals", "REGISTRY.doc",
1188 "[#Python%s%s.chm]" % (major,minor), None,
1189 None, None, None, None, None, None),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001190 ("Uninstall", "MenuDir", "UNINST|Uninstall Python", "REGISTRY",
1191 SystemFolderName+"msiexec", "/x%s" % product_code,
1192 None, None, None, None, None, None),
1193 ])
1194 db.Commit()
1195
1196db = build_database()
1197try:
1198 add_features(db)
1199 add_ui(db)
1200 add_files(db)
1201 add_registry(db)
1202 remove_old_versions(db)
1203 db.Commit()
1204finally:
1205 del db