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