blob: f266fcc93e54b852739d072109241cbd63f78ed3 [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
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00009from uuids import product_codes
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +000010
11# Settings can be overridden in config.py below
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +000012# 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
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000027# Where is sqlite3.dll located, relative to srcdir?
28sqlite_dir = "../sqlite-source-3.3.4"
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +000029
30try:
31 from config import *
32except ImportError:
33 pass
34
35# Extract current version from Include/patchlevel.h
36lines = open(srcdir + "/Include/patchlevel.h").readlines()
37major = minor = micro = level = serial = None
38levels = {
39 'PY_RELEASE_LEVEL_ALPHA':0xA,
40 'PY_RELEASE_LEVEL_BETA': 0xB,
41 'PY_RELEASE_LEVEL_GAMMA':0xC,
42 'PY_RELEASE_LEVEL_FINAL':0xF
43 }
44for l in lines:
45 if not l.startswith("#define"):
46 continue
47 l = l.split()
48 if len(l) != 3:
49 continue
50 _, name, value = l
51 if name == 'PY_MAJOR_VERSION': major = value
52 if name == 'PY_MINOR_VERSION': minor = value
53 if name == 'PY_MICRO_VERSION': micro = value
54 if name == 'PY_RELEASE_LEVEL': level = levels[value]
55 if name == 'PY_RELEASE_SERIAL': serial = value
56
57short_version = major+"."+minor
58# See PC/make_versioninfo.c
59FIELD3 = 1000*int(micro) + 10*level + int(serial)
60current_version = "%s.%d" % (short_version, FIELD3)
61
62# This should never change. The UpgradeCode of this package can be
63# used in the Upgrade table of future packages to make the future
64# package replace this one. See "UpgradeCode Property".
65upgrade_code_snapshot='{92A24481-3ECB-40FC-8836-04B7966EC0D5}'
66upgrade_code='{65E6DE48-A358-434D-AA4F-4AF72DB4718F}'
67
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +000068if snapshot:
69 current_version = "%s.%s.%s" % (major, minor, int(time.time()/3600/24))
70 product_code = msilib.gen_uuid()
71else:
72 product_code = product_codes[current_version]
73
74if full_current_version is None:
75 full_current_version = current_version
76
77extensions = [
78 'bz2.pyd',
79 'pyexpat.pyd',
80 'select.pyd',
81 'unicodedata.pyd',
82 'winsound.pyd',
Trent Micke97e5a72005-12-15 22:08:46 +000083 '_elementtree.pyd',
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +000084 '_bsddb.pyd',
85 '_socket.pyd',
86 '_ssl.pyd',
87 '_testcapi.pyd',
88 '_tkinter.pyd',
Martin v. Löwis8c7c56e2006-03-05 14:04:26 +000089 '_msi.pyd',
Martin v. Löwisa09655e2006-03-10 15:36:28 +000090 '_ctypes.pyd',
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000091 '_ctypes_test.pyd',
Thomas Wouters00ee7ba2006-08-21 19:07:27 +000092 '_sqlite3.pyd',
93 '_hashlib.pyd'
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +000094]
95
Martin v. Löwis141f41a2005-03-15 00:39:40 +000096# Well-known component UUIDs
97# These are needed for SharedDLLs reference counter; if
98# a different UUID was used for each incarnation of, say,
99# python24.dll, an upgrade would set the reference counter
100# from 1 to 2 (due to what I consider a bug in MSI)
101# Using the same UUID is fine since these files are versioned,
102# so Installer will always keep the newest version.
103msvcr71_uuid = "{8666C8DD-D0B4-4B42-928E-A69E32FA5D4D}"
104pythondll_uuid = {
105 "24":"{9B81E618-2301-4035-AC77-75D9ABEB7301}",
Martin v. Löwis4c6c7ca2007-08-30 15:23:04 +0000106 "25":"{2e41b118-38bd-4c1b-a840-6977efd1b911}",
Guido van Rossumaf554a02007-08-16 23:48:43 +0000107 "26":"{34ebecac-f046-4e1c-b0e3-9bac3cdaacfa}",
Martin v. Löwis4c6c7ca2007-08-30 15:23:04 +0000108 "30":"{6953bc3b-6768-4291-8410-7914ce6e2ca8}",
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000109 } [major+minor]
Tim Peterseba28be2005-03-28 01:08:02 +0000110
Martin v. Löwis9fda9312004-12-22 13:41:49 +0000111# Build the mingw import library, libpythonXY.a
112# This requires 'nm' and 'dlltool' executables on your PATH
113def build_mingw_lib(lib_file, def_file, dll_file, mingw_lib):
114 warning = "WARNING: %s - libpythonXX.a not built"
115 nm = find_executable('nm')
116 dlltool = find_executable('dlltool')
117
118 if not nm or not dlltool:
Collin Winter6afaeb72007-08-03 17:06:41 +0000119 print(warning % "nm and/or dlltool were not found")
Martin v. Löwis9fda9312004-12-22 13:41:49 +0000120 return False
121
122 nm_command = '%s -Cs %s' % (nm, lib_file)
123 dlltool_command = "%s --dllname %s --def %s --output-lib %s" % \
124 (dlltool, dll_file, def_file, mingw_lib)
125 export_match = re.compile(r"^_imp__(.*) in python\d+\.dll").match
126
127 f = open(def_file,'w')
Martin v. Löwis4c6c7ca2007-08-30 15:23:04 +0000128 f.write("LIBRARY %s\n" % dll_file)
129 f.write("EXPORTS\n")
Martin v. Löwis9fda9312004-12-22 13:41:49 +0000130
131 nm_pipe = os.popen(nm_command)
132 for line in nm_pipe.readlines():
133 m = export_match(line)
134 if m:
Martin v. Löwis4c6c7ca2007-08-30 15:23:04 +0000135 f.write(m.group(1)+"\n")
Martin v. Löwis9fda9312004-12-22 13:41:49 +0000136 f.close()
137 exit = nm_pipe.close()
138
139 if exit:
Collin Winter6afaeb72007-08-03 17:06:41 +0000140 print(warning % "nm did not run successfully")
Martin v. Löwis9fda9312004-12-22 13:41:49 +0000141 return False
142
143 if os.system(dlltool_command) != 0:
Collin Winter6afaeb72007-08-03 17:06:41 +0000144 print(warning % "dlltool did not run successfully")
Martin v. Löwis9fda9312004-12-22 13:41:49 +0000145 return False
146
147 return True
148
149# Target files (.def and .a) go in PCBuild directory
150lib_file = os.path.join(srcdir, "PCBuild", "python%s%s.lib" % (major, minor))
151def_file = os.path.join(srcdir, "PCBuild", "python%s%s.def" % (major, minor))
152dll_file = "python%s%s.dll" % (major, minor)
153mingw_lib = os.path.join(srcdir, "PCBuild", "libpython%s%s.a" % (major, minor))
154
155have_mingw = build_mingw_lib(lib_file, def_file, dll_file, mingw_lib)
156
Martin v. Löwis856bf9a2006-02-14 20:42:55 +0000157# Determine the target architechture
158dll_path = os.path.join(srcdir, "PCBuild", dll_file)
159msilib.set_arch_from_file(dll_path)
160if msilib.pe_type(dll_path) != msilib.pe_type("msisupport.dll"):
Collin Wintera817e582007-08-22 23:05:06 +0000161 raise SystemError("msisupport.dll for incorrect architecture")
Martin v. Löwis856bf9a2006-02-14 20:42:55 +0000162
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000163if testpackage:
164 ext = 'px'
165 testprefix = 'x'
166else:
167 ext = 'py'
168 testprefix = ''
169
170if msilib.Win64:
171 SystemFolderName = "[SystemFolder64]"
172else:
173 SystemFolderName = "[SystemFolder]"
174
175msilib.reset()
176
177# condition in which to install pythonxy.dll in system32:
178# a) it is Windows 9x or
179# b) it is NT, the user is privileged, and has chosen per-machine installation
180sys32cond = "(Windows9x or (Privileged and ALLUSERS))"
181
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000182def build_database():
183 """Generate an empty database, with just the schema and the
184 Summary information stream."""
185 if snapshot:
186 uc = upgrade_code_snapshot
187 else:
188 uc = upgrade_code
189 # schema represents the installer 2.0 database schema.
190 # sequence is the set of standard sequences
191 # (ui/execute, admin/advt/install)
Martin v. Löwis856bf9a2006-02-14 20:42:55 +0000192 db = msilib.init_database("python-%s%s.msi" % (full_current_version, msilib.arch_ext),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000193 schema, ProductName="Python "+full_current_version,
194 ProductCode=product_code,
195 ProductVersion=current_version,
196 Manufacturer=u"Martin v. L\xf6wis")
197 # The default sequencing of the RemoveExistingProducts action causes
198 # removal of files that got just installed. Place it after
199 # InstallInitialize, so we first uninstall everything, but still roll
200 # back in case the installation is interrupted
201 msilib.change_sequence(sequence.InstallExecuteSequence,
202 "RemoveExistingProducts", 1510)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000203 msilib.add_tables(db, sequence)
204 # We cannot set ALLUSERS in the property table, as this cannot be
205 # reset if the user choses a per-user installation. Instead, we
206 # maintain WhichUsers, which can be "ALL" or "JUSTME". The UI manages
207 # this property, and when the execution starts, ALLUSERS is set
208 # accordingly.
209 add_data(db, "Property", [("UpgradeCode", uc),
210 ("WhichUsers", "ALL"),
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000211 ("ProductLine", "Python%s%s" % (major, minor)),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000212 ])
213 db.Commit()
214 return db
215
216def remove_old_versions(db):
217 "Fill the upgrade table."
218 start = "%s.%s.0" % (major, minor)
219 # This requests that feature selection states of an older
220 # installation should be forwarded into this one. Upgrading
221 # requires that both the old and the new installation are
222 # either both per-machine or per-user.
223 migrate_features = 1
224 # See "Upgrade Table". We remove releases with the same major and
225 # minor version. For an snapshot, we remove all earlier snapshots. For
226 # a release, we remove all snapshots, and all earlier releases.
227 if snapshot:
228 add_data(db, "Upgrade",
Tim Peters66cb0182004-08-26 05:23:19 +0000229 [(upgrade_code_snapshot, start,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000230 current_version,
231 None, # Ignore language
Tim Peters66cb0182004-08-26 05:23:19 +0000232 migrate_features,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000233 None, # Migrate ALL features
234 "REMOVEOLDSNAPSHOT")])
235 props = "REMOVEOLDSNAPSHOT"
236 else:
237 add_data(db, "Upgrade",
238 [(upgrade_code, start, current_version,
239 None, migrate_features, None, "REMOVEOLDVERSION"),
240 (upgrade_code_snapshot, start, "%s.%d.0" % (major, int(minor)+1),
241 None, migrate_features, None, "REMOVEOLDSNAPSHOT")])
242 props = "REMOVEOLDSNAPSHOT;REMOVEOLDVERSION"
243 # Installer collects the product codes of the earlier releases in
244 # these properties. In order to allow modification of the properties,
245 # they must be declared as secure. See "SecureCustomProperties Property"
246 add_data(db, "Property", [("SecureCustomProperties", props)])
247
248class PyDialog(Dialog):
249 """Dialog class with a fixed layout: controls at the top, then a ruler,
250 then a list of buttons: back, next, cancel. Optionally a bitmap at the
251 left."""
252 def __init__(self, *args, **kw):
253 """Dialog(database, name, x, y, w, h, attributes, title, first,
254 default, cancel, bitmap=true)"""
255 Dialog.__init__(self, *args)
256 ruler = self.h - 36
257 bmwidth = 152*ruler/328
258 if kw.get("bitmap", True):
259 self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin")
260 self.line("BottomLine", 0, ruler, self.w, 0)
261
262 def title(self, title):
263 "Set the title text of the dialog at the top."
264 # name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix,
265 # text, in VerdanaBold10
266 self.text("Title", 135, 10, 220, 60, 0x30003,
267 r"{\VerdanaBold10}%s" % title)
268
269 def back(self, title, next, name = "Back", active = 1):
270 """Add a back button with a given title, the tab-next button,
271 its name in the Control table, possibly initially disabled.
272
273 Return the button, so that events can be associated"""
274 if active:
275 flags = 3 # Visible|Enabled
276 else:
277 flags = 1 # Visible
278 return self.pushbutton(name, 180, self.h-27 , 56, 17, flags, title, next)
279
280 def cancel(self, title, next, name = "Cancel", active = 1):
281 """Add a cancel button with a given title, the tab-next button,
282 its name in the Control table, possibly initially disabled.
283
284 Return the button, so that events can be associated"""
285 if active:
286 flags = 3 # Visible|Enabled
287 else:
288 flags = 1 # Visible
289 return self.pushbutton(name, 304, self.h-27, 56, 17, flags, title, next)
290
291 def next(self, title, next, name = "Next", active = 1):
292 """Add a Next button with a given title, the tab-next button,
293 its name in the Control table, possibly initially disabled.
294
295 Return the button, so that events can be associated"""
296 if active:
297 flags = 3 # Visible|Enabled
298 else:
299 flags = 1 # Visible
300 return self.pushbutton(name, 236, self.h-27, 56, 17, flags, title, next)
301
302 def xbutton(self, name, title, next, xpos):
303 """Add a button with a given title, the tab-next button,
304 its name in the Control table, giving its x position; the
305 y-position is aligned with the other buttons.
306
307 Return the button, so that events can be associated"""
308 return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next)
309
310def add_ui(db):
311 x = y = 50
312 w = 370
313 h = 300
314 title = "[ProductName] Setup"
315
316 # see "Dialog Style Bits"
317 modal = 3 # visible | modal
318 modeless = 1 # visible
319 track_disk_space = 32
320
321 add_data(db, 'ActionText', uisample.ActionText)
322 add_data(db, 'UIText', uisample.UIText)
323
324 # Bitmaps
325 if not os.path.exists(srcdir+r"\PC\python_icon.exe"):
326 raise "Run icons.mak in PC directory"
327 add_data(db, "Binary",
328 [("PythonWin", msilib.Binary(srcdir+r"\PCbuild\installer.bmp")), # 152x328 pixels
329 ("py.ico",msilib.Binary(srcdir+r"\PC\py.ico")),
330 ])
331 add_data(db, "Icon",
332 [("python_icon.exe", msilib.Binary(srcdir+r"\PC\python_icon.exe"))])
333
334 # Scripts
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000335 # CheckDir sets TargetExists if TARGETDIR exists.
336 # UpdateEditIDLE sets the REGISTRY.tcl component into
337 # the installed/uninstalled state according to both the
338 # Extensions and TclTk features.
Martin v. Löwiseb68be42004-12-12 15:29:21 +0000339 if os.system("nmake /nologo /c /f msisupport.mak") != 0:
340 raise "'nmake /f msisupport.mak' failed"
341 add_data(db, "Binary", [("Script", msilib.Binary("msisupport.dll"))])
342 # See "Custom Action Type 1"
Martin v. Löwis3390d332005-03-14 17:20:13 +0000343 if msilib.Win64:
344 CheckDir = "CheckDir"
Martin v. Löwisdf40ce32006-02-16 14:38:30 +0000345 UpdateEditIDLE = "UpdateEditIDLE"
Martin v. Löwis3390d332005-03-14 17:20:13 +0000346 else:
347 CheckDir = "_CheckDir@4"
348 UpdateEditIDLE = "_UpdateEditIDLE@4"
Tim Peters0e9980f2004-09-12 03:49:31 +0000349 add_data(db, "CustomAction",
Martin v. Löwis3390d332005-03-14 17:20:13 +0000350 [("CheckDir", 1, "Script", CheckDir)])
Martin v. Löwiseac02e62004-11-18 08:00:33 +0000351 if have_tcl:
352 add_data(db, "CustomAction",
Martin v. Löwis3390d332005-03-14 17:20:13 +0000353 [("UpdateEditIDLE", 1, "Script", UpdateEditIDLE)])
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000354
355 # UI customization properties
356 add_data(db, "Property",
357 # See "DefaultUIFont Property"
358 [("DefaultUIFont", "DlgFont8"),
359 # See "ErrorDialog Style Bit"
360 ("ErrorDialog", "ErrorDlg"),
361 ("Progress1", "Install"), # modified in maintenance type dlg
362 ("Progress2", "installs"),
363 ("MaintenanceForm_Action", "Repair")])
364
365 # Fonts, see "TextStyle Table"
366 add_data(db, "TextStyle",
367 [("DlgFont8", "Tahoma", 9, None, 0),
368 ("DlgFontBold8", "Tahoma", 8, None, 1), #bold
369 ("VerdanaBold10", "Verdana", 10, None, 1),
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000370 ("VerdanaRed9", "Verdana", 9, 255, 0),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000371 ])
372
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000373 compileargs = r'-Wi "[TARGETDIR]Lib\compileall.py" -f -x bad_coding|badsyntax|site-packages "[TARGETDIR]Lib"'
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000374 # See "CustomAction Table"
375 add_data(db, "CustomAction", [
376 # msidbCustomActionTypeFirstSequence + msidbCustomActionTypeTextData + msidbCustomActionTypeProperty
377 # See "Custom Action Type 51",
378 # "Custom Action Execution Scheduling Options"
379 ("InitialTargetDir", 307, "TARGETDIR",
380 "[WindowsVolume]Python%s%s" % (major, minor)),
381 ("SetDLLDirToTarget", 307, "DLLDIR", "[TARGETDIR]"),
382 ("SetDLLDirToSystem32", 307, "DLLDIR", SystemFolderName),
383 # msidbCustomActionTypeExe + msidbCustomActionTypeSourceFile
384 # See "Custom Action Type 18"
Martin v. Löwis7b2563b2004-11-02 22:59:56 +0000385 ("CompilePyc", 18, "python.exe", compileargs),
386 ("CompilePyo", 18, "python.exe", "-O "+compileargs),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000387 ])
388
389 # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table"
390 # Numbers indicate sequence; see sequence.py for how these action integrate
391 add_data(db, "InstallUISequence",
392 [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140),
393 ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141),
394 ("InitialTargetDir", 'TARGETDIR=""', 750),
395 # In the user interface, assume all-users installation if privileged.
396 ("SetDLLDirToSystem32", 'DLLDIR="" and ' + sys32cond, 751),
397 ("SetDLLDirToTarget", 'DLLDIR="" and not ' + sys32cond, 752),
398 ("SelectDirectoryDlg", "Not Installed", 1230),
399 # XXX no support for resume installations yet
400 #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240),
401 ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250),
402 ("ProgressDlg", None, 1280)])
403 add_data(db, "AdminUISequence",
404 [("InitialTargetDir", 'TARGETDIR=""', 750),
405 ("SetDLLDirToTarget", 'DLLDIR=""', 751),
406 ])
407
408 # Execute Sequences
409 add_data(db, "InstallExecuteSequence",
410 [("InitialTargetDir", 'TARGETDIR=""', 750),
411 ("SetDLLDirToSystem32", 'DLLDIR="" and ' + sys32cond, 751),
412 ("SetDLLDirToTarget", 'DLLDIR="" and not ' + sys32cond, 752),
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000413 ("UpdateEditIDLE", None, 1050),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000414 ("CompilePyc", "COMPILEALL", 6800),
415 ("CompilePyo", "COMPILEALL", 6801),
416 ])
417 add_data(db, "AdminExecuteSequence",
418 [("InitialTargetDir", 'TARGETDIR=""', 750),
419 ("SetDLLDirToTarget", 'DLLDIR=""', 751),
420 ("CompilePyc", "COMPILEALL", 6800),
421 ("CompilePyo", "COMPILEALL", 6801),
422 ])
423
424 #####################################################################
425 # Standard dialogs: FatalError, UserExit, ExitDialog
426 fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title,
427 "Finish", "Finish", "Finish")
428 fatal.title("[ProductName] Installer ended prematurely")
429 fatal.back("< Back", "Finish", active = 0)
430 fatal.cancel("Cancel", "Back", active = 0)
431 fatal.text("Description1", 135, 70, 220, 80, 0x30003,
432 "[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.")
433 fatal.text("Description2", 135, 155, 220, 20, 0x30003,
434 "Click the Finish button to exit the Installer.")
435 c=fatal.next("Finish", "Cancel", name="Finish")
436 # See "ControlEvent Table". Parameters are the event, the parameter
437 # to the action, and optionally the condition for the event, and the order
438 # of events.
439 c.event("EndDialog", "Exit")
Tim Peters66cb0182004-08-26 05:23:19 +0000440
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000441 user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title,
442 "Finish", "Finish", "Finish")
443 user_exit.title("[ProductName] Installer was interrupted")
444 user_exit.back("< Back", "Finish", active = 0)
445 user_exit.cancel("Cancel", "Back", active = 0)
446 user_exit.text("Description1", 135, 70, 220, 80, 0x30003,
447 "[ProductName] setup was interrupted. Your system has not been modified. "
448 "To install this program at a later time, please run the installation again.")
449 user_exit.text("Description2", 135, 155, 220, 20, 0x30003,
450 "Click the Finish button to exit the Installer.")
451 c = user_exit.next("Finish", "Cancel", name="Finish")
452 c.event("EndDialog", "Exit")
Tim Peters66cb0182004-08-26 05:23:19 +0000453
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000454 exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title,
455 "Finish", "Finish", "Finish")
456 exit_dialog.title("Completing the [ProductName] Installer")
457 exit_dialog.back("< Back", "Finish", active = 0)
458 exit_dialog.cancel("Cancel", "Back", active = 0)
Martin v. Löwis2dd2a282004-08-22 17:10:12 +0000459 exit_dialog.text("Acknowledgements", 135, 95, 220, 120, 0x30003,
460 "Special Windows thanks to:\n"
Martin v. Löwisd3f61a22004-08-30 09:22:30 +0000461 " Mark Hammond, without whose years of freely \n"
462 " shared Windows expertise, Python for Windows \n"
463 " would still be Python for DOS.")
Tim Peters66cb0182004-08-26 05:23:19 +0000464
Martin v. Löwis8c7c56e2006-03-05 14:04:26 +0000465 c = exit_dialog.text("warning", 135, 200, 220, 40, 0x30003,
466 "{\\VerdanaRed9}Warning: Python 2.5.x is the last "
467 "Python release for Windows 9x.")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000468 c.condition("Hide", "NOT Version9X")
Martin v. Löwis8c7c56e2006-03-05 14:04:26 +0000469
Martin v. Löwis2dd2a282004-08-22 17:10:12 +0000470 exit_dialog.text("Description", 135, 235, 220, 20, 0x30003,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000471 "Click the Finish button to exit the Installer.")
472 c = exit_dialog.next("Finish", "Cancel", name="Finish")
473 c.event("EndDialog", "Return")
474
475 #####################################################################
476 # Required dialog: FilesInUse, ErrorDlg
477 inuse = PyDialog(db, "FilesInUse",
478 x, y, w, h,
479 19, # KeepModeless|Modal|Visible
480 title,
481 "Retry", "Retry", "Retry", bitmap=False)
482 inuse.text("Title", 15, 6, 200, 15, 0x30003,
483 r"{\DlgFontBold8}Files in Use")
484 inuse.text("Description", 20, 23, 280, 20, 0x30003,
485 "Some files that need to be updated are currently in use.")
486 inuse.text("Text", 20, 55, 330, 50, 3,
487 "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.")
488 inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess",
489 None, None, None)
490 c=inuse.back("Exit", "Ignore", name="Exit")
491 c.event("EndDialog", "Exit")
492 c=inuse.next("Ignore", "Retry", name="Ignore")
493 c.event("EndDialog", "Ignore")
494 c=inuse.cancel("Retry", "Exit", name="Retry")
495 c.event("EndDialog","Retry")
496
497
498 # See "Error Dialog". See "ICE20" for the required names of the controls.
499 error = Dialog(db, "ErrorDlg",
500 50, 10, 330, 101,
501 65543, # Error|Minimize|Modal|Visible
502 title,
503 "ErrorText", None, None)
504 error.text("ErrorText", 50,9,280,48,3, "")
505 error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None)
506 error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo")
507 error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes")
508 error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort")
509 error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel")
510 error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore")
511 error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk")
512 error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry")
513
514 #####################################################################
515 # Global "Query Cancel" dialog
516 cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title,
517 "No", "No", "No")
Tim Peters66cb0182004-08-26 05:23:19 +0000518 cancel.text("Text", 48, 15, 194, 30, 3,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000519 "Are you sure you want to cancel [ProductName] installation?")
520 cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
521 "py.ico", None, None)
522 c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No")
523 c.event("EndDialog", "Exit")
Tim Peters66cb0182004-08-26 05:23:19 +0000524
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000525 c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes")
526 c.event("EndDialog", "Return")
527
528 #####################################################################
529 # Global "Wait for costing" dialog
530 costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title,
531 "Return", "Return", "Return")
532 costing.text("Text", 48, 15, 194, 30, 3,
533 "Please wait while the installer finishes determining your disk space requirements.")
534 costing.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
535 "py.ico", None, None)
536 c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None)
537 c.event("EndDialog", "Exit")
538
539 #####################################################################
540 # Preparation dialog: no user input except cancellation
541 prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title,
542 "Cancel", "Cancel", "Cancel")
543 prep.text("Description", 135, 70, 220, 40, 0x30003,
544 "Please wait while the Installer prepares to guide you through the installation.")
545 prep.title("Welcome to the [ProductName] Installer")
546 c=prep.text("ActionText", 135, 110, 220, 20, 0x30003, "Pondering...")
547 c.mapping("ActionText", "Text")
548 c=prep.text("ActionData", 135, 135, 220, 30, 0x30003, None)
549 c.mapping("ActionData", "Text")
550 prep.back("Back", None, active=0)
551 prep.next("Next", None, active=0)
552 c=prep.cancel("Cancel", None)
553 c.event("SpawnDialog", "CancelDlg")
554
555 #####################################################################
556 # Target directory selection
557 seldlg = PyDialog(db, "SelectDirectoryDlg", x, y, w, h, modal, title,
558 "Next", "Next", "Cancel")
559 seldlg.title("Select Destination Directory")
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000560 c = seldlg.text("Existing", 135, 25, 235, 30, 0x30003,
561 "{\VerdanaRed9}This update will replace your existing [ProductLine] installation.")
562 c.condition("Hide", 'REMOVEOLDVERSION="" and REMOVEOLDSNAPSHOT=""')
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000563 seldlg.text("Description", 135, 50, 220, 40, 0x30003,
564 "Please select a directory for the [ProductName] files.")
565
566 seldlg.back("< Back", None, active=0)
567 c = seldlg.next("Next >", "Cancel")
568 c.event("DoAction", "CheckDir", "TargetExistsOk<>1", order=1)
569 # If the target exists, but we found that we are going to remove old versions, don't bother
570 # confirming that the target directory exists. Strictly speaking, we should determine that
571 # the target directory is indeed the target of the product that we are going to remove, but
572 # I don't know how to do that.
573 c.event("SpawnDialog", "ExistingDirectoryDlg", 'TargetExists=1 and REMOVEOLDVERSION="" and REMOVEOLDSNAPSHOT=""', 2)
574 c.event("SetTargetPath", "TARGETDIR", 'TargetExists=0 or REMOVEOLDVERSION<>"" or REMOVEOLDSNAPSHOT<>""', 3)
575 c.event("SpawnWaitDialog", "WaitForCostingDlg", "CostingComplete=1", 4)
576 c.event("NewDialog", "SelectFeaturesDlg", 'TargetExists=0 or REMOVEOLDVERSION<>"" or REMOVEOLDSNAPSHOT<>""', 5)
577
578 c = seldlg.cancel("Cancel", "DirectoryCombo")
579 c.event("SpawnDialog", "CancelDlg")
580
581 seldlg.control("DirectoryCombo", "DirectoryCombo", 135, 70, 172, 80, 393219,
582 "TARGETDIR", None, "DirectoryList", None)
583 seldlg.control("DirectoryList", "DirectoryList", 135, 90, 208, 136, 3, "TARGETDIR",
584 None, "PathEdit", None)
585 seldlg.control("PathEdit", "PathEdit", 135, 230, 206, 16, 3, "TARGETDIR", None, "Next", None)
586 c = seldlg.pushbutton("Up", 306, 70, 18, 18, 3, "Up", None)
587 c.event("DirectoryListUp", "0")
588 c = seldlg.pushbutton("NewDir", 324, 70, 30, 18, 3, "New", None)
589 c.event("DirectoryListNew", "0")
590
591 #####################################################################
592 # SelectFeaturesDlg
593 features = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal|track_disk_space,
594 title, "Tree", "Next", "Cancel")
595 features.title("Customize [ProductName]")
596 features.text("Description", 135, 35, 220, 15, 0x30003,
597 "Select the way you want features to be installed.")
598 features.text("Text", 135,45,220,30, 3,
599 "Click on the icons in the tree below to change the way features will be installed.")
600
601 c=features.back("< Back", "Next")
602 c.event("NewDialog", "SelectDirectoryDlg")
603
604 c=features.next("Next >", "Cancel")
605 c.mapping("SelectionNoItems", "Enabled")
606 c.event("SpawnDialog", "DiskCostDlg", "OutOfDiskSpace=1", order=1)
607 c.event("EndDialog", "Return", "OutOfDiskSpace<>1", order=2)
608
609 c=features.cancel("Cancel", "Tree")
610 c.event("SpawnDialog", "CancelDlg")
611
Tim Peters66cb0182004-08-26 05:23:19 +0000612 # 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 +0000613 features.control("Tree", "SelectionTree", 135, 75, 220, 95, 7, "_BrowseProperty",
614 "Tree of selections", "Back", None)
615
616 #c=features.pushbutton("Reset", 42, 243, 56, 17, 3, "Reset", "DiskCost")
617 #c.mapping("SelectionNoItems", "Enabled")
618 #c.event("Reset", "0")
Tim Peters66cb0182004-08-26 05:23:19 +0000619
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000620 features.control("Box", "GroupBox", 135, 170, 225, 90, 1, None, None, None, None)
621
622 c=features.xbutton("DiskCost", "Disk &Usage", None, 0.10)
623 c.mapping("SelectionNoItems","Enabled")
624 c.event("SpawnDialog", "DiskCostDlg")
625
626 c=features.xbutton("Advanced", "Advanced", None, 0.30)
627 c.event("SpawnDialog", "AdvancedDlg")
628
629 c=features.text("ItemDescription", 140, 180, 210, 30, 3,
630 "Multiline description of the currently selected item.")
631 c.mapping("SelectionDescription","Text")
Tim Peters66cb0182004-08-26 05:23:19 +0000632
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000633 c=features.text("ItemSize", 140, 210, 210, 45, 3,
634 "The size of the currently selected item.")
635 c.mapping("SelectionSize", "Text")
636
637 #####################################################################
638 # Disk cost
639 cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title,
640 "OK", "OK", "OK", bitmap=False)
641 cost.text("Title", 15, 6, 200, 15, 0x30003,
642 "{\DlgFontBold8}Disk Space Requirements")
643 cost.text("Description", 20, 20, 280, 20, 0x30003,
644 "The disk space required for the installation of the selected features.")
645 cost.text("Text", 20, 53, 330, 60, 3,
646 "The highlighted volumes (if any) do not have enough disk space "
647 "available for the currently selected features. You can either "
648 "remove some files from the highlighted volumes, or choose to "
649 "install less features onto local drive(s), or select different "
650 "destination drive(s).")
651 cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223,
652 None, "{120}{70}{70}{70}{70}", None, None)
653 cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return")
654
655 #####################################################################
656 # WhichUsers Dialog. Only available on NT, and for privileged users.
657 # This must be run before FindRelatedProducts, because that will
658 # take into account whether the previous installation was per-user
659 # or per-machine. We currently don't support going back to this
660 # dialog after "Next" was selected; to support this, we would need to
661 # find how to reset the ALLUSERS property, and how to re-run
662 # FindRelatedProducts.
663 # On Windows9x, the ALLUSERS property is ignored on the command line
664 # and in the Property table, but installer fails according to the documentation
665 # if a dialog attempts to set ALLUSERS.
666 whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title,
667 "AdminInstall", "Next", "Cancel")
668 whichusers.title("Select whether to install [ProductName] for all users of this computer.")
669 # A radio group with two options: allusers, justme
670 g = whichusers.radiogroup("AdminInstall", 135, 60, 160, 50, 3,
671 "WhichUsers", "", "Next")
672 g.add("ALL", 0, 5, 150, 20, "Install for all users")
673 g.add("JUSTME", 0, 25, 150, 20, "Install just for me")
674
Tim Peters66cb0182004-08-26 05:23:19 +0000675 whichusers.back("Back", None, active=0)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000676
677 c = whichusers.next("Next >", "Cancel")
678 c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1)
679 c.event("EndDialog", "Return", order = 2)
680
681 c = whichusers.cancel("Cancel", "AdminInstall")
682 c.event("SpawnDialog", "CancelDlg")
Tim Peters66cb0182004-08-26 05:23:19 +0000683
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000684 #####################################################################
685 # Advanced Dialog.
686 advanced = PyDialog(db, "AdvancedDlg", x, y, w, h, modal, title,
687 "CompilePyc", "Next", "Cancel")
688 advanced.title("Advanced Options for [ProductName]")
689 # A radio group with two options: allusers, justme
690 advanced.checkbox("CompilePyc", 135, 60, 230, 50, 3,
691 "COMPILEALL", "Compile .py files to byte code after installation", "Next")
692
693 c = advanced.next("Finish", "Cancel")
694 c.event("EndDialog", "Return")
695
696 c = advanced.cancel("Cancel", "CompilePyc")
697 c.event("SpawnDialog", "CancelDlg")
Tim Peters66cb0182004-08-26 05:23:19 +0000698
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000699 #####################################################################
Tim Peters66cb0182004-08-26 05:23:19 +0000700 # Existing Directory dialog
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000701 dlg = Dialog(db, "ExistingDirectoryDlg", 50, 30, 200, 80, modal, title,
702 "No", "No", "No")
703 dlg.text("Title", 10, 20, 180, 40, 3,
704 "[TARGETDIR] exists. Are you sure you want to overwrite existing files?")
705 c=dlg.pushbutton("Yes", 30, 60, 55, 17, 3, "Yes", "No")
706 c.event("[TargetExists]", "0", order=1)
707 c.event("[TargetExistsOk]", "1", order=2)
708 c.event("EndDialog", "Return", order=3)
709 c=dlg.pushbutton("No", 115, 60, 55, 17, 3, "No", "Yes")
710 c.event("EndDialog", "Return")
711
712 #####################################################################
713 # Installation Progress dialog (modeless)
714 progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title,
715 "Cancel", "Cancel", "Cancel", bitmap=False)
716 progress.text("Title", 20, 15, 200, 15, 0x30003,
717 "{\DlgFontBold8}[Progress1] [ProductName]")
718 progress.text("Text", 35, 65, 300, 30, 3,
719 "Please wait while the Installer [Progress2] [ProductName]. "
720 "This may take several minutes.")
721 progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:")
722
723 c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...")
724 c.mapping("ActionText", "Text")
725
726 #c=progress.text("ActionData", 35, 140, 300, 20, 3, None)
727 #c.mapping("ActionData", "Text")
728
729 c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537,
730 None, "Progress done", None, None)
731 c.mapping("SetProgress", "Progress")
732
733 progress.back("< Back", "Next", active=False)
734 progress.next("Next >", "Cancel", active=False)
735 progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg")
736
737 # Maintenance type: repair/uninstall
738 maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title,
739 "Next", "Next", "Cancel")
740 maint.title("Welcome to the [ProductName] Setup Wizard")
741 maint.text("BodyText", 135, 63, 230, 42, 3,
742 "Select whether you want to repair or remove [ProductName].")
743 g=maint.radiogroup("RepairRadioGroup", 135, 108, 230, 60, 3,
744 "MaintenanceForm_Action", "", "Next")
745 g.add("Change", 0, 0, 200, 17, "&Change [ProductName]")
746 g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]")
747 g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]")
Tim Peters66cb0182004-08-26 05:23:19 +0000748
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000749 maint.back("< Back", None, active=False)
750 c=maint.next("Finish", "Cancel")
751 # Change installation: Change progress dialog to "Change", then ask
752 # for feature selection
753 c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1)
754 c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2)
755
756 # Reinstall: Change progress dialog to "Repair", then invoke reinstall
757 # Also set list of reinstalled features to "ALL"
758 c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5)
759 c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6)
Raymond Hettinger72f08012004-11-07 07:08:25 +0000760 c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000761 c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8)
762
763 # Uninstall: Change progress to "Remove", then invoke uninstall
764 # Also set list of removed features to "ALL"
765 c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11)
766 c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12)
767 c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13)
768 c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14)
769
Tim Peters66cb0182004-08-26 05:23:19 +0000770 # Close dialog when maintenance action scheduled
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000771 c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20)
772 c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21)
Tim Peters66cb0182004-08-26 05:23:19 +0000773
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000774 maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg")
Tim Peters66cb0182004-08-26 05:23:19 +0000775
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000776
777# See "Feature Table". The feature level is 1 for all features,
778# and the feature attributes are 0 for the DefaultFeature, and
779# FollowParent for all other features. The numbers are the Display
780# column.
781def add_features(db):
782 # feature attributes:
783 # msidbFeatureAttributesFollowParent == 2
784 # msidbFeatureAttributesDisallowAdvertise == 8
785 # Features that need to be installed with together with the main feature
786 # (i.e. additional Python libraries) need to follow the parent feature.
787 # Features that have no advertisement trigger (e.g. the test suite)
788 # must not support advertisement
789 global default_feature, tcltk, htmlfiles, tools, testsuite, ext_feature
790 default_feature = Feature(db, "DefaultFeature", "Python",
791 "Python Interpreter and Libraries",
792 1, directory = "TARGETDIR")
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000793 # We don't support advertisement of extensions
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000794 ext_feature = Feature(db, "Extensions", "Register Extensions",
795 "Make this Python installation the default Python installation", 3,
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000796 parent = default_feature, attributes=2|8)
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000797 if have_tcl:
798 tcltk = Feature(db, "TclTk", "Tcl/Tk", "Tkinter, IDLE, pydoc", 5,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000799 parent = default_feature, attributes=2)
800 htmlfiles = Feature(db, "Documentation", "Documentation",
801 "Python HTMLHelp File", 7, parent = default_feature)
802 tools = Feature(db, "Tools", "Utility Scripts",
Tim Peters66cb0182004-08-26 05:23:19 +0000803 "Python utility scripts (Tools/", 9,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000804 parent = default_feature, attributes=2)
805 testsuite = Feature(db, "Testsuite", "Test suite",
806 "Python test suite (Lib/test/)", 11,
807 parent = default_feature, attributes=2|8)
Tim Peters66cb0182004-08-26 05:23:19 +0000808
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000809def extract_msvcr71():
810 import _winreg
811 # Find the location of the merge modules
812 k = _winreg.OpenKey(
813 _winreg.HKEY_LOCAL_MACHINE,
814 r"Software\Microsoft\VisualStudio\7.1\Setup\VS")
815 dir = _winreg.QueryValueEx(k, "MSMDir")[0]
816 _winreg.CloseKey(k)
817 files = glob.glob1(dir, "*CRT71*")
818 assert len(files) == 1
819 file = os.path.join(dir, files[0])
820 # Extract msvcr71.dll
821 m = msilib.MakeMerge2()
822 m.OpenModule(file, 0)
823 m.ExtractFiles(".")
824 m.CloseModule()
825 # Find the version/language of msvcr71.dll
826 installer = msilib.MakeInstaller()
827 return installer.FileVersion("msvcr71.dll", 0), \
828 installer.FileVersion("msvcr71.dll", 1)
829
830class PyDirectory(Directory):
831 """By default, all components in the Python installer
832 can run from source."""
833 def __init__(self, *args, **kw):
834 if not kw.has_key("componentflags"):
835 kw['componentflags'] = 2 #msidbComponentAttributesOptional
836 Directory.__init__(self, *args, **kw)
837
838# See "File Table", "Component Table", "Directory Table",
839# "FeatureComponents Table"
840def add_files(db):
841 cab = CAB("python")
842 tmpfiles = []
843 # Add all executables, icons, text files into the TARGETDIR component
844 root = PyDirectory(db, cab, None, srcdir, "TARGETDIR", "SourceDir")
845 default_feature.set_current()
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000846 if not msilib.Win64:
847 root.add_file("PCBuild/w9xpopen.exe")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000848 root.add_file("README.txt", src="README")
849 root.add_file("NEWS.txt", src="Misc/NEWS")
850 root.add_file("LICENSE.txt", src="LICENSE")
851 root.start_component("python.exe", keyfile="python.exe")
852 root.add_file("PCBuild/python.exe")
853 root.start_component("pythonw.exe", keyfile="pythonw.exe")
854 root.add_file("PCBuild/pythonw.exe")
855
856 # msidbComponentAttributesSharedDllRefCount = 8, see "Component Table"
857 dlldir = PyDirectory(db, cab, root, srcdir, "DLLDIR", ".")
858 pydll = "python%s%s.dll" % (major, minor)
859 pydllsrc = srcdir + "/PCBuild/" + pydll
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000860 dlldir.start_component("DLLDIR", flags = 8, keyfile = pydll, uuid = pythondll_uuid)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000861 installer = msilib.MakeInstaller()
862 pyversion = installer.FileVersion(pydllsrc, 0)
863 if not snapshot:
864 # For releases, the Python DLL has the same version as the
865 # installer package.
866 assert pyversion.split(".")[:3] == current_version.split(".")
867 dlldir.add_file("PCBuild/python%s%s.dll" % (major, minor),
868 version=pyversion,
869 language=installer.FileVersion(pydllsrc, 1))
870 # XXX determine dependencies
871 version, lang = extract_msvcr71()
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000872 dlldir.start_component("msvcr71", flags=8, keyfile="msvcr71.dll", uuid=msvcr71_uuid)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000873 dlldir.add_file("msvcr71.dll", src=os.path.abspath("msvcr71.dll"),
874 version=version, language=lang)
875 tmpfiles.append("msvcr71.dll")
Tim Peters66cb0182004-08-26 05:23:19 +0000876
Thomas Wouters89f507f2006-12-13 04:49:30 +0000877 # Check if _ctypes.pyd exists
878 have_ctypes = os.path.exists(srcdir+"/PCBuild/_ctypes.pyd")
879 if not have_ctypes:
Collin Winter6afaeb72007-08-03 17:06:41 +0000880 print("WARNING: _ctypes.pyd not found, ctypes will not be included")
Thomas Wouters89f507f2006-12-13 04:49:30 +0000881 extensions.remove("_ctypes.pyd")
882
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000883 # Add all .py files in Lib, except lib-tk, test
884 dirs={}
885 pydirs = [(root,"Lib")]
886 while pydirs:
887 parent, dir = pydirs.pop()
Martin v. Löwis9ca9f562006-01-03 06:29:53 +0000888 if dir == ".svn" or dir.startswith("plat-"):
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000889 continue
890 elif dir in ["lib-tk", "idlelib", "Icons"]:
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000891 if not have_tcl:
892 continue
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000893 tcltk.set_current()
Martin v. Löwis5c9e55e2004-12-30 14:08:18 +0000894 elif dir in ['test', 'tests', 'data', 'output']:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000895 # test: Lib, Lib/email, Lib/bsddb, Lib/ctypes, Lib/sqlite3
Martin v. Löwis5c9e55e2004-12-30 14:08:18 +0000896 # tests: Lib/distutils
897 # data: Lib/email/test
898 # output: Lib/test
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000899 testsuite.set_current()
Thomas Wouters89f507f2006-12-13 04:49:30 +0000900 elif not have_ctypes and dir == "ctypes":
901 continue
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000902 else:
903 default_feature.set_current()
904 lib = PyDirectory(db, cab, parent, dir, dir, "%s|%s" % (parent.make_short(dir), dir))
905 # Add additional files
906 dirs[dir]=lib
907 lib.glob("*.txt")
908 if dir=='site-packages':
Martin v. Löwis6d60c092004-11-21 10:16:26 +0000909 lib.add_file("README.txt", src="README")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000910 continue
911 files = lib.glob("*.py")
912 files += lib.glob("*.pyw")
913 if files:
914 # Add an entry to the RemoveFile table to remove bytecode files.
915 lib.remove_pyc()
Thomas Wouters3fc2ca32006-04-21 11:28:17 +0000916 if dir.endswith('.egg-info'):
917 lib.add_file('entry_points.txt')
918 lib.add_file('PKG-INFO')
919 lib.add_file('top_level.txt')
920 lib.add_file('zip-safe')
921 continue
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000922 if dir=='test' and parent.physical=='Lib':
923 lib.add_file("185test.db")
924 lib.add_file("audiotest.au")
925 lib.add_file("cfgparser.1")
Thomas Wouters89f507f2006-12-13 04:49:30 +0000926 lib.add_file("sgml_input.html")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000927 lib.add_file("test.xml")
928 lib.add_file("test.xml.out")
929 lib.add_file("testtar.tar")
Martin v. Löwis7d3755d2004-09-06 06:31:12 +0000930 lib.add_file("test_difflib_expect.html")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000931 lib.add_file("check_soundcard.vbs")
932 lib.add_file("empty.vbs")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000933 lib.glob("*.uue")
934 lib.add_file("readme.txt", src="README")
935 if dir=='decimaltestdata':
936 lib.glob("*.decTest")
937 if dir=='output':
938 lib.glob("test_*")
939 if dir=='idlelib':
940 lib.glob("*.def")
941 lib.add_file("idle.bat")
942 if dir=="Icons":
943 lib.glob("*.gif")
944 lib.add_file("idle.icns")
Thomas Wouters3fc2ca32006-04-21 11:28:17 +0000945 if dir=="command" and parent.physical=="distutils":
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000946 lib.add_file("wininst-6.exe")
947 lib.add_file("wininst-7.1.exe")
Thomas Wouters3fc2ca32006-04-21 11:28:17 +0000948 if dir=="setuptools":
949 lib.add_file("cli.exe")
950 lib.add_file("gui.exe")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000951 if dir=="data" and parent.physical=="test" and parent.basedir.physical=="email":
Martin v. Löwis9ca9f562006-01-03 06:29:53 +0000952 # This should contain all non-.svn files listed in subversion
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000953 for f in os.listdir(lib.absolute):
Martin v. Löwis9ca9f562006-01-03 06:29:53 +0000954 if f.endswith(".txt") or f==".svn":continue
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000955 if f.endswith(".au") or f.endswith(".gif"):
956 lib.add_file(f)
957 else:
Collin Winter6afaeb72007-08-03 17:06:41 +0000958 print("WARNING: New file %s in email/test/data" % f)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000959 for f in os.listdir(lib.absolute):
960 if os.path.isdir(os.path.join(lib.absolute, f)):
961 pydirs.append((lib, f))
962 # Add DLLs
963 default_feature.set_current()
964 lib = PyDirectory(db, cab, root, srcdir+"/PCBuild", "DLLs", "DLLS|DLLs")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000965 lib.add_file("py.ico", src="../PC/py.ico")
966 lib.add_file("pyc.ico", src="../PC/pyc.ico")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000967 dlls = []
968 tclfiles = []
969 for f in extensions:
970 if f=="_tkinter.pyd":
971 continue
972 if not os.path.exists(srcdir+"/PCBuild/"+f):
Collin Winter6afaeb72007-08-03 17:06:41 +0000973 print("WARNING: Missing extension", f)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000974 continue
975 dlls.append(f)
976 lib.add_file(f)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000977 # Add sqlite
978 if msilib.msi_type=="Intel64;1033":
979 sqlite_arch = "/ia64"
980 elif msilib.msi_type=="x64;1033":
981 sqlite_arch = "/amd64"
982 else:
983 sqlite_arch = ""
984 lib.add_file(srcdir+"/"+sqlite_dir+sqlite_arch+"/sqlite3.dll")
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000985 if have_tcl:
986 if not os.path.exists(srcdir+"/PCBuild/_tkinter.pyd"):
Collin Winter6afaeb72007-08-03 17:06:41 +0000987 print("WARNING: Missing _tkinter.pyd")
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000988 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
Collin Winter6afaeb72007-08-03 17:06:41 +0000999 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, ""), "",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001135 r'[DLLs]py.ico', "REGISTRY.def"),
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001136 ("pyw.icon", -1, pat2 % (testprefix, "NoCon"), "",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001137 r'[DLLs]py.ico', "REGISTRY.def"),
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001138 ("pyc.icon", -1, pat2 % (testprefix, "Compiled"), "",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001139 r'[DLLs]pyc.ico', "REGISTRY.def"),
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001140 # 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