blob: 7a0ec1d529783e550793d3a4c1b7bfbfc7c1e274 [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']:
889 # test: Lib, Lib/email, Lib/bsddb
890 # 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öwis8ffe9ab2004-08-22 13:34:34 +0000916 lib.glob("*.uue")
917 lib.add_file("readme.txt", src="README")
918 if dir=='decimaltestdata':
919 lib.glob("*.decTest")
920 if dir=='output':
921 lib.glob("test_*")
922 if dir=='idlelib':
923 lib.glob("*.def")
924 lib.add_file("idle.bat")
925 if dir=="Icons":
926 lib.glob("*.gif")
927 lib.add_file("idle.icns")
928 if dir=="command":
929 lib.add_file("wininst-6.exe")
930 lib.add_file("wininst-7.1.exe")
931 if dir=="data" and parent.physical=="test" and parent.basedir.physical=="email":
Martin v. Löwis9ca9f562006-01-03 06:29:53 +0000932 # This should contain all non-.svn files listed in subversion
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000933 for f in os.listdir(lib.absolute):
Martin v. Löwis9ca9f562006-01-03 06:29:53 +0000934 if f.endswith(".txt") or f==".svn":continue
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000935 if f.endswith(".au") or f.endswith(".gif"):
936 lib.add_file(f)
937 else:
938 print "WARNING: New file %s in email/test/data" % f
939 for f in os.listdir(lib.absolute):
940 if os.path.isdir(os.path.join(lib.absolute, f)):
941 pydirs.append((lib, f))
942 # Add DLLs
943 default_feature.set_current()
944 lib = PyDirectory(db, cab, root, srcdir+"/PCBuild", "DLLs", "DLLS|DLLs")
945 dlls = []
946 tclfiles = []
947 for f in extensions:
948 if f=="_tkinter.pyd":
949 continue
950 if not os.path.exists(srcdir+"/PCBuild/"+f):
951 print "WARNING: Missing extension", f
952 continue
953 dlls.append(f)
954 lib.add_file(f)
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000955 if have_tcl:
956 if not os.path.exists(srcdir+"/PCBuild/_tkinter.pyd"):
957 print "WARNING: Missing _tkinter.pyd"
958 else:
959 lib.start_component("TkDLLs", tcltk)
960 lib.add_file("_tkinter.pyd")
961 dlls.append("_tkinter.pyd")
962 tcldir = os.path.normpath(srcdir+"/../tcltk/bin")
963 for f in glob.glob1(tcldir, "*.dll"):
964 lib.add_file(f, src=os.path.join(tcldir, f))
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000965 # check whether there are any unknown extensions
966 for f in glob.glob1(srcdir+"/PCBuild", "*.pyd"):
967 if f.endswith("_d.pyd"): continue # debug version
968 if f in dlls: continue
969 print "WARNING: Unknown extension", f
Tim Peters66cb0182004-08-26 05:23:19 +0000970
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000971 # Add headers
972 default_feature.set_current()
973 lib = PyDirectory(db, cab, root, "include", "include", "INCLUDE|include")
974 lib.glob("*.h")
975 lib.add_file("pyconfig.h", src="../PC/pyconfig.h")
976 # Add import libraries
977 lib = PyDirectory(db, cab, root, "PCBuild", "libs", "LIBS|libs")
978 for f in dlls:
979 lib.add_file(f.replace('pyd','lib'))
980 lib.add_file('python%s%s.lib' % (major, minor))
Martin v. Löwis9fda9312004-12-22 13:41:49 +0000981 # Add the mingw-format library
982 if have_mingw:
Tim Peters5a9fb3c2005-01-07 16:01:32 +0000983 lib.add_file('libpython%s%s.a' % (major, minor))
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000984 if have_tcl:
985 # Add Tcl/Tk
986 tcldirs = [(root, '../tcltk/lib', 'tcl')]
987 tcltk.set_current()
988 while tcldirs:
989 parent, phys, dir = tcldirs.pop()
990 lib = PyDirectory(db, cab, parent, phys, dir, "%s|%s" % (parent.make_short(dir), dir))
991 if not os.path.exists(lib.absolute):
992 continue
993 for f in os.listdir(lib.absolute):
994 if os.path.isdir(os.path.join(lib.absolute, f)):
995 tcldirs.append((lib, f, f))
996 else:
997 lib.add_file(f)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000998 # Add tools
999 tools.set_current()
1000 tooldir = PyDirectory(db, cab, root, "Tools", "Tools", "TOOLS|Tools")
1001 for f in ['i18n', 'pynche', 'Scripts', 'versioncheck', 'webchecker']:
1002 lib = PyDirectory(db, cab, tooldir, f, f, "%s|%s" % (tooldir.make_short(f), f))
1003 lib.glob("*.py")
1004 lib.glob("*.pyw", exclude=['pydocgui.pyw'])
1005 lib.remove_pyc()
1006 lib.glob("*.txt")
1007 if f == "pynche":
1008 x = PyDirectory(db, cab, lib, "X", "X", "X|X")
1009 x.glob("*.txt")
Martin v. Löwis4d930be2004-12-01 21:46:35 +00001010 if os.path.exists(os.path.join(lib.absolute, "README")):
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001011 lib.add_file("README.txt", src="README")
Martin v. Löwis4d930be2004-12-01 21:46:35 +00001012 if f == 'Scripts':
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001013 if have_tcl:
1014 lib.start_component("pydocgui.pyw", tcltk, keyfile="pydocgui.pyw")
1015 lib.add_file("pydocgui.pyw")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001016 # Add documentation
1017 htmlfiles.set_current()
1018 lib = PyDirectory(db, cab, root, "Doc", "Doc", "DOC|Doc")
1019 lib.start_component("documentation", keyfile="Python%s%s.chm" % (major,minor))
1020 lib.add_file("Python%s%s.chm" % (major, minor))
1021
1022 cab.commit(db)
1023
1024 for f in tmpfiles:
1025 os.unlink(f)
1026
1027# See "Registry Table", "Component Table"
1028def add_registry(db):
1029 # File extensions, associated with the REGISTRY.def component
1030 # IDLE verbs depend on the tcltk feature.
1031 # msidbComponentAttributesRegistryKeyPath = 4
1032 # -1 for Root specifies "dependent on ALLUSERS property"
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001033 tcldata = []
1034 if have_tcl:
1035 tcldata = [
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001036 ("REGISTRY.tcl", msilib.gen_uuid(), "TARGETDIR", 4, None,
1037 "py.IDLE")]
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001038 add_data(db, "Component",
1039 # msidbComponentAttributesRegistryKeyPath = 4
1040 [("REGISTRY", msilib.gen_uuid(), "TARGETDIR", 4, None,
1041 "InstallPath"),
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001042 ("REGISTRY.doc", msilib.gen_uuid(), "TARGETDIR", 4, None,
1043 "Documentation"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001044 ("REGISTRY.def", msilib.gen_uuid(), "TARGETDIR", 4,
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001045 None, None)] + tcldata)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001046 # See "FeatureComponents Table".
1047 # The association between TclTk and pythonw.exe is necessary to make ICE59
1048 # happy, because the installer otherwise believes that the IDLE and PyDoc
1049 # shortcuts might get installed without pythonw.exe being install. This
1050 # is not true, since installing TclTk will install the default feature, which
1051 # will cause pythonw.exe to be installed.
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001052 # REGISTRY.tcl is not associated with any feature, as it will be requested
1053 # through a custom action
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001054 tcldata = []
1055 if have_tcl:
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001056 tcldata = [(tcltk.id, "pythonw.exe")]
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001057 add_data(db, "FeatureComponents",
1058 [(default_feature.id, "REGISTRY"),
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001059 (htmlfiles.id, "REGISTRY.doc"),
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001060 (ext_feature.id, "REGISTRY.def")] +
1061 tcldata
1062 )
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001063 # Extensions are not advertised. For advertised extensions,
1064 # we would need separate binaries that install along with the
1065 # extension.
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001066 pat = r"Software\Classes\%sPython.%sFile\shell\%s\command"
1067 ewi = "Edit with IDLE"
1068 pat2 = r"Software\Classes\%sPython.%sFile\DefaultIcon"
1069 pat3 = r"Software\Classes\%sPython.%sFile"
Martin v. Löwiseac02e62004-11-18 08:00:33 +00001070 tcl_verbs = []
1071 if have_tcl:
1072 tcl_verbs=[
1073 ("py.IDLE", -1, pat % (testprefix, "", ewi), "",
1074 r'"[TARGETDIR]pythonw.exe" "[TARGETDIR]Lib\idlelib\idle.pyw" -n -e "%1"',
1075 "REGISTRY.tcl"),
1076 ("pyw.IDLE", -1, pat % (testprefix, "NoCon", ewi), "",
1077 r'"[TARGETDIR]pythonw.exe" "[TARGETDIR]Lib\idlelib\idle.pyw" -n -e "%1"',
1078 "REGISTRY.tcl"),
1079 ]
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001080 add_data(db, "Registry",
1081 [# Extensions
1082 ("py.ext", -1, r"Software\Classes\."+ext, "",
1083 "Python.File", "REGISTRY.def"),
1084 ("pyw.ext", -1, r"Software\Classes\."+ext+'w', "",
1085 "Python.NoConFile", "REGISTRY.def"),
1086 ("pyc.ext", -1, r"Software\Classes\."+ext+'c', "",
1087 "Python.CompiledFile", "REGISTRY.def"),
1088 ("pyo.ext", -1, r"Software\Classes\."+ext+'o', "",
1089 "Python.CompiledFile", "REGISTRY.def"),
1090 # MIME types
1091 ("py.mime", -1, r"Software\Classes\."+ext, "Content Type",
1092 "text/plain", "REGISTRY.def"),
1093 ("pyw.mime", -1, r"Software\Classes\."+ext+'w', "Content Type",
1094 "text/plain", "REGISTRY.def"),
1095 #Verbs
1096 ("py.open", -1, pat % (testprefix, "", "open"), "",
1097 r'"[TARGETDIR]python.exe" "%1" %*', "REGISTRY.def"),
1098 ("pyw.open", -1, pat % (testprefix, "NoCon", "open"), "",
1099 r'"[TARGETDIR]pythonw.exe" "%1" %*', "REGISTRY.def"),
1100 ("pyc.open", -1, pat % (testprefix, "Compiled", "open"), "",
1101 r'"[TARGETDIR]python.exe" "%1" %*', "REGISTRY.def"),
Martin v. Löwiseac02e62004-11-18 08:00:33 +00001102 ] + tcl_verbs + [
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001103 #Icons
1104 ("py.icon", -1, pat2 % (testprefix, ""), "",
1105 r'[TARGETDIR]py.ico', "REGISTRY.def"),
1106 ("pyw.icon", -1, pat2 % (testprefix, "NoCon"), "",
1107 r'[TARGETDIR]py.ico', "REGISTRY.def"),
1108 ("pyc.icon", -1, pat2 % (testprefix, "Compiled"), "",
1109 r'[TARGETDIR]pyc.ico', "REGISTRY.def"),
1110 # Descriptions
1111 ("py.txt", -1, pat3 % (testprefix, ""), "",
1112 "Python File", "REGISTRY.def"),
1113 ("pyw.txt", -1, pat3 % (testprefix, "NoCon"), "",
1114 "Python File (no console)", "REGISTRY.def"),
1115 ("pyc.txt", -1, pat3 % (testprefix, "Compiled"), "",
1116 "Compiled Python File", "REGISTRY.def"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001117 ])
Tim Peters66cb0182004-08-26 05:23:19 +00001118
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001119 # Registry keys
1120 prefix = r"Software\%sPython\PythonCore\%s" % (testprefix, short_version)
1121 add_data(db, "Registry",
1122 [("InstallPath", -1, prefix+r"\InstallPath", "", "[TARGETDIR]", "REGISTRY"),
1123 ("InstallGroup", -1, prefix+r"\InstallPath\InstallGroup", "",
1124 "Python %s" % short_version, "REGISTRY"),
1125 ("PythonPath", -1, prefix+r"\PythonPath", "",
Martin v. Löwisf13337d2004-09-19 18:36:45 +00001126 r"[TARGETDIR]Lib;[TARGETDIR]DLLs;[TARGETDIR]Lib\lib-tk", "REGISTRY"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001127 ("Documentation", -1, prefix+r"\Help\Main Python Documentation", "",
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001128 r"[TARGETDIR]Doc\Python%s%s.chm" % (major, minor), "REGISTRY.doc"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001129 ("Modules", -1, prefix+r"\Modules", "+", None, "REGISTRY"),
1130 ("AppPaths", -1, r"Software\Microsoft\Windows\CurrentVersion\App Paths\Python.exe",
1131 "", r"[TARGETDIR]Python.exe", "REGISTRY.def")
1132 ])
1133 # Shortcuts, see "Shortcut Table"
1134 add_data(db, "Directory",
1135 [("ProgramMenuFolder", "TARGETDIR", "."),
1136 ("MenuDir", "ProgramMenuFolder", "PY%s%s|%sPython %s.%s" % (major,minor,testprefix,major,minor))])
1137 add_data(db, "RemoveFile",
1138 [("MenuDir", "TARGETDIR", None, "MenuDir", 2)])
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001139 tcltkshortcuts = []
1140 if have_tcl:
1141 tcltkshortcuts = [
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001142 ("IDLE", "MenuDir", "IDLE|IDLE (Python GUI)", "pythonw.exe",
Martin v. Löwisac191da2004-12-22 12:55:44 +00001143 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 +00001144 ("PyDoc", "MenuDir", "MODDOCS|Module Docs", "pythonw.exe",
Martin v. Löwisac191da2004-12-22 12:55:44 +00001145 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 +00001146 ]
1147 add_data(db, "Shortcut",
1148 tcltkshortcuts +
1149 [# Advertised shortcuts: targets are features, not files
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001150 ("Python", "MenuDir", "PYTHON|Python (command line)", "python.exe",
1151 default_feature.id, None, None, None, "python_icon.exe", 2, None, "TARGETDIR"),
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001152 # Advertising the Manual breaks on (some?) Win98, and the shortcut lacks an
1153 # icon first.
1154 #("Manual", "MenuDir", "MANUAL|Python Manuals", "documentation",
1155 # htmlfiles.id, None, None, None, None, None, None, None),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001156 ## Non-advertised shortcuts: must be associated with a registry component
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001157 ("Manual", "MenuDir", "MANUAL|Python Manuals", "REGISTRY.doc",
1158 "[#Python%s%s.chm]" % (major,minor), None,
1159 None, None, None, None, None, None),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001160 ("Uninstall", "MenuDir", "UNINST|Uninstall Python", "REGISTRY",
1161 SystemFolderName+"msiexec", "/x%s" % product_code,
1162 None, None, None, None, None, None),
1163 ])
1164 db.Commit()
1165
1166db = build_database()
1167try:
1168 add_features(db)
1169 add_ui(db)
1170 add_files(db)
1171 add_registry(db)
1172 remove_old_versions(db)
1173 db.Commit()
1174finally:
1175 del db