blob: 74a364b7af8a60e97b4ace01b3c90e23f97172db [file] [log] [blame]
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001# Python MSI Generator
2# (C) 2003 Martin v. Loewis
3# See "FOO" in comments refers to MSDN sections with the title FOO.
Martin v. Löwis9fda9312004-12-22 13:41:49 +00004import msilib, schema, sequence, os, glob, time, re
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00005from msilib import Feature, CAB, Directory, Dialog, Binary, add_data
6import uisample
7from win32com.client import constants
Martin v. Löwis9fda9312004-12-22 13:41:49 +00008from distutils.spawn import find_executable
Martin v. Löwis1d278fc2006-03-28 18:30:05 +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
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +000027
28try:
29 from config import *
30except ImportError:
31 pass
32
33# Extract current version from Include/patchlevel.h
34lines = open(srcdir + "/Include/patchlevel.h").readlines()
35major = minor = micro = level = serial = None
36levels = {
37 'PY_RELEASE_LEVEL_ALPHA':0xA,
38 'PY_RELEASE_LEVEL_BETA': 0xB,
39 'PY_RELEASE_LEVEL_GAMMA':0xC,
40 'PY_RELEASE_LEVEL_FINAL':0xF
41 }
42for l in lines:
43 if not l.startswith("#define"):
44 continue
45 l = l.split()
46 if len(l) != 3:
47 continue
48 _, name, value = l
49 if name == 'PY_MAJOR_VERSION': major = value
50 if name == 'PY_MINOR_VERSION': minor = value
51 if name == 'PY_MICRO_VERSION': micro = value
52 if name == 'PY_RELEASE_LEVEL': level = levels[value]
53 if name == 'PY_RELEASE_SERIAL': serial = value
54
55short_version = major+"."+minor
56# See PC/make_versioninfo.c
57FIELD3 = 1000*int(micro) + 10*level + int(serial)
58current_version = "%s.%d" % (short_version, FIELD3)
59
60# This should never change. The UpgradeCode of this package can be
61# used in the Upgrade table of future packages to make the future
62# package replace this one. See "UpgradeCode Property".
63upgrade_code_snapshot='{92A24481-3ECB-40FC-8836-04B7966EC0D5}'
64upgrade_code='{65E6DE48-A358-434D-AA4F-4AF72DB4718F}'
65
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +000066if snapshot:
67 current_version = "%s.%s.%s" % (major, minor, int(time.time()/3600/24))
68 product_code = msilib.gen_uuid()
69else:
70 product_code = product_codes[current_version]
71
72if full_current_version is None:
73 full_current_version = current_version
74
75extensions = [
76 'bz2.pyd',
77 'pyexpat.pyd',
78 'select.pyd',
79 'unicodedata.pyd',
80 'winsound.pyd',
Trent Micke97e5a72005-12-15 22:08:46 +000081 '_elementtree.pyd',
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +000082 '_bsddb.pyd',
83 '_socket.pyd',
84 '_ssl.pyd',
85 '_testcapi.pyd',
86 '_tkinter.pyd',
Martin v. Löwis8c7c56e2006-03-05 14:04:26 +000087 '_msi.pyd',
Martin v. Löwisa09655e2006-03-10 15:36:28 +000088 '_ctypes.pyd',
89 '_ctypes_test.pyd'
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +000090]
91
Martin v. Löwis141f41a2005-03-15 00:39:40 +000092# Well-known component UUIDs
93# These are needed for SharedDLLs reference counter; if
94# a different UUID was used for each incarnation of, say,
95# python24.dll, an upgrade would set the reference counter
96# from 1 to 2 (due to what I consider a bug in MSI)
97# Using the same UUID is fine since these files are versioned,
98# so Installer will always keep the newest version.
99msvcr71_uuid = "{8666C8DD-D0B4-4B42-928E-A69E32FA5D4D}"
100pythondll_uuid = {
101 "24":"{9B81E618-2301-4035-AC77-75D9ABEB7301}",
102 "25":"{2e41b118-38bd-4c1b-a840-6977efd1b911}"
103 } [major+minor]
Tim Peterseba28be2005-03-28 01:08:02 +0000104
Martin v. Löwis9fda9312004-12-22 13:41:49 +0000105# Build the mingw import library, libpythonXY.a
106# This requires 'nm' and 'dlltool' executables on your PATH
107def build_mingw_lib(lib_file, def_file, dll_file, mingw_lib):
108 warning = "WARNING: %s - libpythonXX.a not built"
109 nm = find_executable('nm')
110 dlltool = find_executable('dlltool')
111
112 if not nm or not dlltool:
113 print warning % "nm and/or dlltool were not found"
114 return False
115
116 nm_command = '%s -Cs %s' % (nm, lib_file)
117 dlltool_command = "%s --dllname %s --def %s --output-lib %s" % \
118 (dlltool, dll_file, def_file, mingw_lib)
119 export_match = re.compile(r"^_imp__(.*) in python\d+\.dll").match
120
121 f = open(def_file,'w')
122 print >>f, "LIBRARY %s" % dll_file
123 print >>f, "EXPORTS"
124
125 nm_pipe = os.popen(nm_command)
126 for line in nm_pipe.readlines():
127 m = export_match(line)
128 if m:
129 print >>f, m.group(1)
130 f.close()
131 exit = nm_pipe.close()
132
133 if exit:
134 print warning % "nm did not run successfully"
135 return False
136
137 if os.system(dlltool_command) != 0:
138 print warning % "dlltool did not run successfully"
139 return False
140
141 return True
142
143# Target files (.def and .a) go in PCBuild directory
144lib_file = os.path.join(srcdir, "PCBuild", "python%s%s.lib" % (major, minor))
145def_file = os.path.join(srcdir, "PCBuild", "python%s%s.def" % (major, minor))
146dll_file = "python%s%s.dll" % (major, minor)
147mingw_lib = os.path.join(srcdir, "PCBuild", "libpython%s%s.a" % (major, minor))
148
149have_mingw = build_mingw_lib(lib_file, def_file, dll_file, mingw_lib)
150
Martin v. Löwis856bf9a2006-02-14 20:42:55 +0000151# Determine the target architechture
152dll_path = os.path.join(srcdir, "PCBuild", dll_file)
153msilib.set_arch_from_file(dll_path)
154if msilib.pe_type(dll_path) != msilib.pe_type("msisupport.dll"):
155 raise SystemError, "msisupport.dll for incorrect architecture"
156
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000157if testpackage:
158 ext = 'px'
159 testprefix = 'x'
160else:
161 ext = 'py'
162 testprefix = ''
163
164if msilib.Win64:
165 SystemFolderName = "[SystemFolder64]"
166else:
167 SystemFolderName = "[SystemFolder]"
168
169msilib.reset()
170
171# condition in which to install pythonxy.dll in system32:
172# a) it is Windows 9x or
173# b) it is NT, the user is privileged, and has chosen per-machine installation
174sys32cond = "(Windows9x or (Privileged and ALLUSERS))"
175
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000176def build_database():
177 """Generate an empty database, with just the schema and the
178 Summary information stream."""
179 if snapshot:
180 uc = upgrade_code_snapshot
181 else:
182 uc = upgrade_code
183 # schema represents the installer 2.0 database schema.
184 # sequence is the set of standard sequences
185 # (ui/execute, admin/advt/install)
Martin v. Löwis856bf9a2006-02-14 20:42:55 +0000186 db = msilib.init_database("python-%s%s.msi" % (full_current_version, msilib.arch_ext),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000187 schema, ProductName="Python "+full_current_version,
188 ProductCode=product_code,
189 ProductVersion=current_version,
190 Manufacturer=u"Martin v. L\xf6wis")
191 # The default sequencing of the RemoveExistingProducts action causes
192 # removal of files that got just installed. Place it after
193 # InstallInitialize, so we first uninstall everything, but still roll
194 # back in case the installation is interrupted
195 msilib.change_sequence(sequence.InstallExecuteSequence,
196 "RemoveExistingProducts", 1510)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000197 msilib.add_tables(db, sequence)
198 # We cannot set ALLUSERS in the property table, as this cannot be
199 # reset if the user choses a per-user installation. Instead, we
200 # maintain WhichUsers, which can be "ALL" or "JUSTME". The UI manages
201 # this property, and when the execution starts, ALLUSERS is set
202 # accordingly.
203 add_data(db, "Property", [("UpgradeCode", uc),
204 ("WhichUsers", "ALL"),
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000205 ("ProductLine", "Python%s%s" % (major, minor)),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000206 ])
207 db.Commit()
208 return db
209
210def remove_old_versions(db):
211 "Fill the upgrade table."
212 start = "%s.%s.0" % (major, minor)
213 # This requests that feature selection states of an older
214 # installation should be forwarded into this one. Upgrading
215 # requires that both the old and the new installation are
216 # either both per-machine or per-user.
217 migrate_features = 1
218 # See "Upgrade Table". We remove releases with the same major and
219 # minor version. For an snapshot, we remove all earlier snapshots. For
220 # a release, we remove all snapshots, and all earlier releases.
221 if snapshot:
222 add_data(db, "Upgrade",
Tim Peters66cb0182004-08-26 05:23:19 +0000223 [(upgrade_code_snapshot, start,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000224 current_version,
225 None, # Ignore language
Tim Peters66cb0182004-08-26 05:23:19 +0000226 migrate_features,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000227 None, # Migrate ALL features
228 "REMOVEOLDSNAPSHOT")])
229 props = "REMOVEOLDSNAPSHOT"
230 else:
231 add_data(db, "Upgrade",
232 [(upgrade_code, start, current_version,
233 None, migrate_features, None, "REMOVEOLDVERSION"),
234 (upgrade_code_snapshot, start, "%s.%d.0" % (major, int(minor)+1),
235 None, migrate_features, None, "REMOVEOLDSNAPSHOT")])
236 props = "REMOVEOLDSNAPSHOT;REMOVEOLDVERSION"
237 # Installer collects the product codes of the earlier releases in
238 # these properties. In order to allow modification of the properties,
239 # they must be declared as secure. See "SecureCustomProperties Property"
240 add_data(db, "Property", [("SecureCustomProperties", props)])
241
242class PyDialog(Dialog):
243 """Dialog class with a fixed layout: controls at the top, then a ruler,
244 then a list of buttons: back, next, cancel. Optionally a bitmap at the
245 left."""
246 def __init__(self, *args, **kw):
247 """Dialog(database, name, x, y, w, h, attributes, title, first,
248 default, cancel, bitmap=true)"""
249 Dialog.__init__(self, *args)
250 ruler = self.h - 36
251 bmwidth = 152*ruler/328
252 if kw.get("bitmap", True):
253 self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin")
254 self.line("BottomLine", 0, ruler, self.w, 0)
255
256 def title(self, title):
257 "Set the title text of the dialog at the top."
258 # name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix,
259 # text, in VerdanaBold10
260 self.text("Title", 135, 10, 220, 60, 0x30003,
261 r"{\VerdanaBold10}%s" % title)
262
263 def back(self, title, next, name = "Back", active = 1):
264 """Add a back button with a given title, the tab-next button,
265 its name in the Control table, possibly initially disabled.
266
267 Return the button, so that events can be associated"""
268 if active:
269 flags = 3 # Visible|Enabled
270 else:
271 flags = 1 # Visible
272 return self.pushbutton(name, 180, self.h-27 , 56, 17, flags, title, next)
273
274 def cancel(self, title, next, name = "Cancel", active = 1):
275 """Add a cancel button with a given title, the tab-next button,
276 its name in the Control table, possibly initially disabled.
277
278 Return the button, so that events can be associated"""
279 if active:
280 flags = 3 # Visible|Enabled
281 else:
282 flags = 1 # Visible
283 return self.pushbutton(name, 304, self.h-27, 56, 17, flags, title, next)
284
285 def next(self, title, next, name = "Next", active = 1):
286 """Add a Next button with a given title, the tab-next button,
287 its name in the Control table, possibly initially disabled.
288
289 Return the button, so that events can be associated"""
290 if active:
291 flags = 3 # Visible|Enabled
292 else:
293 flags = 1 # Visible
294 return self.pushbutton(name, 236, self.h-27, 56, 17, flags, title, next)
295
296 def xbutton(self, name, title, next, xpos):
297 """Add a button with a given title, the tab-next button,
298 its name in the Control table, giving its x position; the
299 y-position is aligned with the other buttons.
300
301 Return the button, so that events can be associated"""
302 return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next)
303
304def add_ui(db):
305 x = y = 50
306 w = 370
307 h = 300
308 title = "[ProductName] Setup"
309
310 # see "Dialog Style Bits"
311 modal = 3 # visible | modal
312 modeless = 1 # visible
313 track_disk_space = 32
314
315 add_data(db, 'ActionText', uisample.ActionText)
316 add_data(db, 'UIText', uisample.UIText)
317
318 # Bitmaps
319 if not os.path.exists(srcdir+r"\PC\python_icon.exe"):
320 raise "Run icons.mak in PC directory"
321 add_data(db, "Binary",
322 [("PythonWin", msilib.Binary(srcdir+r"\PCbuild\installer.bmp")), # 152x328 pixels
323 ("py.ico",msilib.Binary(srcdir+r"\PC\py.ico")),
324 ])
325 add_data(db, "Icon",
326 [("python_icon.exe", msilib.Binary(srcdir+r"\PC\python_icon.exe"))])
327
328 # Scripts
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000329 # CheckDir sets TargetExists if TARGETDIR exists.
330 # UpdateEditIDLE sets the REGISTRY.tcl component into
331 # the installed/uninstalled state according to both the
332 # Extensions and TclTk features.
Martin v. Löwiseb68be42004-12-12 15:29:21 +0000333 if os.system("nmake /nologo /c /f msisupport.mak") != 0:
334 raise "'nmake /f msisupport.mak' failed"
335 add_data(db, "Binary", [("Script", msilib.Binary("msisupport.dll"))])
336 # See "Custom Action Type 1"
Martin v. Löwis3390d332005-03-14 17:20:13 +0000337 if msilib.Win64:
338 CheckDir = "CheckDir"
Martin v. Löwisdf40ce32006-02-16 14:38:30 +0000339 UpdateEditIDLE = "UpdateEditIDLE"
Martin v. Löwis3390d332005-03-14 17:20:13 +0000340 else:
341 CheckDir = "_CheckDir@4"
342 UpdateEditIDLE = "_UpdateEditIDLE@4"
Tim Peters0e9980f2004-09-12 03:49:31 +0000343 add_data(db, "CustomAction",
Martin v. Löwis3390d332005-03-14 17:20:13 +0000344 [("CheckDir", 1, "Script", CheckDir)])
Martin v. Löwiseac02e62004-11-18 08:00:33 +0000345 if have_tcl:
346 add_data(db, "CustomAction",
Martin v. Löwis3390d332005-03-14 17:20:13 +0000347 [("UpdateEditIDLE", 1, "Script", UpdateEditIDLE)])
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000348
349 # UI customization properties
350 add_data(db, "Property",
351 # See "DefaultUIFont Property"
352 [("DefaultUIFont", "DlgFont8"),
353 # See "ErrorDialog Style Bit"
354 ("ErrorDialog", "ErrorDlg"),
355 ("Progress1", "Install"), # modified in maintenance type dlg
356 ("Progress2", "installs"),
357 ("MaintenanceForm_Action", "Repair")])
358
359 # Fonts, see "TextStyle Table"
360 add_data(db, "TextStyle",
361 [("DlgFont8", "Tahoma", 9, None, 0),
362 ("DlgFontBold8", "Tahoma", 8, None, 1), #bold
363 ("VerdanaBold10", "Verdana", 10, None, 1),
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000364 ("VerdanaRed9", "Verdana", 9, 255, 0),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000365 ])
366
Martin v. Löwis7b2563b2004-11-02 22:59:56 +0000367 compileargs = r"-Wi [TARGETDIR]Lib\compileall.py -f -x badsyntax [TARGETDIR]Lib"
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000368 # See "CustomAction Table"
369 add_data(db, "CustomAction", [
370 # msidbCustomActionTypeFirstSequence + msidbCustomActionTypeTextData + msidbCustomActionTypeProperty
371 # See "Custom Action Type 51",
372 # "Custom Action Execution Scheduling Options"
373 ("InitialTargetDir", 307, "TARGETDIR",
374 "[WindowsVolume]Python%s%s" % (major, minor)),
375 ("SetDLLDirToTarget", 307, "DLLDIR", "[TARGETDIR]"),
376 ("SetDLLDirToSystem32", 307, "DLLDIR", SystemFolderName),
377 # msidbCustomActionTypeExe + msidbCustomActionTypeSourceFile
378 # See "Custom Action Type 18"
Martin v. Löwis7b2563b2004-11-02 22:59:56 +0000379 ("CompilePyc", 18, "python.exe", compileargs),
380 ("CompilePyo", 18, "python.exe", "-O "+compileargs),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000381 ])
382
383 # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table"
384 # Numbers indicate sequence; see sequence.py for how these action integrate
385 add_data(db, "InstallUISequence",
386 [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140),
387 ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141),
388 ("InitialTargetDir", 'TARGETDIR=""', 750),
389 # In the user interface, assume all-users installation if privileged.
390 ("SetDLLDirToSystem32", 'DLLDIR="" and ' + sys32cond, 751),
391 ("SetDLLDirToTarget", 'DLLDIR="" and not ' + sys32cond, 752),
392 ("SelectDirectoryDlg", "Not Installed", 1230),
393 # XXX no support for resume installations yet
394 #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240),
395 ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250),
396 ("ProgressDlg", None, 1280)])
397 add_data(db, "AdminUISequence",
398 [("InitialTargetDir", 'TARGETDIR=""', 750),
399 ("SetDLLDirToTarget", 'DLLDIR=""', 751),
400 ])
401
402 # Execute Sequences
403 add_data(db, "InstallExecuteSequence",
404 [("InitialTargetDir", 'TARGETDIR=""', 750),
405 ("SetDLLDirToSystem32", 'DLLDIR="" and ' + sys32cond, 751),
406 ("SetDLLDirToTarget", 'DLLDIR="" and not ' + sys32cond, 752),
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000407 ("UpdateEditIDLE", None, 1050),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000408 ("CompilePyc", "COMPILEALL", 6800),
409 ("CompilePyo", "COMPILEALL", 6801),
410 ])
411 add_data(db, "AdminExecuteSequence",
412 [("InitialTargetDir", 'TARGETDIR=""', 750),
413 ("SetDLLDirToTarget", 'DLLDIR=""', 751),
414 ("CompilePyc", "COMPILEALL", 6800),
415 ("CompilePyo", "COMPILEALL", 6801),
416 ])
417
418 #####################################################################
419 # Standard dialogs: FatalError, UserExit, ExitDialog
420 fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title,
421 "Finish", "Finish", "Finish")
422 fatal.title("[ProductName] Installer ended prematurely")
423 fatal.back("< Back", "Finish", active = 0)
424 fatal.cancel("Cancel", "Back", active = 0)
425 fatal.text("Description1", 135, 70, 220, 80, 0x30003,
426 "[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.")
427 fatal.text("Description2", 135, 155, 220, 20, 0x30003,
428 "Click the Finish button to exit the Installer.")
429 c=fatal.next("Finish", "Cancel", name="Finish")
430 # See "ControlEvent Table". Parameters are the event, the parameter
431 # to the action, and optionally the condition for the event, and the order
432 # of events.
433 c.event("EndDialog", "Exit")
Tim Peters66cb0182004-08-26 05:23:19 +0000434
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000435 user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title,
436 "Finish", "Finish", "Finish")
437 user_exit.title("[ProductName] Installer was interrupted")
438 user_exit.back("< Back", "Finish", active = 0)
439 user_exit.cancel("Cancel", "Back", active = 0)
440 user_exit.text("Description1", 135, 70, 220, 80, 0x30003,
441 "[ProductName] setup was interrupted. Your system has not been modified. "
442 "To install this program at a later time, please run the installation again.")
443 user_exit.text("Description2", 135, 155, 220, 20, 0x30003,
444 "Click the Finish button to exit the Installer.")
445 c = user_exit.next("Finish", "Cancel", name="Finish")
446 c.event("EndDialog", "Exit")
Tim Peters66cb0182004-08-26 05:23:19 +0000447
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000448 exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title,
449 "Finish", "Finish", "Finish")
450 exit_dialog.title("Completing the [ProductName] Installer")
451 exit_dialog.back("< Back", "Finish", active = 0)
452 exit_dialog.cancel("Cancel", "Back", active = 0)
Martin v. Löwis2dd2a282004-08-22 17:10:12 +0000453 exit_dialog.text("Acknowledgements", 135, 95, 220, 120, 0x30003,
454 "Special Windows thanks to:\n"
Martin v. Löwisd3f61a22004-08-30 09:22:30 +0000455 " LettError, Erik van Blokland, for the \n"
456 " Python for Windows graphic.\n"
Martin v. Löwis2dd2a282004-08-22 17:10:12 +0000457 " http://www.letterror.com/\n"
458 "\n"
Martin v. Löwisd3f61a22004-08-30 09:22:30 +0000459 " Mark Hammond, without whose years of freely \n"
460 " shared Windows expertise, Python for Windows \n"
461 " would still be Python for DOS.")
Tim Peters66cb0182004-08-26 05:23:19 +0000462
Martin v. Löwis8c7c56e2006-03-05 14:04:26 +0000463 c = exit_dialog.text("warning", 135, 200, 220, 40, 0x30003,
464 "{\\VerdanaRed9}Warning: Python 2.5.x is the last "
465 "Python release for Windows 9x.")
Martin v. Löwisdf511792006-03-28 07:51:51 +0000466 c.condition("Hide", "NOT Version9X")
Martin v. Löwis8c7c56e2006-03-05 14:04:26 +0000467
Martin v. Löwis2dd2a282004-08-22 17:10:12 +0000468 exit_dialog.text("Description", 135, 235, 220, 20, 0x30003,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000469 "Click the Finish button to exit the Installer.")
470 c = exit_dialog.next("Finish", "Cancel", name="Finish")
471 c.event("EndDialog", "Return")
472
473 #####################################################################
474 # Required dialog: FilesInUse, ErrorDlg
475 inuse = PyDialog(db, "FilesInUse",
476 x, y, w, h,
477 19, # KeepModeless|Modal|Visible
478 title,
479 "Retry", "Retry", "Retry", bitmap=False)
480 inuse.text("Title", 15, 6, 200, 15, 0x30003,
481 r"{\DlgFontBold8}Files in Use")
482 inuse.text("Description", 20, 23, 280, 20, 0x30003,
483 "Some files that need to be updated are currently in use.")
484 inuse.text("Text", 20, 55, 330, 50, 3,
485 "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.")
486 inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess",
487 None, None, None)
488 c=inuse.back("Exit", "Ignore", name="Exit")
489 c.event("EndDialog", "Exit")
490 c=inuse.next("Ignore", "Retry", name="Ignore")
491 c.event("EndDialog", "Ignore")
492 c=inuse.cancel("Retry", "Exit", name="Retry")
493 c.event("EndDialog","Retry")
494
495
496 # See "Error Dialog". See "ICE20" for the required names of the controls.
497 error = Dialog(db, "ErrorDlg",
498 50, 10, 330, 101,
499 65543, # Error|Minimize|Modal|Visible
500 title,
501 "ErrorText", None, None)
502 error.text("ErrorText", 50,9,280,48,3, "")
503 error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None)
504 error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo")
505 error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes")
506 error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort")
507 error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel")
508 error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore")
509 error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk")
510 error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry")
511
512 #####################################################################
513 # Global "Query Cancel" dialog
514 cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title,
515 "No", "No", "No")
Tim Peters66cb0182004-08-26 05:23:19 +0000516 cancel.text("Text", 48, 15, 194, 30, 3,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000517 "Are you sure you want to cancel [ProductName] installation?")
518 cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
519 "py.ico", None, None)
520 c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No")
521 c.event("EndDialog", "Exit")
Tim Peters66cb0182004-08-26 05:23:19 +0000522
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000523 c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes")
524 c.event("EndDialog", "Return")
525
526 #####################################################################
527 # Global "Wait for costing" dialog
528 costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title,
529 "Return", "Return", "Return")
530 costing.text("Text", 48, 15, 194, 30, 3,
531 "Please wait while the installer finishes determining your disk space requirements.")
532 costing.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
533 "py.ico", None, None)
534 c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None)
535 c.event("EndDialog", "Exit")
536
537 #####################################################################
538 # Preparation dialog: no user input except cancellation
539 prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title,
540 "Cancel", "Cancel", "Cancel")
541 prep.text("Description", 135, 70, 220, 40, 0x30003,
542 "Please wait while the Installer prepares to guide you through the installation.")
543 prep.title("Welcome to the [ProductName] Installer")
544 c=prep.text("ActionText", 135, 110, 220, 20, 0x30003, "Pondering...")
545 c.mapping("ActionText", "Text")
546 c=prep.text("ActionData", 135, 135, 220, 30, 0x30003, None)
547 c.mapping("ActionData", "Text")
548 prep.back("Back", None, active=0)
549 prep.next("Next", None, active=0)
550 c=prep.cancel("Cancel", None)
551 c.event("SpawnDialog", "CancelDlg")
552
553 #####################################################################
554 # Target directory selection
555 seldlg = PyDialog(db, "SelectDirectoryDlg", x, y, w, h, modal, title,
556 "Next", "Next", "Cancel")
557 seldlg.title("Select Destination Directory")
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000558 c = seldlg.text("Existing", 135, 25, 235, 30, 0x30003,
559 "{\VerdanaRed9}This update will replace your existing [ProductLine] installation.")
560 c.condition("Hide", 'REMOVEOLDVERSION="" and REMOVEOLDSNAPSHOT=""')
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000561 seldlg.text("Description", 135, 50, 220, 40, 0x30003,
562 "Please select a directory for the [ProductName] files.")
563
564 seldlg.back("< Back", None, active=0)
565 c = seldlg.next("Next >", "Cancel")
566 c.event("DoAction", "CheckDir", "TargetExistsOk<>1", order=1)
567 # If the target exists, but we found that we are going to remove old versions, don't bother
568 # confirming that the target directory exists. Strictly speaking, we should determine that
569 # the target directory is indeed the target of the product that we are going to remove, but
570 # I don't know how to do that.
571 c.event("SpawnDialog", "ExistingDirectoryDlg", 'TargetExists=1 and REMOVEOLDVERSION="" and REMOVEOLDSNAPSHOT=""', 2)
572 c.event("SetTargetPath", "TARGETDIR", 'TargetExists=0 or REMOVEOLDVERSION<>"" or REMOVEOLDSNAPSHOT<>""', 3)
573 c.event("SpawnWaitDialog", "WaitForCostingDlg", "CostingComplete=1", 4)
574 c.event("NewDialog", "SelectFeaturesDlg", 'TargetExists=0 or REMOVEOLDVERSION<>"" or REMOVEOLDSNAPSHOT<>""', 5)
575
576 c = seldlg.cancel("Cancel", "DirectoryCombo")
577 c.event("SpawnDialog", "CancelDlg")
578
579 seldlg.control("DirectoryCombo", "DirectoryCombo", 135, 70, 172, 80, 393219,
580 "TARGETDIR", None, "DirectoryList", None)
581 seldlg.control("DirectoryList", "DirectoryList", 135, 90, 208, 136, 3, "TARGETDIR",
582 None, "PathEdit", None)
583 seldlg.control("PathEdit", "PathEdit", 135, 230, 206, 16, 3, "TARGETDIR", None, "Next", None)
584 c = seldlg.pushbutton("Up", 306, 70, 18, 18, 3, "Up", None)
585 c.event("DirectoryListUp", "0")
586 c = seldlg.pushbutton("NewDir", 324, 70, 30, 18, 3, "New", None)
587 c.event("DirectoryListNew", "0")
588
589 #####################################################################
590 # SelectFeaturesDlg
591 features = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal|track_disk_space,
592 title, "Tree", "Next", "Cancel")
593 features.title("Customize [ProductName]")
594 features.text("Description", 135, 35, 220, 15, 0x30003,
595 "Select the way you want features to be installed.")
596 features.text("Text", 135,45,220,30, 3,
597 "Click on the icons in the tree below to change the way features will be installed.")
598
599 c=features.back("< Back", "Next")
600 c.event("NewDialog", "SelectDirectoryDlg")
601
602 c=features.next("Next >", "Cancel")
603 c.mapping("SelectionNoItems", "Enabled")
604 c.event("SpawnDialog", "DiskCostDlg", "OutOfDiskSpace=1", order=1)
605 c.event("EndDialog", "Return", "OutOfDiskSpace<>1", order=2)
606
607 c=features.cancel("Cancel", "Tree")
608 c.event("SpawnDialog", "CancelDlg")
609
Tim Peters66cb0182004-08-26 05:23:19 +0000610 # 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 +0000611 features.control("Tree", "SelectionTree", 135, 75, 220, 95, 7, "_BrowseProperty",
612 "Tree of selections", "Back", None)
613
614 #c=features.pushbutton("Reset", 42, 243, 56, 17, 3, "Reset", "DiskCost")
615 #c.mapping("SelectionNoItems", "Enabled")
616 #c.event("Reset", "0")
Tim Peters66cb0182004-08-26 05:23:19 +0000617
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000618 features.control("Box", "GroupBox", 135, 170, 225, 90, 1, None, None, None, None)
619
620 c=features.xbutton("DiskCost", "Disk &Usage", None, 0.10)
621 c.mapping("SelectionNoItems","Enabled")
622 c.event("SpawnDialog", "DiskCostDlg")
623
624 c=features.xbutton("Advanced", "Advanced", None, 0.30)
625 c.event("SpawnDialog", "AdvancedDlg")
626
627 c=features.text("ItemDescription", 140, 180, 210, 30, 3,
628 "Multiline description of the currently selected item.")
629 c.mapping("SelectionDescription","Text")
Tim Peters66cb0182004-08-26 05:23:19 +0000630
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000631 c=features.text("ItemSize", 140, 210, 210, 45, 3,
632 "The size of the currently selected item.")
633 c.mapping("SelectionSize", "Text")
634
635 #####################################################################
636 # Disk cost
637 cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title,
638 "OK", "OK", "OK", bitmap=False)
639 cost.text("Title", 15, 6, 200, 15, 0x30003,
640 "{\DlgFontBold8}Disk Space Requirements")
641 cost.text("Description", 20, 20, 280, 20, 0x30003,
642 "The disk space required for the installation of the selected features.")
643 cost.text("Text", 20, 53, 330, 60, 3,
644 "The highlighted volumes (if any) do not have enough disk space "
645 "available for the currently selected features. You can either "
646 "remove some files from the highlighted volumes, or choose to "
647 "install less features onto local drive(s), or select different "
648 "destination drive(s).")
649 cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223,
650 None, "{120}{70}{70}{70}{70}", None, None)
651 cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return")
652
653 #####################################################################
654 # WhichUsers Dialog. Only available on NT, and for privileged users.
655 # This must be run before FindRelatedProducts, because that will
656 # take into account whether the previous installation was per-user
657 # or per-machine. We currently don't support going back to this
658 # dialog after "Next" was selected; to support this, we would need to
659 # find how to reset the ALLUSERS property, and how to re-run
660 # FindRelatedProducts.
661 # On Windows9x, the ALLUSERS property is ignored on the command line
662 # and in the Property table, but installer fails according to the documentation
663 # if a dialog attempts to set ALLUSERS.
664 whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title,
665 "AdminInstall", "Next", "Cancel")
666 whichusers.title("Select whether to install [ProductName] for all users of this computer.")
667 # A radio group with two options: allusers, justme
668 g = whichusers.radiogroup("AdminInstall", 135, 60, 160, 50, 3,
669 "WhichUsers", "", "Next")
670 g.add("ALL", 0, 5, 150, 20, "Install for all users")
671 g.add("JUSTME", 0, 25, 150, 20, "Install just for me")
672
Tim Peters66cb0182004-08-26 05:23:19 +0000673 whichusers.back("Back", None, active=0)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000674
675 c = whichusers.next("Next >", "Cancel")
676 c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1)
677 c.event("EndDialog", "Return", order = 2)
678
679 c = whichusers.cancel("Cancel", "AdminInstall")
680 c.event("SpawnDialog", "CancelDlg")
Tim Peters66cb0182004-08-26 05:23:19 +0000681
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000682 #####################################################################
683 # Advanced Dialog.
684 advanced = PyDialog(db, "AdvancedDlg", x, y, w, h, modal, title,
685 "CompilePyc", "Next", "Cancel")
686 advanced.title("Advanced Options for [ProductName]")
687 # A radio group with two options: allusers, justme
688 advanced.checkbox("CompilePyc", 135, 60, 230, 50, 3,
689 "COMPILEALL", "Compile .py files to byte code after installation", "Next")
690
691 c = advanced.next("Finish", "Cancel")
692 c.event("EndDialog", "Return")
693
694 c = advanced.cancel("Cancel", "CompilePyc")
695 c.event("SpawnDialog", "CancelDlg")
Tim Peters66cb0182004-08-26 05:23:19 +0000696
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000697 #####################################################################
Tim Peters66cb0182004-08-26 05:23:19 +0000698 # Existing Directory dialog
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000699 dlg = Dialog(db, "ExistingDirectoryDlg", 50, 30, 200, 80, modal, title,
700 "No", "No", "No")
701 dlg.text("Title", 10, 20, 180, 40, 3,
702 "[TARGETDIR] exists. Are you sure you want to overwrite existing files?")
703 c=dlg.pushbutton("Yes", 30, 60, 55, 17, 3, "Yes", "No")
704 c.event("[TargetExists]", "0", order=1)
705 c.event("[TargetExistsOk]", "1", order=2)
706 c.event("EndDialog", "Return", order=3)
707 c=dlg.pushbutton("No", 115, 60, 55, 17, 3, "No", "Yes")
708 c.event("EndDialog", "Return")
709
710 #####################################################################
711 # Installation Progress dialog (modeless)
712 progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title,
713 "Cancel", "Cancel", "Cancel", bitmap=False)
714 progress.text("Title", 20, 15, 200, 15, 0x30003,
715 "{\DlgFontBold8}[Progress1] [ProductName]")
716 progress.text("Text", 35, 65, 300, 30, 3,
717 "Please wait while the Installer [Progress2] [ProductName]. "
718 "This may take several minutes.")
719 progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:")
720
721 c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...")
722 c.mapping("ActionText", "Text")
723
724 #c=progress.text("ActionData", 35, 140, 300, 20, 3, None)
725 #c.mapping("ActionData", "Text")
726
727 c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537,
728 None, "Progress done", None, None)
729 c.mapping("SetProgress", "Progress")
730
731 progress.back("< Back", "Next", active=False)
732 progress.next("Next >", "Cancel", active=False)
733 progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg")
734
735 # Maintenance type: repair/uninstall
736 maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title,
737 "Next", "Next", "Cancel")
738 maint.title("Welcome to the [ProductName] Setup Wizard")
739 maint.text("BodyText", 135, 63, 230, 42, 3,
740 "Select whether you want to repair or remove [ProductName].")
741 g=maint.radiogroup("RepairRadioGroup", 135, 108, 230, 60, 3,
742 "MaintenanceForm_Action", "", "Next")
743 g.add("Change", 0, 0, 200, 17, "&Change [ProductName]")
744 g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]")
745 g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]")
Tim Peters66cb0182004-08-26 05:23:19 +0000746
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000747 maint.back("< Back", None, active=False)
748 c=maint.next("Finish", "Cancel")
749 # Change installation: Change progress dialog to "Change", then ask
750 # for feature selection
751 c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1)
752 c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2)
753
754 # Reinstall: Change progress dialog to "Repair", then invoke reinstall
755 # Also set list of reinstalled features to "ALL"
756 c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5)
757 c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6)
Raymond Hettinger72f08012004-11-07 07:08:25 +0000758 c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000759 c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8)
760
761 # Uninstall: Change progress to "Remove", then invoke uninstall
762 # Also set list of removed features to "ALL"
763 c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11)
764 c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12)
765 c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13)
766 c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14)
767
Tim Peters66cb0182004-08-26 05:23:19 +0000768 # Close dialog when maintenance action scheduled
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000769 c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20)
770 c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21)
Tim Peters66cb0182004-08-26 05:23:19 +0000771
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000772 maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg")
Tim Peters66cb0182004-08-26 05:23:19 +0000773
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000774
775# See "Feature Table". The feature level is 1 for all features,
776# and the feature attributes are 0 for the DefaultFeature, and
777# FollowParent for all other features. The numbers are the Display
778# column.
779def add_features(db):
780 # feature attributes:
781 # msidbFeatureAttributesFollowParent == 2
782 # msidbFeatureAttributesDisallowAdvertise == 8
783 # Features that need to be installed with together with the main feature
784 # (i.e. additional Python libraries) need to follow the parent feature.
785 # Features that have no advertisement trigger (e.g. the test suite)
786 # must not support advertisement
787 global default_feature, tcltk, htmlfiles, tools, testsuite, ext_feature
788 default_feature = Feature(db, "DefaultFeature", "Python",
789 "Python Interpreter and Libraries",
790 1, directory = "TARGETDIR")
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000791 # We don't support advertisement of extensions
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000792 ext_feature = Feature(db, "Extensions", "Register Extensions",
793 "Make this Python installation the default Python installation", 3,
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000794 parent = default_feature, attributes=2|8)
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000795 if have_tcl:
796 tcltk = Feature(db, "TclTk", "Tcl/Tk", "Tkinter, IDLE, pydoc", 5,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000797 parent = default_feature, attributes=2)
798 htmlfiles = Feature(db, "Documentation", "Documentation",
799 "Python HTMLHelp File", 7, parent = default_feature)
800 tools = Feature(db, "Tools", "Utility Scripts",
Tim Peters66cb0182004-08-26 05:23:19 +0000801 "Python utility scripts (Tools/", 9,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000802 parent = default_feature, attributes=2)
803 testsuite = Feature(db, "Testsuite", "Test suite",
804 "Python test suite (Lib/test/)", 11,
805 parent = default_feature, attributes=2|8)
Tim Peters66cb0182004-08-26 05:23:19 +0000806
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000807def extract_msvcr71():
808 import _winreg
809 # Find the location of the merge modules
810 k = _winreg.OpenKey(
811 _winreg.HKEY_LOCAL_MACHINE,
812 r"Software\Microsoft\VisualStudio\7.1\Setup\VS")
813 dir = _winreg.QueryValueEx(k, "MSMDir")[0]
814 _winreg.CloseKey(k)
815 files = glob.glob1(dir, "*CRT71*")
816 assert len(files) == 1
817 file = os.path.join(dir, files[0])
818 # Extract msvcr71.dll
819 m = msilib.MakeMerge2()
820 m.OpenModule(file, 0)
821 m.ExtractFiles(".")
822 m.CloseModule()
823 # Find the version/language of msvcr71.dll
824 installer = msilib.MakeInstaller()
825 return installer.FileVersion("msvcr71.dll", 0), \
826 installer.FileVersion("msvcr71.dll", 1)
827
828class PyDirectory(Directory):
829 """By default, all components in the Python installer
830 can run from source."""
831 def __init__(self, *args, **kw):
832 if not kw.has_key("componentflags"):
833 kw['componentflags'] = 2 #msidbComponentAttributesOptional
834 Directory.__init__(self, *args, **kw)
835
836# See "File Table", "Component Table", "Directory Table",
837# "FeatureComponents Table"
838def add_files(db):
839 cab = CAB("python")
840 tmpfiles = []
841 # Add all executables, icons, text files into the TARGETDIR component
842 root = PyDirectory(db, cab, None, srcdir, "TARGETDIR", "SourceDir")
843 default_feature.set_current()
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000844 if not msilib.Win64:
845 root.add_file("PCBuild/w9xpopen.exe")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000846 root.add_file("PC/py.ico")
847 root.add_file("PC/pyc.ico")
848 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
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000877 # Add all .py files in Lib, except lib-tk, test
878 dirs={}
879 pydirs = [(root,"Lib")]
880 while pydirs:
881 parent, dir = pydirs.pop()
Martin v. Löwis9ca9f562006-01-03 06:29:53 +0000882 if dir == ".svn" or dir.startswith("plat-"):
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000883 continue
884 elif dir in ["lib-tk", "idlelib", "Icons"]:
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000885 if not have_tcl:
886 continue
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000887 tcltk.set_current()
Martin v. Löwis5c9e55e2004-12-30 14:08:18 +0000888 elif dir in ['test', 'tests', 'data', 'output']:
Martin v. Löwis59c3acc2006-04-03 12:07:46 +0000889 # test: Lib, Lib/email, Lib/bsddb, Lib/ctypes, Lib/sqlite3
Martin v. Löwis5c9e55e2004-12-30 14:08:18 +0000890 # tests: Lib/distutils
891 # data: Lib/email/test
892 # output: Lib/test
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000893 testsuite.set_current()
894 else:
895 default_feature.set_current()
896 lib = PyDirectory(db, cab, parent, dir, dir, "%s|%s" % (parent.make_short(dir), dir))
897 # Add additional files
898 dirs[dir]=lib
899 lib.glob("*.txt")
900 if dir=='site-packages':
Martin v. Löwis6d60c092004-11-21 10:16:26 +0000901 lib.add_file("README.txt", src="README")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000902 continue
903 files = lib.glob("*.py")
904 files += lib.glob("*.pyw")
905 if files:
906 # Add an entry to the RemoveFile table to remove bytecode files.
907 lib.remove_pyc()
908 if dir=='test' and parent.physical=='Lib':
909 lib.add_file("185test.db")
910 lib.add_file("audiotest.au")
911 lib.add_file("cfgparser.1")
912 lib.add_file("test.xml")
913 lib.add_file("test.xml.out")
914 lib.add_file("testtar.tar")
Martin v. Löwis7d3755d2004-09-06 06:31:12 +0000915 lib.add_file("test_difflib_expect.html")
Martin v. Löwis59c3acc2006-04-03 12:07:46 +0000916 lib.add_file("check_soundcard.vbs")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000917 lib.glob("*.uue")
918 lib.add_file("readme.txt", src="README")
919 if dir=='decimaltestdata':
920 lib.glob("*.decTest")
921 if dir=='output':
922 lib.glob("test_*")
923 if dir=='idlelib':
924 lib.glob("*.def")
925 lib.add_file("idle.bat")
926 if dir=="Icons":
927 lib.glob("*.gif")
928 lib.add_file("idle.icns")
929 if dir=="command":
930 lib.add_file("wininst-6.exe")
931 lib.add_file("wininst-7.1.exe")
932 if dir=="data" and parent.physical=="test" and parent.basedir.physical=="email":
Martin v. Löwis9ca9f562006-01-03 06:29:53 +0000933 # This should contain all non-.svn files listed in subversion
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000934 for f in os.listdir(lib.absolute):
Martin v. Löwis9ca9f562006-01-03 06:29:53 +0000935 if f.endswith(".txt") or f==".svn":continue
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000936 if f.endswith(".au") or f.endswith(".gif"):
937 lib.add_file(f)
938 else:
939 print "WARNING: New file %s in email/test/data" % f
940 for f in os.listdir(lib.absolute):
941 if os.path.isdir(os.path.join(lib.absolute, f)):
942 pydirs.append((lib, f))
943 # Add DLLs
944 default_feature.set_current()
945 lib = PyDirectory(db, cab, root, srcdir+"/PCBuild", "DLLs", "DLLS|DLLs")
946 dlls = []
947 tclfiles = []
948 for f in extensions:
949 if f=="_tkinter.pyd":
950 continue
951 if not os.path.exists(srcdir+"/PCBuild/"+f):
952 print "WARNING: Missing extension", f
953 continue
954 dlls.append(f)
955 lib.add_file(f)
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000956 if have_tcl:
957 if not os.path.exists(srcdir+"/PCBuild/_tkinter.pyd"):
958 print "WARNING: Missing _tkinter.pyd"
959 else:
960 lib.start_component("TkDLLs", tcltk)
961 lib.add_file("_tkinter.pyd")
962 dlls.append("_tkinter.pyd")
963 tcldir = os.path.normpath(srcdir+"/../tcltk/bin")
964 for f in glob.glob1(tcldir, "*.dll"):
965 lib.add_file(f, src=os.path.join(tcldir, f))
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000966 # check whether there are any unknown extensions
967 for f in glob.glob1(srcdir+"/PCBuild", "*.pyd"):
968 if f.endswith("_d.pyd"): continue # debug version
969 if f in dlls: continue
970 print "WARNING: Unknown extension", f
Tim Peters66cb0182004-08-26 05:23:19 +0000971
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000972 # Add headers
973 default_feature.set_current()
974 lib = PyDirectory(db, cab, root, "include", "include", "INCLUDE|include")
975 lib.glob("*.h")
976 lib.add_file("pyconfig.h", src="../PC/pyconfig.h")
977 # Add import libraries
978 lib = PyDirectory(db, cab, root, "PCBuild", "libs", "LIBS|libs")
979 for f in dlls:
980 lib.add_file(f.replace('pyd','lib'))
981 lib.add_file('python%s%s.lib' % (major, minor))
Martin v. Löwis9fda9312004-12-22 13:41:49 +0000982 # Add the mingw-format library
983 if have_mingw:
Tim Peters5a9fb3c2005-01-07 16:01:32 +0000984 lib.add_file('libpython%s%s.a' % (major, minor))
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000985 if have_tcl:
986 # Add Tcl/Tk
987 tcldirs = [(root, '../tcltk/lib', 'tcl')]
988 tcltk.set_current()
989 while tcldirs:
990 parent, phys, dir = tcldirs.pop()
991 lib = PyDirectory(db, cab, parent, phys, dir, "%s|%s" % (parent.make_short(dir), dir))
992 if not os.path.exists(lib.absolute):
993 continue
994 for f in os.listdir(lib.absolute):
995 if os.path.isdir(os.path.join(lib.absolute, f)):
996 tcldirs.append((lib, f, f))
997 else:
998 lib.add_file(f)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000999 # Add tools
1000 tools.set_current()
1001 tooldir = PyDirectory(db, cab, root, "Tools", "Tools", "TOOLS|Tools")
1002 for f in ['i18n', 'pynche', 'Scripts', 'versioncheck', 'webchecker']:
1003 lib = PyDirectory(db, cab, tooldir, f, f, "%s|%s" % (tooldir.make_short(f), f))
1004 lib.glob("*.py")
1005 lib.glob("*.pyw", exclude=['pydocgui.pyw'])
1006 lib.remove_pyc()
1007 lib.glob("*.txt")
1008 if f == "pynche":
1009 x = PyDirectory(db, cab, lib, "X", "X", "X|X")
1010 x.glob("*.txt")
Martin v. Löwis4d930be2004-12-01 21:46:35 +00001011 if os.path.exists(os.path.join(lib.absolute, "README")):
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001012 lib.add_file("README.txt", src="README")
Martin v. Löwis4d930be2004-12-01 21:46:35 +00001013 if f == 'Scripts':
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001014 if have_tcl:
1015 lib.start_component("pydocgui.pyw", tcltk, keyfile="pydocgui.pyw")
1016 lib.add_file("pydocgui.pyw")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001017 # Add documentation
1018 htmlfiles.set_current()
1019 lib = PyDirectory(db, cab, root, "Doc", "Doc", "DOC|Doc")
1020 lib.start_component("documentation", keyfile="Python%s%s.chm" % (major,minor))
1021 lib.add_file("Python%s%s.chm" % (major, minor))
1022
1023 cab.commit(db)
1024
1025 for f in tmpfiles:
1026 os.unlink(f)
1027
1028# See "Registry Table", "Component Table"
1029def add_registry(db):
1030 # File extensions, associated with the REGISTRY.def component
1031 # IDLE verbs depend on the tcltk feature.
1032 # msidbComponentAttributesRegistryKeyPath = 4
1033 # -1 for Root specifies "dependent on ALLUSERS property"
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001034 tcldata = []
1035 if have_tcl:
1036 tcldata = [
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001037 ("REGISTRY.tcl", msilib.gen_uuid(), "TARGETDIR", 4, None,
1038 "py.IDLE")]
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001039 add_data(db, "Component",
1040 # msidbComponentAttributesRegistryKeyPath = 4
1041 [("REGISTRY", msilib.gen_uuid(), "TARGETDIR", 4, None,
1042 "InstallPath"),
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001043 ("REGISTRY.doc", msilib.gen_uuid(), "TARGETDIR", 4, None,
1044 "Documentation"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001045 ("REGISTRY.def", msilib.gen_uuid(), "TARGETDIR", 4,
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001046 None, None)] + tcldata)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001047 # See "FeatureComponents Table".
1048 # The association between TclTk and pythonw.exe is necessary to make ICE59
1049 # happy, because the installer otherwise believes that the IDLE and PyDoc
1050 # shortcuts might get installed without pythonw.exe being install. This
1051 # is not true, since installing TclTk will install the default feature, which
1052 # will cause pythonw.exe to be installed.
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001053 # REGISTRY.tcl is not associated with any feature, as it will be requested
1054 # through a custom action
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001055 tcldata = []
1056 if have_tcl:
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001057 tcldata = [(tcltk.id, "pythonw.exe")]
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001058 add_data(db, "FeatureComponents",
1059 [(default_feature.id, "REGISTRY"),
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001060 (htmlfiles.id, "REGISTRY.doc"),
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001061 (ext_feature.id, "REGISTRY.def")] +
1062 tcldata
1063 )
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001064 # Extensions are not advertised. For advertised extensions,
1065 # we would need separate binaries that install along with the
1066 # extension.
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001067 pat = r"Software\Classes\%sPython.%sFile\shell\%s\command"
1068 ewi = "Edit with IDLE"
1069 pat2 = r"Software\Classes\%sPython.%sFile\DefaultIcon"
1070 pat3 = r"Software\Classes\%sPython.%sFile"
Martin v. Löwiseac02e62004-11-18 08:00:33 +00001071 tcl_verbs = []
1072 if have_tcl:
1073 tcl_verbs=[
1074 ("py.IDLE", -1, pat % (testprefix, "", ewi), "",
1075 r'"[TARGETDIR]pythonw.exe" "[TARGETDIR]Lib\idlelib\idle.pyw" -n -e "%1"',
1076 "REGISTRY.tcl"),
1077 ("pyw.IDLE", -1, pat % (testprefix, "NoCon", ewi), "",
1078 r'"[TARGETDIR]pythonw.exe" "[TARGETDIR]Lib\idlelib\idle.pyw" -n -e "%1"',
1079 "REGISTRY.tcl"),
1080 ]
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001081 add_data(db, "Registry",
1082 [# Extensions
1083 ("py.ext", -1, r"Software\Classes\."+ext, "",
1084 "Python.File", "REGISTRY.def"),
1085 ("pyw.ext", -1, r"Software\Classes\."+ext+'w', "",
1086 "Python.NoConFile", "REGISTRY.def"),
1087 ("pyc.ext", -1, r"Software\Classes\."+ext+'c', "",
1088 "Python.CompiledFile", "REGISTRY.def"),
1089 ("pyo.ext", -1, r"Software\Classes\."+ext+'o', "",
1090 "Python.CompiledFile", "REGISTRY.def"),
1091 # MIME types
1092 ("py.mime", -1, r"Software\Classes\."+ext, "Content Type",
1093 "text/plain", "REGISTRY.def"),
1094 ("pyw.mime", -1, r"Software\Classes\."+ext+'w', "Content Type",
1095 "text/plain", "REGISTRY.def"),
1096 #Verbs
1097 ("py.open", -1, pat % (testprefix, "", "open"), "",
1098 r'"[TARGETDIR]python.exe" "%1" %*', "REGISTRY.def"),
1099 ("pyw.open", -1, pat % (testprefix, "NoCon", "open"), "",
1100 r'"[TARGETDIR]pythonw.exe" "%1" %*', "REGISTRY.def"),
1101 ("pyc.open", -1, pat % (testprefix, "Compiled", "open"), "",
1102 r'"[TARGETDIR]python.exe" "%1" %*', "REGISTRY.def"),
Martin v. Löwiseac02e62004-11-18 08:00:33 +00001103 ] + tcl_verbs + [
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001104 #Icons
1105 ("py.icon", -1, pat2 % (testprefix, ""), "",
1106 r'[TARGETDIR]py.ico', "REGISTRY.def"),
1107 ("pyw.icon", -1, pat2 % (testprefix, "NoCon"), "",
1108 r'[TARGETDIR]py.ico', "REGISTRY.def"),
1109 ("pyc.icon", -1, pat2 % (testprefix, "Compiled"), "",
1110 r'[TARGETDIR]pyc.ico', "REGISTRY.def"),
1111 # Descriptions
1112 ("py.txt", -1, pat3 % (testprefix, ""), "",
1113 "Python File", "REGISTRY.def"),
1114 ("pyw.txt", -1, pat3 % (testprefix, "NoCon"), "",
1115 "Python File (no console)", "REGISTRY.def"),
1116 ("pyc.txt", -1, pat3 % (testprefix, "Compiled"), "",
1117 "Compiled Python File", "REGISTRY.def"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001118 ])
Tim Peters66cb0182004-08-26 05:23:19 +00001119
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001120 # Registry keys
1121 prefix = r"Software\%sPython\PythonCore\%s" % (testprefix, short_version)
1122 add_data(db, "Registry",
1123 [("InstallPath", -1, prefix+r"\InstallPath", "", "[TARGETDIR]", "REGISTRY"),
1124 ("InstallGroup", -1, prefix+r"\InstallPath\InstallGroup", "",
1125 "Python %s" % short_version, "REGISTRY"),
1126 ("PythonPath", -1, prefix+r"\PythonPath", "",
Martin v. Löwisf13337d2004-09-19 18:36:45 +00001127 r"[TARGETDIR]Lib;[TARGETDIR]DLLs;[TARGETDIR]Lib\lib-tk", "REGISTRY"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001128 ("Documentation", -1, prefix+r"\Help\Main Python Documentation", "",
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001129 r"[TARGETDIR]Doc\Python%s%s.chm" % (major, minor), "REGISTRY.doc"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001130 ("Modules", -1, prefix+r"\Modules", "+", None, "REGISTRY"),
1131 ("AppPaths", -1, r"Software\Microsoft\Windows\CurrentVersion\App Paths\Python.exe",
1132 "", r"[TARGETDIR]Python.exe", "REGISTRY.def")
1133 ])
1134 # Shortcuts, see "Shortcut Table"
1135 add_data(db, "Directory",
1136 [("ProgramMenuFolder", "TARGETDIR", "."),
1137 ("MenuDir", "ProgramMenuFolder", "PY%s%s|%sPython %s.%s" % (major,minor,testprefix,major,minor))])
1138 add_data(db, "RemoveFile",
1139 [("MenuDir", "TARGETDIR", None, "MenuDir", 2)])
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001140 tcltkshortcuts = []
1141 if have_tcl:
1142 tcltkshortcuts = [
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001143 ("IDLE", "MenuDir", "IDLE|IDLE (Python GUI)", "pythonw.exe",
Martin v. Löwisac191da2004-12-22 12:55:44 +00001144 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 +00001145 ("PyDoc", "MenuDir", "MODDOCS|Module Docs", "pythonw.exe",
Martin v. Löwisac191da2004-12-22 12:55:44 +00001146 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 +00001147 ]
1148 add_data(db, "Shortcut",
1149 tcltkshortcuts +
1150 [# Advertised shortcuts: targets are features, not files
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001151 ("Python", "MenuDir", "PYTHON|Python (command line)", "python.exe",
1152 default_feature.id, None, None, None, "python_icon.exe", 2, None, "TARGETDIR"),
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001153 # Advertising the Manual breaks on (some?) Win98, and the shortcut lacks an
1154 # icon first.
1155 #("Manual", "MenuDir", "MANUAL|Python Manuals", "documentation",
1156 # htmlfiles.id, None, None, None, None, None, None, None),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001157 ## Non-advertised shortcuts: must be associated with a registry component
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001158 ("Manual", "MenuDir", "MANUAL|Python Manuals", "REGISTRY.doc",
1159 "[#Python%s%s.chm]" % (major,minor), None,
1160 None, None, None, None, None, None),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001161 ("Uninstall", "MenuDir", "UNINST|Uninstall Python", "REGISTRY",
1162 SystemFolderName+"msiexec", "/x%s" % product_code,
1163 None, None, None, None, None, None),
1164 ])
1165 db.Commit()
1166
1167db = build_database()
1168try:
1169 add_features(db)
1170 add_ui(db)
1171 add_files(db)
1172 add_registry(db)
1173 remove_old_versions(db)
1174 db.Commit()
1175finally:
1176 del db