blob: 0d54255ffd1a179b177670ba1e5d52f24b85a470 [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:
Martin v. Löwis47cc2a02007-08-30 18:27:06 +0000171 SystemFolderName = "[System64Folder]"
Martin v. Löwis283e35f2007-08-31 09:59:29 +0000172 registry_component = 4|256
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000173else:
174 SystemFolderName = "[SystemFolder]"
Martin v. Löwis283e35f2007-08-31 09:59:29 +0000175 registry_component = 4
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000176
177msilib.reset()
178
179# condition in which to install pythonxy.dll in system32:
180# a) it is Windows 9x or
181# b) it is NT, the user is privileged, and has chosen per-machine installation
182sys32cond = "(Windows9x or (Privileged and ALLUSERS))"
183
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000184def build_database():
185 """Generate an empty database, with just the schema and the
186 Summary information stream."""
187 if snapshot:
188 uc = upgrade_code_snapshot
189 else:
190 uc = upgrade_code
191 # schema represents the installer 2.0 database schema.
192 # sequence is the set of standard sequences
193 # (ui/execute, admin/advt/install)
Martin v. Löwis856bf9a2006-02-14 20:42:55 +0000194 db = msilib.init_database("python-%s%s.msi" % (full_current_version, msilib.arch_ext),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000195 schema, ProductName="Python "+full_current_version,
196 ProductCode=product_code,
197 ProductVersion=current_version,
Martin v. Löwis39afe1e2007-09-01 06:36:49 +0000198 Manufacturer=u"Python Software Foundation")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000199 # The default sequencing of the RemoveExistingProducts action causes
200 # removal of files that got just installed. Place it after
201 # InstallInitialize, so we first uninstall everything, but still roll
202 # back in case the installation is interrupted
203 msilib.change_sequence(sequence.InstallExecuteSequence,
204 "RemoveExistingProducts", 1510)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000205 msilib.add_tables(db, sequence)
206 # We cannot set ALLUSERS in the property table, as this cannot be
207 # reset if the user choses a per-user installation. Instead, we
208 # maintain WhichUsers, which can be "ALL" or "JUSTME". The UI manages
209 # this property, and when the execution starts, ALLUSERS is set
210 # accordingly.
211 add_data(db, "Property", [("UpgradeCode", uc),
212 ("WhichUsers", "ALL"),
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000213 ("ProductLine", "Python%s%s" % (major, minor)),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000214 ])
215 db.Commit()
216 return db
217
218def remove_old_versions(db):
219 "Fill the upgrade table."
220 start = "%s.%s.0" % (major, minor)
221 # This requests that feature selection states of an older
222 # installation should be forwarded into this one. Upgrading
223 # requires that both the old and the new installation are
224 # either both per-machine or per-user.
225 migrate_features = 1
226 # See "Upgrade Table". We remove releases with the same major and
227 # minor version. For an snapshot, we remove all earlier snapshots. For
228 # a release, we remove all snapshots, and all earlier releases.
229 if snapshot:
230 add_data(db, "Upgrade",
Tim Peters66cb0182004-08-26 05:23:19 +0000231 [(upgrade_code_snapshot, start,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000232 current_version,
233 None, # Ignore language
Tim Peters66cb0182004-08-26 05:23:19 +0000234 migrate_features,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000235 None, # Migrate ALL features
236 "REMOVEOLDSNAPSHOT")])
237 props = "REMOVEOLDSNAPSHOT"
238 else:
239 add_data(db, "Upgrade",
240 [(upgrade_code, start, current_version,
241 None, migrate_features, None, "REMOVEOLDVERSION"),
242 (upgrade_code_snapshot, start, "%s.%d.0" % (major, int(minor)+1),
243 None, migrate_features, None, "REMOVEOLDSNAPSHOT")])
244 props = "REMOVEOLDSNAPSHOT;REMOVEOLDVERSION"
245 # Installer collects the product codes of the earlier releases in
246 # these properties. In order to allow modification of the properties,
247 # they must be declared as secure. See "SecureCustomProperties Property"
248 add_data(db, "Property", [("SecureCustomProperties", props)])
249
250class PyDialog(Dialog):
251 """Dialog class with a fixed layout: controls at the top, then a ruler,
252 then a list of buttons: back, next, cancel. Optionally a bitmap at the
253 left."""
254 def __init__(self, *args, **kw):
255 """Dialog(database, name, x, y, w, h, attributes, title, first,
256 default, cancel, bitmap=true)"""
257 Dialog.__init__(self, *args)
258 ruler = self.h - 36
259 bmwidth = 152*ruler/328
260 if kw.get("bitmap", True):
261 self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin")
262 self.line("BottomLine", 0, ruler, self.w, 0)
263
264 def title(self, title):
265 "Set the title text of the dialog at the top."
266 # name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix,
267 # text, in VerdanaBold10
268 self.text("Title", 135, 10, 220, 60, 0x30003,
269 r"{\VerdanaBold10}%s" % title)
270
271 def back(self, title, next, name = "Back", active = 1):
272 """Add a back button with a given title, the tab-next button,
273 its name in the Control table, possibly initially disabled.
274
275 Return the button, so that events can be associated"""
276 if active:
277 flags = 3 # Visible|Enabled
278 else:
279 flags = 1 # Visible
280 return self.pushbutton(name, 180, self.h-27 , 56, 17, flags, title, next)
281
282 def cancel(self, title, next, name = "Cancel", active = 1):
283 """Add a cancel button with a given title, the tab-next button,
284 its name in the Control table, possibly initially disabled.
285
286 Return the button, so that events can be associated"""
287 if active:
288 flags = 3 # Visible|Enabled
289 else:
290 flags = 1 # Visible
291 return self.pushbutton(name, 304, self.h-27, 56, 17, flags, title, next)
292
293 def next(self, title, next, name = "Next", active = 1):
294 """Add a Next 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, 236, self.h-27, 56, 17, flags, title, next)
303
304 def xbutton(self, name, title, next, xpos):
305 """Add a button with a given title, the tab-next button,
306 its name in the Control table, giving its x position; the
307 y-position is aligned with the other buttons.
308
309 Return the button, so that events can be associated"""
310 return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next)
311
312def add_ui(db):
313 x = y = 50
314 w = 370
315 h = 300
316 title = "[ProductName] Setup"
317
318 # see "Dialog Style Bits"
319 modal = 3 # visible | modal
320 modeless = 1 # visible
321 track_disk_space = 32
322
323 add_data(db, 'ActionText', uisample.ActionText)
324 add_data(db, 'UIText', uisample.UIText)
325
326 # Bitmaps
327 if not os.path.exists(srcdir+r"\PC\python_icon.exe"):
328 raise "Run icons.mak in PC directory"
329 add_data(db, "Binary",
330 [("PythonWin", msilib.Binary(srcdir+r"\PCbuild\installer.bmp")), # 152x328 pixels
331 ("py.ico",msilib.Binary(srcdir+r"\PC\py.ico")),
332 ])
333 add_data(db, "Icon",
334 [("python_icon.exe", msilib.Binary(srcdir+r"\PC\python_icon.exe"))])
335
336 # Scripts
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000337 # CheckDir sets TargetExists if TARGETDIR exists.
338 # UpdateEditIDLE sets the REGISTRY.tcl component into
339 # the installed/uninstalled state according to both the
340 # Extensions and TclTk features.
Martin v. Löwiseb68be42004-12-12 15:29:21 +0000341 if os.system("nmake /nologo /c /f msisupport.mak") != 0:
342 raise "'nmake /f msisupport.mak' failed"
343 add_data(db, "Binary", [("Script", msilib.Binary("msisupport.dll"))])
344 # See "Custom Action Type 1"
Martin v. Löwis3390d332005-03-14 17:20:13 +0000345 if msilib.Win64:
346 CheckDir = "CheckDir"
Martin v. Löwisdf40ce32006-02-16 14:38:30 +0000347 UpdateEditIDLE = "UpdateEditIDLE"
Martin v. Löwis3390d332005-03-14 17:20:13 +0000348 else:
349 CheckDir = "_CheckDir@4"
350 UpdateEditIDLE = "_UpdateEditIDLE@4"
Tim Peters0e9980f2004-09-12 03:49:31 +0000351 add_data(db, "CustomAction",
Martin v. Löwis3390d332005-03-14 17:20:13 +0000352 [("CheckDir", 1, "Script", CheckDir)])
Martin v. Löwiseac02e62004-11-18 08:00:33 +0000353 if have_tcl:
354 add_data(db, "CustomAction",
Martin v. Löwis3390d332005-03-14 17:20:13 +0000355 [("UpdateEditIDLE", 1, "Script", UpdateEditIDLE)])
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000356
357 # UI customization properties
358 add_data(db, "Property",
359 # See "DefaultUIFont Property"
360 [("DefaultUIFont", "DlgFont8"),
361 # See "ErrorDialog Style Bit"
362 ("ErrorDialog", "ErrorDlg"),
363 ("Progress1", "Install"), # modified in maintenance type dlg
364 ("Progress2", "installs"),
365 ("MaintenanceForm_Action", "Repair")])
366
367 # Fonts, see "TextStyle Table"
368 add_data(db, "TextStyle",
369 [("DlgFont8", "Tahoma", 9, None, 0),
370 ("DlgFontBold8", "Tahoma", 8, None, 1), #bold
371 ("VerdanaBold10", "Verdana", 10, None, 1),
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000372 ("VerdanaRed9", "Verdana", 9, 255, 0),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000373 ])
374
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000375 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 +0000376 # See "CustomAction Table"
377 add_data(db, "CustomAction", [
378 # msidbCustomActionTypeFirstSequence + msidbCustomActionTypeTextData + msidbCustomActionTypeProperty
379 # See "Custom Action Type 51",
380 # "Custom Action Execution Scheduling Options"
381 ("InitialTargetDir", 307, "TARGETDIR",
382 "[WindowsVolume]Python%s%s" % (major, minor)),
383 ("SetDLLDirToTarget", 307, "DLLDIR", "[TARGETDIR]"),
384 ("SetDLLDirToSystem32", 307, "DLLDIR", SystemFolderName),
385 # msidbCustomActionTypeExe + msidbCustomActionTypeSourceFile
386 # See "Custom Action Type 18"
Martin v. Löwis7b2563b2004-11-02 22:59:56 +0000387 ("CompilePyc", 18, "python.exe", compileargs),
388 ("CompilePyo", 18, "python.exe", "-O "+compileargs),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000389 ])
390
391 # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table"
392 # Numbers indicate sequence; see sequence.py for how these action integrate
393 add_data(db, "InstallUISequence",
394 [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140),
395 ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141),
396 ("InitialTargetDir", 'TARGETDIR=""', 750),
397 # In the user interface, assume all-users installation if privileged.
398 ("SetDLLDirToSystem32", 'DLLDIR="" and ' + sys32cond, 751),
399 ("SetDLLDirToTarget", 'DLLDIR="" and not ' + sys32cond, 752),
400 ("SelectDirectoryDlg", "Not Installed", 1230),
401 # XXX no support for resume installations yet
402 #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240),
403 ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250),
404 ("ProgressDlg", None, 1280)])
405 add_data(db, "AdminUISequence",
406 [("InitialTargetDir", 'TARGETDIR=""', 750),
407 ("SetDLLDirToTarget", 'DLLDIR=""', 751),
408 ])
409
410 # Execute Sequences
411 add_data(db, "InstallExecuteSequence",
412 [("InitialTargetDir", 'TARGETDIR=""', 750),
413 ("SetDLLDirToSystem32", 'DLLDIR="" and ' + sys32cond, 751),
414 ("SetDLLDirToTarget", 'DLLDIR="" and not ' + sys32cond, 752),
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000415 ("UpdateEditIDLE", None, 1050),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000416 ("CompilePyc", "COMPILEALL", 6800),
417 ("CompilePyo", "COMPILEALL", 6801),
418 ])
419 add_data(db, "AdminExecuteSequence",
420 [("InitialTargetDir", 'TARGETDIR=""', 750),
421 ("SetDLLDirToTarget", 'DLLDIR=""', 751),
422 ("CompilePyc", "COMPILEALL", 6800),
423 ("CompilePyo", "COMPILEALL", 6801),
424 ])
425
426 #####################################################################
427 # Standard dialogs: FatalError, UserExit, ExitDialog
428 fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title,
429 "Finish", "Finish", "Finish")
430 fatal.title("[ProductName] Installer ended prematurely")
431 fatal.back("< Back", "Finish", active = 0)
432 fatal.cancel("Cancel", "Back", active = 0)
433 fatal.text("Description1", 135, 70, 220, 80, 0x30003,
434 "[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.")
435 fatal.text("Description2", 135, 155, 220, 20, 0x30003,
436 "Click the Finish button to exit the Installer.")
437 c=fatal.next("Finish", "Cancel", name="Finish")
438 # See "ControlEvent Table". Parameters are the event, the parameter
439 # to the action, and optionally the condition for the event, and the order
440 # of events.
441 c.event("EndDialog", "Exit")
Tim Peters66cb0182004-08-26 05:23:19 +0000442
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000443 user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title,
444 "Finish", "Finish", "Finish")
445 user_exit.title("[ProductName] Installer was interrupted")
446 user_exit.back("< Back", "Finish", active = 0)
447 user_exit.cancel("Cancel", "Back", active = 0)
448 user_exit.text("Description1", 135, 70, 220, 80, 0x30003,
449 "[ProductName] setup was interrupted. Your system has not been modified. "
450 "To install this program at a later time, please run the installation again.")
451 user_exit.text("Description2", 135, 155, 220, 20, 0x30003,
452 "Click the Finish button to exit the Installer.")
453 c = user_exit.next("Finish", "Cancel", name="Finish")
454 c.event("EndDialog", "Exit")
Tim Peters66cb0182004-08-26 05:23:19 +0000455
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000456 exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title,
457 "Finish", "Finish", "Finish")
458 exit_dialog.title("Completing the [ProductName] Installer")
459 exit_dialog.back("< Back", "Finish", active = 0)
460 exit_dialog.cancel("Cancel", "Back", active = 0)
Martin v. Löwis2dd2a282004-08-22 17:10:12 +0000461 exit_dialog.text("Acknowledgements", 135, 95, 220, 120, 0x30003,
462 "Special Windows thanks to:\n"
Martin v. Löwisd3f61a22004-08-30 09:22:30 +0000463 " Mark Hammond, without whose years of freely \n"
464 " shared Windows expertise, Python for Windows \n"
465 " would still be Python for DOS.")
Tim Peters66cb0182004-08-26 05:23:19 +0000466
Martin v. Löwis8c7c56e2006-03-05 14:04:26 +0000467 c = exit_dialog.text("warning", 135, 200, 220, 40, 0x30003,
468 "{\\VerdanaRed9}Warning: Python 2.5.x is the last "
469 "Python release for Windows 9x.")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000470 c.condition("Hide", "NOT Version9X")
Martin v. Löwis8c7c56e2006-03-05 14:04:26 +0000471
Martin v. Löwis2dd2a282004-08-22 17:10:12 +0000472 exit_dialog.text("Description", 135, 235, 220, 20, 0x30003,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000473 "Click the Finish button to exit the Installer.")
474 c = exit_dialog.next("Finish", "Cancel", name="Finish")
475 c.event("EndDialog", "Return")
476
477 #####################################################################
478 # Required dialog: FilesInUse, ErrorDlg
479 inuse = PyDialog(db, "FilesInUse",
480 x, y, w, h,
481 19, # KeepModeless|Modal|Visible
482 title,
483 "Retry", "Retry", "Retry", bitmap=False)
484 inuse.text("Title", 15, 6, 200, 15, 0x30003,
485 r"{\DlgFontBold8}Files in Use")
486 inuse.text("Description", 20, 23, 280, 20, 0x30003,
487 "Some files that need to be updated are currently in use.")
488 inuse.text("Text", 20, 55, 330, 50, 3,
489 "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.")
490 inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess",
491 None, None, None)
492 c=inuse.back("Exit", "Ignore", name="Exit")
493 c.event("EndDialog", "Exit")
494 c=inuse.next("Ignore", "Retry", name="Ignore")
495 c.event("EndDialog", "Ignore")
496 c=inuse.cancel("Retry", "Exit", name="Retry")
497 c.event("EndDialog","Retry")
498
499
500 # See "Error Dialog". See "ICE20" for the required names of the controls.
501 error = Dialog(db, "ErrorDlg",
502 50, 10, 330, 101,
503 65543, # Error|Minimize|Modal|Visible
504 title,
505 "ErrorText", None, None)
506 error.text("ErrorText", 50,9,280,48,3, "")
507 error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None)
508 error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo")
509 error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes")
510 error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort")
511 error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel")
512 error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore")
513 error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk")
514 error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry")
515
516 #####################################################################
517 # Global "Query Cancel" dialog
518 cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title,
519 "No", "No", "No")
Tim Peters66cb0182004-08-26 05:23:19 +0000520 cancel.text("Text", 48, 15, 194, 30, 3,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000521 "Are you sure you want to cancel [ProductName] installation?")
522 cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
523 "py.ico", None, None)
524 c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No")
525 c.event("EndDialog", "Exit")
Tim Peters66cb0182004-08-26 05:23:19 +0000526
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000527 c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes")
528 c.event("EndDialog", "Return")
529
530 #####################################################################
531 # Global "Wait for costing" dialog
532 costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title,
533 "Return", "Return", "Return")
534 costing.text("Text", 48, 15, 194, 30, 3,
535 "Please wait while the installer finishes determining your disk space requirements.")
536 costing.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
537 "py.ico", None, None)
538 c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None)
539 c.event("EndDialog", "Exit")
540
541 #####################################################################
542 # Preparation dialog: no user input except cancellation
543 prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title,
544 "Cancel", "Cancel", "Cancel")
545 prep.text("Description", 135, 70, 220, 40, 0x30003,
546 "Please wait while the Installer prepares to guide you through the installation.")
547 prep.title("Welcome to the [ProductName] Installer")
548 c=prep.text("ActionText", 135, 110, 220, 20, 0x30003, "Pondering...")
549 c.mapping("ActionText", "Text")
550 c=prep.text("ActionData", 135, 135, 220, 30, 0x30003, None)
551 c.mapping("ActionData", "Text")
552 prep.back("Back", None, active=0)
553 prep.next("Next", None, active=0)
554 c=prep.cancel("Cancel", None)
555 c.event("SpawnDialog", "CancelDlg")
556
557 #####################################################################
558 # Target directory selection
559 seldlg = PyDialog(db, "SelectDirectoryDlg", x, y, w, h, modal, title,
560 "Next", "Next", "Cancel")
561 seldlg.title("Select Destination Directory")
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000562 c = seldlg.text("Existing", 135, 25, 235, 30, 0x30003,
563 "{\VerdanaRed9}This update will replace your existing [ProductLine] installation.")
564 c.condition("Hide", 'REMOVEOLDVERSION="" and REMOVEOLDSNAPSHOT=""')
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000565 seldlg.text("Description", 135, 50, 220, 40, 0x30003,
566 "Please select a directory for the [ProductName] files.")
567
568 seldlg.back("< Back", None, active=0)
569 c = seldlg.next("Next >", "Cancel")
570 c.event("DoAction", "CheckDir", "TargetExistsOk<>1", order=1)
571 # If the target exists, but we found that we are going to remove old versions, don't bother
572 # confirming that the target directory exists. Strictly speaking, we should determine that
573 # the target directory is indeed the target of the product that we are going to remove, but
574 # I don't know how to do that.
575 c.event("SpawnDialog", "ExistingDirectoryDlg", 'TargetExists=1 and REMOVEOLDVERSION="" and REMOVEOLDSNAPSHOT=""', 2)
576 c.event("SetTargetPath", "TARGETDIR", 'TargetExists=0 or REMOVEOLDVERSION<>"" or REMOVEOLDSNAPSHOT<>""', 3)
577 c.event("SpawnWaitDialog", "WaitForCostingDlg", "CostingComplete=1", 4)
578 c.event("NewDialog", "SelectFeaturesDlg", 'TargetExists=0 or REMOVEOLDVERSION<>"" or REMOVEOLDSNAPSHOT<>""', 5)
579
580 c = seldlg.cancel("Cancel", "DirectoryCombo")
581 c.event("SpawnDialog", "CancelDlg")
582
583 seldlg.control("DirectoryCombo", "DirectoryCombo", 135, 70, 172, 80, 393219,
584 "TARGETDIR", None, "DirectoryList", None)
585 seldlg.control("DirectoryList", "DirectoryList", 135, 90, 208, 136, 3, "TARGETDIR",
586 None, "PathEdit", None)
587 seldlg.control("PathEdit", "PathEdit", 135, 230, 206, 16, 3, "TARGETDIR", None, "Next", None)
588 c = seldlg.pushbutton("Up", 306, 70, 18, 18, 3, "Up", None)
589 c.event("DirectoryListUp", "0")
590 c = seldlg.pushbutton("NewDir", 324, 70, 30, 18, 3, "New", None)
591 c.event("DirectoryListNew", "0")
592
593 #####################################################################
594 # SelectFeaturesDlg
595 features = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal|track_disk_space,
596 title, "Tree", "Next", "Cancel")
597 features.title("Customize [ProductName]")
598 features.text("Description", 135, 35, 220, 15, 0x30003,
599 "Select the way you want features to be installed.")
600 features.text("Text", 135,45,220,30, 3,
601 "Click on the icons in the tree below to change the way features will be installed.")
602
603 c=features.back("< Back", "Next")
604 c.event("NewDialog", "SelectDirectoryDlg")
605
606 c=features.next("Next >", "Cancel")
607 c.mapping("SelectionNoItems", "Enabled")
608 c.event("SpawnDialog", "DiskCostDlg", "OutOfDiskSpace=1", order=1)
609 c.event("EndDialog", "Return", "OutOfDiskSpace<>1", order=2)
610
611 c=features.cancel("Cancel", "Tree")
612 c.event("SpawnDialog", "CancelDlg")
613
Tim Peters66cb0182004-08-26 05:23:19 +0000614 # 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 +0000615 features.control("Tree", "SelectionTree", 135, 75, 220, 95, 7, "_BrowseProperty",
616 "Tree of selections", "Back", None)
617
618 #c=features.pushbutton("Reset", 42, 243, 56, 17, 3, "Reset", "DiskCost")
619 #c.mapping("SelectionNoItems", "Enabled")
620 #c.event("Reset", "0")
Tim Peters66cb0182004-08-26 05:23:19 +0000621
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000622 features.control("Box", "GroupBox", 135, 170, 225, 90, 1, None, None, None, None)
623
624 c=features.xbutton("DiskCost", "Disk &Usage", None, 0.10)
625 c.mapping("SelectionNoItems","Enabled")
626 c.event("SpawnDialog", "DiskCostDlg")
627
628 c=features.xbutton("Advanced", "Advanced", None, 0.30)
629 c.event("SpawnDialog", "AdvancedDlg")
630
631 c=features.text("ItemDescription", 140, 180, 210, 30, 3,
632 "Multiline description of the currently selected item.")
633 c.mapping("SelectionDescription","Text")
Tim Peters66cb0182004-08-26 05:23:19 +0000634
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000635 c=features.text("ItemSize", 140, 210, 210, 45, 3,
636 "The size of the currently selected item.")
637 c.mapping("SelectionSize", "Text")
638
639 #####################################################################
640 # Disk cost
641 cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title,
642 "OK", "OK", "OK", bitmap=False)
643 cost.text("Title", 15, 6, 200, 15, 0x30003,
644 "{\DlgFontBold8}Disk Space Requirements")
645 cost.text("Description", 20, 20, 280, 20, 0x30003,
646 "The disk space required for the installation of the selected features.")
647 cost.text("Text", 20, 53, 330, 60, 3,
648 "The highlighted volumes (if any) do not have enough disk space "
649 "available for the currently selected features. You can either "
650 "remove some files from the highlighted volumes, or choose to "
651 "install less features onto local drive(s), or select different "
652 "destination drive(s).")
653 cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223,
654 None, "{120}{70}{70}{70}{70}", None, None)
655 cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return")
656
657 #####################################################################
658 # WhichUsers Dialog. Only available on NT, and for privileged users.
659 # This must be run before FindRelatedProducts, because that will
660 # take into account whether the previous installation was per-user
661 # or per-machine. We currently don't support going back to this
662 # dialog after "Next" was selected; to support this, we would need to
663 # find how to reset the ALLUSERS property, and how to re-run
664 # FindRelatedProducts.
665 # On Windows9x, the ALLUSERS property is ignored on the command line
666 # and in the Property table, but installer fails according to the documentation
667 # if a dialog attempts to set ALLUSERS.
668 whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title,
669 "AdminInstall", "Next", "Cancel")
670 whichusers.title("Select whether to install [ProductName] for all users of this computer.")
671 # A radio group with two options: allusers, justme
672 g = whichusers.radiogroup("AdminInstall", 135, 60, 160, 50, 3,
673 "WhichUsers", "", "Next")
674 g.add("ALL", 0, 5, 150, 20, "Install for all users")
675 g.add("JUSTME", 0, 25, 150, 20, "Install just for me")
676
Tim Peters66cb0182004-08-26 05:23:19 +0000677 whichusers.back("Back", None, active=0)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000678
679 c = whichusers.next("Next >", "Cancel")
680 c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1)
681 c.event("EndDialog", "Return", order = 2)
682
683 c = whichusers.cancel("Cancel", "AdminInstall")
684 c.event("SpawnDialog", "CancelDlg")
Tim Peters66cb0182004-08-26 05:23:19 +0000685
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000686 #####################################################################
687 # Advanced Dialog.
688 advanced = PyDialog(db, "AdvancedDlg", x, y, w, h, modal, title,
689 "CompilePyc", "Next", "Cancel")
690 advanced.title("Advanced Options for [ProductName]")
691 # A radio group with two options: allusers, justme
692 advanced.checkbox("CompilePyc", 135, 60, 230, 50, 3,
693 "COMPILEALL", "Compile .py files to byte code after installation", "Next")
694
695 c = advanced.next("Finish", "Cancel")
696 c.event("EndDialog", "Return")
697
698 c = advanced.cancel("Cancel", "CompilePyc")
699 c.event("SpawnDialog", "CancelDlg")
Tim Peters66cb0182004-08-26 05:23:19 +0000700
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000701 #####################################################################
Tim Peters66cb0182004-08-26 05:23:19 +0000702 # Existing Directory dialog
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000703 dlg = Dialog(db, "ExistingDirectoryDlg", 50, 30, 200, 80, modal, title,
704 "No", "No", "No")
705 dlg.text("Title", 10, 20, 180, 40, 3,
706 "[TARGETDIR] exists. Are you sure you want to overwrite existing files?")
707 c=dlg.pushbutton("Yes", 30, 60, 55, 17, 3, "Yes", "No")
708 c.event("[TargetExists]", "0", order=1)
709 c.event("[TargetExistsOk]", "1", order=2)
710 c.event("EndDialog", "Return", order=3)
711 c=dlg.pushbutton("No", 115, 60, 55, 17, 3, "No", "Yes")
712 c.event("EndDialog", "Return")
713
714 #####################################################################
715 # Installation Progress dialog (modeless)
716 progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title,
717 "Cancel", "Cancel", "Cancel", bitmap=False)
718 progress.text("Title", 20, 15, 200, 15, 0x30003,
719 "{\DlgFontBold8}[Progress1] [ProductName]")
720 progress.text("Text", 35, 65, 300, 30, 3,
721 "Please wait while the Installer [Progress2] [ProductName]. "
722 "This may take several minutes.")
723 progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:")
724
725 c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...")
726 c.mapping("ActionText", "Text")
727
728 #c=progress.text("ActionData", 35, 140, 300, 20, 3, None)
729 #c.mapping("ActionData", "Text")
730
731 c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537,
732 None, "Progress done", None, None)
733 c.mapping("SetProgress", "Progress")
734
735 progress.back("< Back", "Next", active=False)
736 progress.next("Next >", "Cancel", active=False)
737 progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg")
738
739 # Maintenance type: repair/uninstall
740 maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title,
741 "Next", "Next", "Cancel")
742 maint.title("Welcome to the [ProductName] Setup Wizard")
743 maint.text("BodyText", 135, 63, 230, 42, 3,
744 "Select whether you want to repair or remove [ProductName].")
745 g=maint.radiogroup("RepairRadioGroup", 135, 108, 230, 60, 3,
746 "MaintenanceForm_Action", "", "Next")
747 g.add("Change", 0, 0, 200, 17, "&Change [ProductName]")
748 g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]")
749 g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]")
Tim Peters66cb0182004-08-26 05:23:19 +0000750
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000751 maint.back("< Back", None, active=False)
752 c=maint.next("Finish", "Cancel")
753 # Change installation: Change progress dialog to "Change", then ask
754 # for feature selection
755 c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1)
756 c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2)
757
758 # Reinstall: Change progress dialog to "Repair", then invoke reinstall
759 # Also set list of reinstalled features to "ALL"
760 c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5)
761 c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6)
Raymond Hettinger72f08012004-11-07 07:08:25 +0000762 c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000763 c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8)
764
765 # Uninstall: Change progress to "Remove", then invoke uninstall
766 # Also set list of removed features to "ALL"
767 c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11)
768 c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12)
769 c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13)
770 c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14)
771
Tim Peters66cb0182004-08-26 05:23:19 +0000772 # Close dialog when maintenance action scheduled
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000773 c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20)
774 c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21)
Tim Peters66cb0182004-08-26 05:23:19 +0000775
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000776 maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg")
Tim Peters66cb0182004-08-26 05:23:19 +0000777
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000778
779# See "Feature Table". The feature level is 1 for all features,
780# and the feature attributes are 0 for the DefaultFeature, and
781# FollowParent for all other features. The numbers are the Display
782# column.
783def add_features(db):
784 # feature attributes:
785 # msidbFeatureAttributesFollowParent == 2
786 # msidbFeatureAttributesDisallowAdvertise == 8
787 # Features that need to be installed with together with the main feature
788 # (i.e. additional Python libraries) need to follow the parent feature.
789 # Features that have no advertisement trigger (e.g. the test suite)
790 # must not support advertisement
791 global default_feature, tcltk, htmlfiles, tools, testsuite, ext_feature
792 default_feature = Feature(db, "DefaultFeature", "Python",
793 "Python Interpreter and Libraries",
794 1, directory = "TARGETDIR")
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000795 # We don't support advertisement of extensions
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000796 ext_feature = Feature(db, "Extensions", "Register Extensions",
797 "Make this Python installation the default Python installation", 3,
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000798 parent = default_feature, attributes=2|8)
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000799 if have_tcl:
800 tcltk = Feature(db, "TclTk", "Tcl/Tk", "Tkinter, IDLE, pydoc", 5,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000801 parent = default_feature, attributes=2)
802 htmlfiles = Feature(db, "Documentation", "Documentation",
803 "Python HTMLHelp File", 7, parent = default_feature)
804 tools = Feature(db, "Tools", "Utility Scripts",
Tim Peters66cb0182004-08-26 05:23:19 +0000805 "Python utility scripts (Tools/", 9,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000806 parent = default_feature, attributes=2)
807 testsuite = Feature(db, "Testsuite", "Test suite",
808 "Python test suite (Lib/test/)", 11,
809 parent = default_feature, attributes=2|8)
Tim Peters66cb0182004-08-26 05:23:19 +0000810
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000811def extract_msvcr71():
812 import _winreg
813 # Find the location of the merge modules
814 k = _winreg.OpenKey(
815 _winreg.HKEY_LOCAL_MACHINE,
816 r"Software\Microsoft\VisualStudio\7.1\Setup\VS")
817 dir = _winreg.QueryValueEx(k, "MSMDir")[0]
818 _winreg.CloseKey(k)
819 files = glob.glob1(dir, "*CRT71*")
820 assert len(files) == 1
821 file = os.path.join(dir, files[0])
822 # Extract msvcr71.dll
823 m = msilib.MakeMerge2()
824 m.OpenModule(file, 0)
825 m.ExtractFiles(".")
826 m.CloseModule()
827 # Find the version/language of msvcr71.dll
828 installer = msilib.MakeInstaller()
829 return installer.FileVersion("msvcr71.dll", 0), \
830 installer.FileVersion("msvcr71.dll", 1)
831
832class PyDirectory(Directory):
833 """By default, all components in the Python installer
834 can run from source."""
835 def __init__(self, *args, **kw):
836 if not kw.has_key("componentflags"):
837 kw['componentflags'] = 2 #msidbComponentAttributesOptional
838 Directory.__init__(self, *args, **kw)
839
840# See "File Table", "Component Table", "Directory Table",
841# "FeatureComponents Table"
842def add_files(db):
843 cab = CAB("python")
844 tmpfiles = []
845 # Add all executables, icons, text files into the TARGETDIR component
846 root = PyDirectory(db, cab, None, srcdir, "TARGETDIR", "SourceDir")
847 default_feature.set_current()
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000848 if not msilib.Win64:
849 root.add_file("PCBuild/w9xpopen.exe")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000850 root.add_file("README.txt", src="README")
851 root.add_file("NEWS.txt", src="Misc/NEWS")
852 root.add_file("LICENSE.txt", src="LICENSE")
853 root.start_component("python.exe", keyfile="python.exe")
854 root.add_file("PCBuild/python.exe")
855 root.start_component("pythonw.exe", keyfile="pythonw.exe")
856 root.add_file("PCBuild/pythonw.exe")
857
858 # msidbComponentAttributesSharedDllRefCount = 8, see "Component Table"
859 dlldir = PyDirectory(db, cab, root, srcdir, "DLLDIR", ".")
860 pydll = "python%s%s.dll" % (major, minor)
861 pydllsrc = srcdir + "/PCBuild/" + pydll
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000862 dlldir.start_component("DLLDIR", flags = 8, keyfile = pydll, uuid = pythondll_uuid)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000863 installer = msilib.MakeInstaller()
864 pyversion = installer.FileVersion(pydllsrc, 0)
865 if not snapshot:
866 # For releases, the Python DLL has the same version as the
867 # installer package.
868 assert pyversion.split(".")[:3] == current_version.split(".")
869 dlldir.add_file("PCBuild/python%s%s.dll" % (major, minor),
870 version=pyversion,
871 language=installer.FileVersion(pydllsrc, 1))
872 # XXX determine dependencies
873 version, lang = extract_msvcr71()
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000874 dlldir.start_component("msvcr71", flags=8, keyfile="msvcr71.dll", uuid=msvcr71_uuid)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000875 dlldir.add_file("msvcr71.dll", src=os.path.abspath("msvcr71.dll"),
876 version=version, language=lang)
877 tmpfiles.append("msvcr71.dll")
Tim Peters66cb0182004-08-26 05:23:19 +0000878
Thomas Wouters89f507f2006-12-13 04:49:30 +0000879 # Check if _ctypes.pyd exists
880 have_ctypes = os.path.exists(srcdir+"/PCBuild/_ctypes.pyd")
881 if not have_ctypes:
Collin Winter6afaeb72007-08-03 17:06:41 +0000882 print("WARNING: _ctypes.pyd not found, ctypes will not be included")
Thomas Wouters89f507f2006-12-13 04:49:30 +0000883 extensions.remove("_ctypes.pyd")
884
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000885 # Add all .py files in Lib, except lib-tk, test
886 dirs={}
887 pydirs = [(root,"Lib")]
888 while pydirs:
889 parent, dir = pydirs.pop()
Martin v. Löwis9ca9f562006-01-03 06:29:53 +0000890 if dir == ".svn" or dir.startswith("plat-"):
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000891 continue
892 elif dir in ["lib-tk", "idlelib", "Icons"]:
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000893 if not have_tcl:
894 continue
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000895 tcltk.set_current()
Martin v. Löwis5c9e55e2004-12-30 14:08:18 +0000896 elif dir in ['test', 'tests', 'data', 'output']:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000897 # test: Lib, Lib/email, Lib/bsddb, Lib/ctypes, Lib/sqlite3
Martin v. Löwis5c9e55e2004-12-30 14:08:18 +0000898 # tests: Lib/distutils
899 # data: Lib/email/test
900 # output: Lib/test
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000901 testsuite.set_current()
Thomas Wouters89f507f2006-12-13 04:49:30 +0000902 elif not have_ctypes and dir == "ctypes":
903 continue
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000904 else:
905 default_feature.set_current()
906 lib = PyDirectory(db, cab, parent, dir, dir, "%s|%s" % (parent.make_short(dir), dir))
907 # Add additional files
908 dirs[dir]=lib
909 lib.glob("*.txt")
910 if dir=='site-packages':
Martin v. Löwis6d60c092004-11-21 10:16:26 +0000911 lib.add_file("README.txt", src="README")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000912 continue
913 files = lib.glob("*.py")
914 files += lib.glob("*.pyw")
915 if files:
916 # Add an entry to the RemoveFile table to remove bytecode files.
917 lib.remove_pyc()
Thomas Wouters3fc2ca32006-04-21 11:28:17 +0000918 if dir.endswith('.egg-info'):
919 lib.add_file('entry_points.txt')
920 lib.add_file('PKG-INFO')
921 lib.add_file('top_level.txt')
922 lib.add_file('zip-safe')
923 continue
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000924 if dir=='test' and parent.physical=='Lib':
925 lib.add_file("185test.db")
926 lib.add_file("audiotest.au")
927 lib.add_file("cfgparser.1")
Thomas Wouters89f507f2006-12-13 04:49:30 +0000928 lib.add_file("sgml_input.html")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000929 lib.add_file("test.xml")
930 lib.add_file("test.xml.out")
931 lib.add_file("testtar.tar")
Martin v. Löwis7d3755d2004-09-06 06:31:12 +0000932 lib.add_file("test_difflib_expect.html")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000933 lib.add_file("check_soundcard.vbs")
934 lib.add_file("empty.vbs")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000935 lib.glob("*.uue")
936 lib.add_file("readme.txt", src="README")
937 if dir=='decimaltestdata':
938 lib.glob("*.decTest")
939 if dir=='output':
940 lib.glob("test_*")
941 if dir=='idlelib':
942 lib.glob("*.def")
943 lib.add_file("idle.bat")
944 if dir=="Icons":
945 lib.glob("*.gif")
946 lib.add_file("idle.icns")
Thomas Wouters3fc2ca32006-04-21 11:28:17 +0000947 if dir=="command" and parent.physical=="distutils":
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000948 lib.add_file("wininst-6.exe")
949 lib.add_file("wininst-7.1.exe")
Thomas Wouters3fc2ca32006-04-21 11:28:17 +0000950 if dir=="setuptools":
951 lib.add_file("cli.exe")
952 lib.add_file("gui.exe")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000953 if dir=="data" and parent.physical=="test" and parent.basedir.physical=="email":
Martin v. Löwis9ca9f562006-01-03 06:29:53 +0000954 # This should contain all non-.svn files listed in subversion
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000955 for f in os.listdir(lib.absolute):
Martin v. Löwis9ca9f562006-01-03 06:29:53 +0000956 if f.endswith(".txt") or f==".svn":continue
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000957 if f.endswith(".au") or f.endswith(".gif"):
958 lib.add_file(f)
959 else:
Collin Winter6afaeb72007-08-03 17:06:41 +0000960 print("WARNING: New file %s in email/test/data" % f)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000961 for f in os.listdir(lib.absolute):
962 if os.path.isdir(os.path.join(lib.absolute, f)):
963 pydirs.append((lib, f))
964 # Add DLLs
965 default_feature.set_current()
966 lib = PyDirectory(db, cab, root, srcdir+"/PCBuild", "DLLs", "DLLS|DLLs")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000967 lib.add_file("py.ico", src="../PC/py.ico")
968 lib.add_file("pyc.ico", src="../PC/pyc.ico")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000969 dlls = []
970 tclfiles = []
971 for f in extensions:
972 if f=="_tkinter.pyd":
973 continue
974 if not os.path.exists(srcdir+"/PCBuild/"+f):
Collin Winter6afaeb72007-08-03 17:06:41 +0000975 print("WARNING: Missing extension", f)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000976 continue
977 dlls.append(f)
978 lib.add_file(f)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000979 # Add sqlite
980 if msilib.msi_type=="Intel64;1033":
981 sqlite_arch = "/ia64"
982 elif msilib.msi_type=="x64;1033":
983 sqlite_arch = "/amd64"
984 else:
985 sqlite_arch = ""
986 lib.add_file(srcdir+"/"+sqlite_dir+sqlite_arch+"/sqlite3.dll")
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000987 if have_tcl:
988 if not os.path.exists(srcdir+"/PCBuild/_tkinter.pyd"):
Collin Winter6afaeb72007-08-03 17:06:41 +0000989 print("WARNING: Missing _tkinter.pyd")
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000990 else:
991 lib.start_component("TkDLLs", tcltk)
992 lib.add_file("_tkinter.pyd")
993 dlls.append("_tkinter.pyd")
994 tcldir = os.path.normpath(srcdir+"/../tcltk/bin")
995 for f in glob.glob1(tcldir, "*.dll"):
996 lib.add_file(f, src=os.path.join(tcldir, f))
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000997 # check whether there are any unknown extensions
998 for f in glob.glob1(srcdir+"/PCBuild", "*.pyd"):
999 if f.endswith("_d.pyd"): continue # debug version
1000 if f in dlls: continue
Collin Winter6afaeb72007-08-03 17:06:41 +00001001 print("WARNING: Unknown extension", f)
Tim Peters66cb0182004-08-26 05:23:19 +00001002
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001003 # Add headers
1004 default_feature.set_current()
1005 lib = PyDirectory(db, cab, root, "include", "include", "INCLUDE|include")
1006 lib.glob("*.h")
1007 lib.add_file("pyconfig.h", src="../PC/pyconfig.h")
1008 # Add import libraries
1009 lib = PyDirectory(db, cab, root, "PCBuild", "libs", "LIBS|libs")
1010 for f in dlls:
1011 lib.add_file(f.replace('pyd','lib'))
1012 lib.add_file('python%s%s.lib' % (major, minor))
Martin v. Löwis9fda9312004-12-22 13:41:49 +00001013 # Add the mingw-format library
1014 if have_mingw:
Tim Peters5a9fb3c2005-01-07 16:01:32 +00001015 lib.add_file('libpython%s%s.a' % (major, minor))
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001016 if have_tcl:
1017 # Add Tcl/Tk
1018 tcldirs = [(root, '../tcltk/lib', 'tcl')]
1019 tcltk.set_current()
1020 while tcldirs:
1021 parent, phys, dir = tcldirs.pop()
1022 lib = PyDirectory(db, cab, parent, phys, dir, "%s|%s" % (parent.make_short(dir), dir))
1023 if not os.path.exists(lib.absolute):
1024 continue
1025 for f in os.listdir(lib.absolute):
1026 if os.path.isdir(os.path.join(lib.absolute, f)):
1027 tcldirs.append((lib, f, f))
1028 else:
1029 lib.add_file(f)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001030 # Add tools
1031 tools.set_current()
1032 tooldir = PyDirectory(db, cab, root, "Tools", "Tools", "TOOLS|Tools")
1033 for f in ['i18n', 'pynche', 'Scripts', 'versioncheck', 'webchecker']:
1034 lib = PyDirectory(db, cab, tooldir, f, f, "%s|%s" % (tooldir.make_short(f), f))
1035 lib.glob("*.py")
1036 lib.glob("*.pyw", exclude=['pydocgui.pyw'])
1037 lib.remove_pyc()
1038 lib.glob("*.txt")
1039 if f == "pynche":
1040 x = PyDirectory(db, cab, lib, "X", "X", "X|X")
1041 x.glob("*.txt")
Martin v. Löwis4d930be2004-12-01 21:46:35 +00001042 if os.path.exists(os.path.join(lib.absolute, "README")):
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001043 lib.add_file("README.txt", src="README")
Martin v. Löwis4d930be2004-12-01 21:46:35 +00001044 if f == 'Scripts':
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001045 if have_tcl:
1046 lib.start_component("pydocgui.pyw", tcltk, keyfile="pydocgui.pyw")
1047 lib.add_file("pydocgui.pyw")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001048 # Add documentation
1049 htmlfiles.set_current()
1050 lib = PyDirectory(db, cab, root, "Doc", "Doc", "DOC|Doc")
1051 lib.start_component("documentation", keyfile="Python%s%s.chm" % (major,minor))
1052 lib.add_file("Python%s%s.chm" % (major, minor))
1053
1054 cab.commit(db)
1055
1056 for f in tmpfiles:
1057 os.unlink(f)
1058
1059# See "Registry Table", "Component Table"
1060def add_registry(db):
1061 # File extensions, associated with the REGISTRY.def component
1062 # IDLE verbs depend on the tcltk feature.
1063 # msidbComponentAttributesRegistryKeyPath = 4
1064 # -1 for Root specifies "dependent on ALLUSERS property"
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001065 tcldata = []
1066 if have_tcl:
1067 tcldata = [
Martin v. Löwis283e35f2007-08-31 09:59:29 +00001068 ("REGISTRY.tcl", msilib.gen_uuid(), "TARGETDIR", registry_component, None,
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001069 "py.IDLE")]
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001070 add_data(db, "Component",
1071 # msidbComponentAttributesRegistryKeyPath = 4
Martin v. Löwis283e35f2007-08-31 09:59:29 +00001072 [("REGISTRY", msilib.gen_uuid(), "TARGETDIR", registry_component, None,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001073 "InstallPath"),
Martin v. Löwis283e35f2007-08-31 09:59:29 +00001074 ("REGISTRY.doc", msilib.gen_uuid(), "TARGETDIR", registry_component, None,
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001075 "Documentation"),
Martin v. Löwis283e35f2007-08-31 09:59:29 +00001076 ("REGISTRY.def", msilib.gen_uuid(), "TARGETDIR", registry_component,
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001077 None, None)] + tcldata)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001078 # See "FeatureComponents Table".
1079 # The association between TclTk and pythonw.exe is necessary to make ICE59
1080 # happy, because the installer otherwise believes that the IDLE and PyDoc
1081 # shortcuts might get installed without pythonw.exe being install. This
1082 # is not true, since installing TclTk will install the default feature, which
1083 # will cause pythonw.exe to be installed.
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001084 # REGISTRY.tcl is not associated with any feature, as it will be requested
1085 # through a custom action
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001086 tcldata = []
1087 if have_tcl:
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001088 tcldata = [(tcltk.id, "pythonw.exe")]
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001089 add_data(db, "FeatureComponents",
1090 [(default_feature.id, "REGISTRY"),
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001091 (htmlfiles.id, "REGISTRY.doc"),
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001092 (ext_feature.id, "REGISTRY.def")] +
1093 tcldata
1094 )
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001095 # Extensions are not advertised. For advertised extensions,
1096 # we would need separate binaries that install along with the
1097 # extension.
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001098 pat = r"Software\Classes\%sPython.%sFile\shell\%s\command"
1099 ewi = "Edit with IDLE"
1100 pat2 = r"Software\Classes\%sPython.%sFile\DefaultIcon"
1101 pat3 = r"Software\Classes\%sPython.%sFile"
Martin v. Löwiseac02e62004-11-18 08:00:33 +00001102 tcl_verbs = []
1103 if have_tcl:
1104 tcl_verbs=[
1105 ("py.IDLE", -1, pat % (testprefix, "", ewi), "",
1106 r'"[TARGETDIR]pythonw.exe" "[TARGETDIR]Lib\idlelib\idle.pyw" -n -e "%1"',
1107 "REGISTRY.tcl"),
1108 ("pyw.IDLE", -1, pat % (testprefix, "NoCon", ewi), "",
1109 r'"[TARGETDIR]pythonw.exe" "[TARGETDIR]Lib\idlelib\idle.pyw" -n -e "%1"',
1110 "REGISTRY.tcl"),
1111 ]
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001112 add_data(db, "Registry",
1113 [# Extensions
1114 ("py.ext", -1, r"Software\Classes\."+ext, "",
1115 "Python.File", "REGISTRY.def"),
1116 ("pyw.ext", -1, r"Software\Classes\."+ext+'w', "",
1117 "Python.NoConFile", "REGISTRY.def"),
1118 ("pyc.ext", -1, r"Software\Classes\."+ext+'c', "",
1119 "Python.CompiledFile", "REGISTRY.def"),
1120 ("pyo.ext", -1, r"Software\Classes\."+ext+'o', "",
1121 "Python.CompiledFile", "REGISTRY.def"),
1122 # MIME types
1123 ("py.mime", -1, r"Software\Classes\."+ext, "Content Type",
1124 "text/plain", "REGISTRY.def"),
1125 ("pyw.mime", -1, r"Software\Classes\."+ext+'w', "Content Type",
1126 "text/plain", "REGISTRY.def"),
1127 #Verbs
1128 ("py.open", -1, pat % (testprefix, "", "open"), "",
1129 r'"[TARGETDIR]python.exe" "%1" %*', "REGISTRY.def"),
1130 ("pyw.open", -1, pat % (testprefix, "NoCon", "open"), "",
1131 r'"[TARGETDIR]pythonw.exe" "%1" %*', "REGISTRY.def"),
1132 ("pyc.open", -1, pat % (testprefix, "Compiled", "open"), "",
1133 r'"[TARGETDIR]python.exe" "%1" %*', "REGISTRY.def"),
Martin v. Löwiseac02e62004-11-18 08:00:33 +00001134 ] + tcl_verbs + [
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001135 #Icons
1136 ("py.icon", -1, pat2 % (testprefix, ""), "",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001137 r'[DLLs]py.ico', "REGISTRY.def"),
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001138 ("pyw.icon", -1, pat2 % (testprefix, "NoCon"), "",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001139 r'[DLLs]py.ico', "REGISTRY.def"),
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001140 ("pyc.icon", -1, pat2 % (testprefix, "Compiled"), "",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001141 r'[DLLs]pyc.ico', "REGISTRY.def"),
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001142 # Descriptions
1143 ("py.txt", -1, pat3 % (testprefix, ""), "",
1144 "Python File", "REGISTRY.def"),
1145 ("pyw.txt", -1, pat3 % (testprefix, "NoCon"), "",
1146 "Python File (no console)", "REGISTRY.def"),
1147 ("pyc.txt", -1, pat3 % (testprefix, "Compiled"), "",
1148 "Compiled Python File", "REGISTRY.def"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001149 ])
Tim Peters66cb0182004-08-26 05:23:19 +00001150
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001151 # Registry keys
1152 prefix = r"Software\%sPython\PythonCore\%s" % (testprefix, short_version)
1153 add_data(db, "Registry",
1154 [("InstallPath", -1, prefix+r"\InstallPath", "", "[TARGETDIR]", "REGISTRY"),
1155 ("InstallGroup", -1, prefix+r"\InstallPath\InstallGroup", "",
1156 "Python %s" % short_version, "REGISTRY"),
1157 ("PythonPath", -1, prefix+r"\PythonPath", "",
Martin v. Löwisf13337d2004-09-19 18:36:45 +00001158 r"[TARGETDIR]Lib;[TARGETDIR]DLLs;[TARGETDIR]Lib\lib-tk", "REGISTRY"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001159 ("Documentation", -1, prefix+r"\Help\Main Python Documentation", "",
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001160 r"[TARGETDIR]Doc\Python%s%s.chm" % (major, minor), "REGISTRY.doc"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001161 ("Modules", -1, prefix+r"\Modules", "+", None, "REGISTRY"),
1162 ("AppPaths", -1, r"Software\Microsoft\Windows\CurrentVersion\App Paths\Python.exe",
1163 "", r"[TARGETDIR]Python.exe", "REGISTRY.def")
1164 ])
1165 # Shortcuts, see "Shortcut Table"
1166 add_data(db, "Directory",
1167 [("ProgramMenuFolder", "TARGETDIR", "."),
1168 ("MenuDir", "ProgramMenuFolder", "PY%s%s|%sPython %s.%s" % (major,minor,testprefix,major,minor))])
1169 add_data(db, "RemoveFile",
1170 [("MenuDir", "TARGETDIR", None, "MenuDir", 2)])
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001171 tcltkshortcuts = []
1172 if have_tcl:
1173 tcltkshortcuts = [
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001174 ("IDLE", "MenuDir", "IDLE|IDLE (Python GUI)", "pythonw.exe",
Martin v. Löwisac191da2004-12-22 12:55:44 +00001175 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 +00001176 ("PyDoc", "MenuDir", "MODDOCS|Module Docs", "pythonw.exe",
Martin v. Löwisac191da2004-12-22 12:55:44 +00001177 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 +00001178 ]
1179 add_data(db, "Shortcut",
1180 tcltkshortcuts +
1181 [# Advertised shortcuts: targets are features, not files
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001182 ("Python", "MenuDir", "PYTHON|Python (command line)", "python.exe",
1183 default_feature.id, None, None, None, "python_icon.exe", 2, None, "TARGETDIR"),
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001184 # Advertising the Manual breaks on (some?) Win98, and the shortcut lacks an
1185 # icon first.
1186 #("Manual", "MenuDir", "MANUAL|Python Manuals", "documentation",
1187 # htmlfiles.id, None, None, None, None, None, None, None),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001188 ## Non-advertised shortcuts: must be associated with a registry component
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001189 ("Manual", "MenuDir", "MANUAL|Python Manuals", "REGISTRY.doc",
1190 "[#Python%s%s.chm]" % (major,minor), None,
1191 None, None, None, None, None, None),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001192 ("Uninstall", "MenuDir", "UNINST|Uninstall Python", "REGISTRY",
1193 SystemFolderName+"msiexec", "/x%s" % product_code,
1194 None, None, None, None, None, None),
1195 ])
1196 db.Commit()
1197
1198db = build_database()
1199try:
1200 add_features(db)
1201 add_ui(db)
1202 add_files(db)
1203 add_registry(db)
1204 remove_old_versions(db)
1205 db.Commit()
1206finally:
1207 del db