blob: a0d70dea92ebb5652287a20c77fb0127f20e52e1 [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.
Christian Heimes9acba042007-12-04 14:57:30 +00004import msilib, schema, sequence, os, glob, time, re, shutil
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öwis34360642010-03-16 18:49:28 +000010import tempfile
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +000011
12# Settings can be overridden in config.py below
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +000013# 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
Christian Heimes9acba042007-12-04 14:57:30 +000028# path to PCbuild directory
Martin v. Löwise7a434e2008-01-06 11:03:43 +000029PCBUILD="PCbuild"
Christian Heimes9acba042007-12-04 14:57:30 +000030# msvcrt version
Martin v. Löwise7a434e2008-01-06 11:03:43 +000031MSVCR = "90"
Martin v. Löwis34360642010-03-16 18:49:28 +000032# Name of certificate in default store to sign MSI with
33certname = None
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +000034
35try:
36 from config import *
37except ImportError:
38 pass
39
40# Extract current version from Include/patchlevel.h
41lines = open(srcdir + "/Include/patchlevel.h").readlines()
42major = minor = micro = level = serial = None
43levels = {
44 'PY_RELEASE_LEVEL_ALPHA':0xA,
45 'PY_RELEASE_LEVEL_BETA': 0xB,
46 'PY_RELEASE_LEVEL_GAMMA':0xC,
47 'PY_RELEASE_LEVEL_FINAL':0xF
48 }
49for l in lines:
50 if not l.startswith("#define"):
51 continue
52 l = l.split()
53 if len(l) != 3:
54 continue
55 _, name, value = l
56 if name == 'PY_MAJOR_VERSION': major = value
57 if name == 'PY_MINOR_VERSION': minor = value
58 if name == 'PY_MICRO_VERSION': micro = value
59 if name == 'PY_RELEASE_LEVEL': level = levels[value]
60 if name == 'PY_RELEASE_SERIAL': serial = value
61
62short_version = major+"."+minor
63# See PC/make_versioninfo.c
64FIELD3 = 1000*int(micro) + 10*level + int(serial)
65current_version = "%s.%d" % (short_version, FIELD3)
66
67# This should never change. The UpgradeCode of this package can be
68# used in the Upgrade table of future packages to make the future
69# package replace this one. See "UpgradeCode Property".
Martin v. Löwisab0b29b2008-09-13 08:11:57 +000070# upgrade_code gets set to upgrade_code_64 when we have determined
71# that the target is Win64.
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +000072upgrade_code_snapshot='{92A24481-3ECB-40FC-8836-04B7966EC0D5}'
73upgrade_code='{65E6DE48-A358-434D-AA4F-4AF72DB4718F}'
Martin v. Löwisab0b29b2008-09-13 08:11:57 +000074upgrade_code_64='{6A965A0C-6EE6-4E3A-9983-3263F56311EC}'
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +000075
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +000076if snapshot:
77 current_version = "%s.%s.%s" % (major, minor, int(time.time()/3600/24))
78 product_code = msilib.gen_uuid()
79else:
80 product_code = product_codes[current_version]
81
82if full_current_version is None:
83 full_current_version = current_version
84
85extensions = [
86 'bz2.pyd',
87 'pyexpat.pyd',
88 'select.pyd',
89 'unicodedata.pyd',
90 'winsound.pyd',
Trent Micke97e5a72005-12-15 22:08:46 +000091 '_elementtree.pyd',
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +000092 '_bsddb.pyd',
93 '_socket.pyd',
94 '_ssl.pyd',
95 '_testcapi.pyd',
96 '_tkinter.pyd',
Martin v. Löwis8c7c56e2006-03-05 14:04:26 +000097 '_msi.pyd',
Martin v. Löwisa09655e2006-03-10 15:36:28 +000098 '_ctypes.pyd',
Martin v. Löwis1a494bd2006-04-04 07:10:59 +000099 '_ctypes_test.pyd',
Martin v. Löwisa09fd6e2006-08-16 12:55:10 +0000100 '_sqlite3.pyd',
Martin v. Löwis1d2ce452008-06-30 07:01:09 +0000101 '_hashlib.pyd',
102 '_multiprocessing.pyd'
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000103]
104
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000105# Well-known component UUIDs
106# These are needed for SharedDLLs reference counter; if
107# a different UUID was used for each incarnation of, say,
108# python24.dll, an upgrade would set the reference counter
109# from 1 to 2 (due to what I consider a bug in MSI)
110# Using the same UUID is fine since these files are versioned,
111# so Installer will always keep the newest version.
Christian Heimes9acba042007-12-04 14:57:30 +0000112# NOTE: All uuids are self generated.
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000113pythondll_uuid = {
114 "24":"{9B81E618-2301-4035-AC77-75D9ABEB7301}",
Martin v. Löwis5409c8d2007-08-30 18:15:22 +0000115 "25":"{2e41b118-38bd-4c1b-a840-6977efd1b911}",
Martin v. Löwisbe7abbb2007-08-14 05:01:50 +0000116 "26":"{34ebecac-f046-4e1c-b0e3-9bac3cdaacfa}",
Martin v. Löwis62d45382008-10-02 20:04:47 +0000117 "27":"{4fe21c76-1760-437b-a2f2-99909130a175}",
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000118 } [major+minor]
Tim Peterseba28be2005-03-28 01:08:02 +0000119
Martin v. Löwis344d0662008-05-09 18:21:55 +0000120# Compute the name that Sphinx gives to the docfile
121docfile = ""
Martin v. Löwis741e6992009-06-28 12:24:23 +0000122if int(micro):
123 docfile = micro
Martin v. Löwis344d0662008-05-09 18:21:55 +0000124if level < 0xf:
Benjamin Peterson0805b282009-06-13 15:48:04 +0000125 if level == 0xC:
Martin v. Löwis741e6992009-06-28 12:24:23 +0000126 docfile += "rc%s" % (serial,)
Benjamin Peterson0805b282009-06-13 15:48:04 +0000127 else:
Martin v. Löwis741e6992009-06-28 12:24:23 +0000128 docfile += '%x%s' % (level, serial)
Martin v. Löwis344d0662008-05-09 18:21:55 +0000129docfile = 'python%s%s%s.chm' % (major, minor, docfile)
130
Martin v. Löwis9fda9312004-12-22 13:41:49 +0000131# Build the mingw import library, libpythonXY.a
132# This requires 'nm' and 'dlltool' executables on your PATH
133def build_mingw_lib(lib_file, def_file, dll_file, mingw_lib):
134 warning = "WARNING: %s - libpythonXX.a not built"
135 nm = find_executable('nm')
136 dlltool = find_executable('dlltool')
137
138 if not nm or not dlltool:
139 print warning % "nm and/or dlltool were not found"
140 return False
141
142 nm_command = '%s -Cs %s' % (nm, lib_file)
143 dlltool_command = "%s --dllname %s --def %s --output-lib %s" % \
144 (dlltool, dll_file, def_file, mingw_lib)
145 export_match = re.compile(r"^_imp__(.*) in python\d+\.dll").match
146
147 f = open(def_file,'w')
148 print >>f, "LIBRARY %s" % dll_file
149 print >>f, "EXPORTS"
150
151 nm_pipe = os.popen(nm_command)
152 for line in nm_pipe.readlines():
153 m = export_match(line)
154 if m:
155 print >>f, m.group(1)
156 f.close()
157 exit = nm_pipe.close()
158
159 if exit:
160 print warning % "nm did not run successfully"
161 return False
162
163 if os.system(dlltool_command) != 0:
164 print warning % "dlltool did not run successfully"
165 return False
166
167 return True
168
169# Target files (.def and .a) go in PCBuild directory
Christian Heimes9acba042007-12-04 14:57:30 +0000170lib_file = os.path.join(srcdir, PCBUILD, "python%s%s.lib" % (major, minor))
171def_file = os.path.join(srcdir, PCBUILD, "python%s%s.def" % (major, minor))
Martin v. Löwis9fda9312004-12-22 13:41:49 +0000172dll_file = "python%s%s.dll" % (major, minor)
Christian Heimes9acba042007-12-04 14:57:30 +0000173mingw_lib = os.path.join(srcdir, PCBUILD, "libpython%s%s.a" % (major, minor))
Martin v. Löwis9fda9312004-12-22 13:41:49 +0000174
175have_mingw = build_mingw_lib(lib_file, def_file, dll_file, mingw_lib)
176
Martin v. Löwis856bf9a2006-02-14 20:42:55 +0000177# Determine the target architechture
Christian Heimes9acba042007-12-04 14:57:30 +0000178dll_path = os.path.join(srcdir, PCBUILD, dll_file)
Martin v. Löwis856bf9a2006-02-14 20:42:55 +0000179msilib.set_arch_from_file(dll_path)
180if msilib.pe_type(dll_path) != msilib.pe_type("msisupport.dll"):
181 raise SystemError, "msisupport.dll for incorrect architecture"
Martin v. Löwisab0b29b2008-09-13 08:11:57 +0000182if msilib.Win64:
183 upgrade_code = upgrade_code_64
Martin v. Löwisa9aff012008-09-13 08:36:22 +0000184 # Bump the last digit of the code by one, so that 32-bit and 64-bit
185 # releases get separate product codes
186 digit = hex((int(product_code[-2],16)+1)%16)[-1]
187 product_code = product_code[:-2] + digit + '}'
Martin v. Löwis856bf9a2006-02-14 20:42:55 +0000188
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000189if testpackage:
190 ext = 'px'
191 testprefix = 'x'
192else:
193 ext = 'py'
194 testprefix = ''
195
196if msilib.Win64:
Martin v. Löwis75c23bd2007-08-30 18:25:47 +0000197 SystemFolderName = "[System64Folder]"
Martin v. Löwis1ab4a1f2007-08-31 10:01:07 +0000198 registry_component = 4|256
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000199else:
200 SystemFolderName = "[SystemFolder]"
Martin v. Löwis1ab4a1f2007-08-31 10:01:07 +0000201 registry_component = 4
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000202
203msilib.reset()
204
205# condition in which to install pythonxy.dll in system32:
206# a) it is Windows 9x or
207# b) it is NT, the user is privileged, and has chosen per-machine installation
208sys32cond = "(Windows9x or (Privileged and ALLUSERS))"
209
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000210def build_database():
211 """Generate an empty database, with just the schema and the
212 Summary information stream."""
213 if snapshot:
214 uc = upgrade_code_snapshot
215 else:
216 uc = upgrade_code
Martin v. Löwisa9aff012008-09-13 08:36:22 +0000217 if msilib.Win64:
218 productsuffix = " (64-bit)"
219 else:
220 productsuffix = ""
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000221 # schema represents the installer 2.0 database schema.
222 # sequence is the set of standard sequences
223 # (ui/execute, admin/advt/install)
Martin v. Löwis34360642010-03-16 18:49:28 +0000224 msiname = "python-%s%s.msi" % (full_current_version, msilib.arch_ext)
225 db = msilib.init_database(msiname,
Martin v. Löwisa9aff012008-09-13 08:36:22 +0000226 schema, ProductName="Python "+full_current_version+productsuffix,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000227 ProductCode=product_code,
228 ProductVersion=current_version,
Martin v. Löwiseba774b2008-10-13 11:23:35 +0000229 Manufacturer=u"Python Software Foundation",
230 request_uac = True)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000231 # The default sequencing of the RemoveExistingProducts action causes
232 # removal of files that got just installed. Place it after
233 # InstallInitialize, so we first uninstall everything, but still roll
234 # back in case the installation is interrupted
235 msilib.change_sequence(sequence.InstallExecuteSequence,
236 "RemoveExistingProducts", 1510)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000237 msilib.add_tables(db, sequence)
238 # We cannot set ALLUSERS in the property table, as this cannot be
239 # reset if the user choses a per-user installation. Instead, we
240 # maintain WhichUsers, which can be "ALL" or "JUSTME". The UI manages
241 # this property, and when the execution starts, ALLUSERS is set
242 # accordingly.
243 add_data(db, "Property", [("UpgradeCode", uc),
244 ("WhichUsers", "ALL"),
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000245 ("ProductLine", "Python%s%s" % (major, minor)),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000246 ])
247 db.Commit()
Martin v. Löwis34360642010-03-16 18:49:28 +0000248 return db, msiname
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000249
250def remove_old_versions(db):
251 "Fill the upgrade table."
252 start = "%s.%s.0" % (major, minor)
253 # This requests that feature selection states of an older
254 # installation should be forwarded into this one. Upgrading
255 # requires that both the old and the new installation are
256 # either both per-machine or per-user.
257 migrate_features = 1
258 # See "Upgrade Table". We remove releases with the same major and
259 # minor version. For an snapshot, we remove all earlier snapshots. For
260 # a release, we remove all snapshots, and all earlier releases.
261 if snapshot:
262 add_data(db, "Upgrade",
Tim Peters66cb0182004-08-26 05:23:19 +0000263 [(upgrade_code_snapshot, start,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000264 current_version,
265 None, # Ignore language
Tim Peters66cb0182004-08-26 05:23:19 +0000266 migrate_features,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000267 None, # Migrate ALL features
268 "REMOVEOLDSNAPSHOT")])
269 props = "REMOVEOLDSNAPSHOT"
270 else:
271 add_data(db, "Upgrade",
272 [(upgrade_code, start, current_version,
273 None, migrate_features, None, "REMOVEOLDVERSION"),
274 (upgrade_code_snapshot, start, "%s.%d.0" % (major, int(minor)+1),
275 None, migrate_features, None, "REMOVEOLDSNAPSHOT")])
276 props = "REMOVEOLDSNAPSHOT;REMOVEOLDVERSION"
Martin v. Löwis3f5fda82008-09-08 13:50:10 +0000277
278 props += ";TARGETDIR;DLLDIR"
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000279 # Installer collects the product codes of the earlier releases in
280 # these properties. In order to allow modification of the properties,
281 # they must be declared as secure. See "SecureCustomProperties Property"
282 add_data(db, "Property", [("SecureCustomProperties", props)])
283
284class PyDialog(Dialog):
285 """Dialog class with a fixed layout: controls at the top, then a ruler,
286 then a list of buttons: back, next, cancel. Optionally a bitmap at the
287 left."""
288 def __init__(self, *args, **kw):
289 """Dialog(database, name, x, y, w, h, attributes, title, first,
290 default, cancel, bitmap=true)"""
291 Dialog.__init__(self, *args)
292 ruler = self.h - 36
293 bmwidth = 152*ruler/328
294 if kw.get("bitmap", True):
295 self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin")
296 self.line("BottomLine", 0, ruler, self.w, 0)
297
298 def title(self, title):
299 "Set the title text of the dialog at the top."
300 # name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix,
301 # text, in VerdanaBold10
302 self.text("Title", 135, 10, 220, 60, 0x30003,
303 r"{\VerdanaBold10}%s" % title)
304
305 def back(self, title, next, name = "Back", active = 1):
306 """Add a back button with a given title, the tab-next button,
307 its name in the Control table, possibly initially disabled.
308
309 Return the button, so that events can be associated"""
310 if active:
311 flags = 3 # Visible|Enabled
312 else:
313 flags = 1 # Visible
314 return self.pushbutton(name, 180, self.h-27 , 56, 17, flags, title, next)
315
316 def cancel(self, title, next, name = "Cancel", active = 1):
317 """Add a cancel button with a given title, the tab-next button,
318 its name in the Control table, possibly initially disabled.
319
320 Return the button, so that events can be associated"""
321 if active:
322 flags = 3 # Visible|Enabled
323 else:
324 flags = 1 # Visible
325 return self.pushbutton(name, 304, self.h-27, 56, 17, flags, title, next)
326
327 def next(self, title, next, name = "Next", active = 1):
328 """Add a Next button with a given title, the tab-next button,
329 its name in the Control table, possibly initially disabled.
330
331 Return the button, so that events can be associated"""
332 if active:
333 flags = 3 # Visible|Enabled
334 else:
335 flags = 1 # Visible
336 return self.pushbutton(name, 236, self.h-27, 56, 17, flags, title, next)
337
338 def xbutton(self, name, title, next, xpos):
339 """Add a button with a given title, the tab-next button,
340 its name in the Control table, giving its x position; the
341 y-position is aligned with the other buttons.
342
343 Return the button, so that events can be associated"""
344 return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next)
345
346def add_ui(db):
347 x = y = 50
348 w = 370
349 h = 300
350 title = "[ProductName] Setup"
351
352 # see "Dialog Style Bits"
353 modal = 3 # visible | modal
354 modeless = 1 # visible
355 track_disk_space = 32
356
357 add_data(db, 'ActionText', uisample.ActionText)
358 add_data(db, 'UIText', uisample.UIText)
359
360 # Bitmaps
361 if not os.path.exists(srcdir+r"\PC\python_icon.exe"):
362 raise "Run icons.mak in PC directory"
363 add_data(db, "Binary",
Christian Heimes7e28e492008-01-01 13:52:57 +0000364 [("PythonWin", msilib.Binary(r"%s\PCbuild\installer.bmp" % srcdir)), # 152x328 pixels
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000365 ("py.ico",msilib.Binary(srcdir+r"\PC\py.ico")),
366 ])
367 add_data(db, "Icon",
368 [("python_icon.exe", msilib.Binary(srcdir+r"\PC\python_icon.exe"))])
369
370 # Scripts
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000371 # CheckDir sets TargetExists if TARGETDIR exists.
372 # UpdateEditIDLE sets the REGISTRY.tcl component into
373 # the installed/uninstalled state according to both the
374 # Extensions and TclTk features.
Martin v. Löwiseb68be42004-12-12 15:29:21 +0000375 if os.system("nmake /nologo /c /f msisupport.mak") != 0:
376 raise "'nmake /f msisupport.mak' failed"
377 add_data(db, "Binary", [("Script", msilib.Binary("msisupport.dll"))])
378 # See "Custom Action Type 1"
Martin v. Löwis3390d332005-03-14 17:20:13 +0000379 if msilib.Win64:
380 CheckDir = "CheckDir"
Martin v. Löwisdf40ce32006-02-16 14:38:30 +0000381 UpdateEditIDLE = "UpdateEditIDLE"
Martin v. Löwis3390d332005-03-14 17:20:13 +0000382 else:
383 CheckDir = "_CheckDir@4"
384 UpdateEditIDLE = "_UpdateEditIDLE@4"
Tim Peters0e9980f2004-09-12 03:49:31 +0000385 add_data(db, "CustomAction",
Martin v. Löwis3390d332005-03-14 17:20:13 +0000386 [("CheckDir", 1, "Script", CheckDir)])
Martin v. Löwiseac02e62004-11-18 08:00:33 +0000387 if have_tcl:
388 add_data(db, "CustomAction",
Martin v. Löwis3390d332005-03-14 17:20:13 +0000389 [("UpdateEditIDLE", 1, "Script", UpdateEditIDLE)])
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000390
391 # UI customization properties
392 add_data(db, "Property",
393 # See "DefaultUIFont Property"
394 [("DefaultUIFont", "DlgFont8"),
395 # See "ErrorDialog Style Bit"
396 ("ErrorDialog", "ErrorDlg"),
397 ("Progress1", "Install"), # modified in maintenance type dlg
398 ("Progress2", "installs"),
399 ("MaintenanceForm_Action", "Repair")])
400
401 # Fonts, see "TextStyle Table"
402 add_data(db, "TextStyle",
403 [("DlgFont8", "Tahoma", 9, None, 0),
404 ("DlgFontBold8", "Tahoma", 8, None, 1), #bold
405 ("VerdanaBold10", "Verdana", 10, None, 1),
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000406 ("VerdanaRed9", "Verdana", 9, 255, 0),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000407 ])
408
Martin v. Löwis24e4e162010-03-15 13:00:17 +0000409 compileargs = r'-Wi "[TARGETDIR]Lib\compileall.py" -f -x "bad_coding|badsyntax|site-packages|py3_" "[TARGETDIR]Lib"'
Martin v. Löwis367c79a2008-05-25 16:37:34 +0000410 lib2to3args = r'-c "import lib2to3.pygram, lib2to3.patcomp;lib2to3.patcomp.PatternCompiler()"'
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000411 # See "CustomAction Table"
412 add_data(db, "CustomAction", [
413 # msidbCustomActionTypeFirstSequence + msidbCustomActionTypeTextData + msidbCustomActionTypeProperty
414 # See "Custom Action Type 51",
415 # "Custom Action Execution Scheduling Options"
416 ("InitialTargetDir", 307, "TARGETDIR",
417 "[WindowsVolume]Python%s%s" % (major, minor)),
418 ("SetDLLDirToTarget", 307, "DLLDIR", "[TARGETDIR]"),
419 ("SetDLLDirToSystem32", 307, "DLLDIR", SystemFolderName),
420 # msidbCustomActionTypeExe + msidbCustomActionTypeSourceFile
421 # See "Custom Action Type 18"
Martin v. Löwis7b2563b2004-11-02 22:59:56 +0000422 ("CompilePyc", 18, "python.exe", compileargs),
423 ("CompilePyo", 18, "python.exe", "-O "+compileargs),
Martin v. Löwis367c79a2008-05-25 16:37:34 +0000424 ("CompileGrammar", 18, "python.exe", lib2to3args),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000425 ])
426
427 # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table"
428 # Numbers indicate sequence; see sequence.py for how these action integrate
429 add_data(db, "InstallUISequence",
430 [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140),
431 ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141),
432 ("InitialTargetDir", 'TARGETDIR=""', 750),
433 # In the user interface, assume all-users installation if privileged.
434 ("SetDLLDirToSystem32", 'DLLDIR="" and ' + sys32cond, 751),
435 ("SetDLLDirToTarget", 'DLLDIR="" and not ' + sys32cond, 752),
436 ("SelectDirectoryDlg", "Not Installed", 1230),
437 # XXX no support for resume installations yet
438 #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240),
439 ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250),
440 ("ProgressDlg", None, 1280)])
441 add_data(db, "AdminUISequence",
442 [("InitialTargetDir", 'TARGETDIR=""', 750),
443 ("SetDLLDirToTarget", 'DLLDIR=""', 751),
444 ])
445
446 # Execute Sequences
447 add_data(db, "InstallExecuteSequence",
448 [("InitialTargetDir", 'TARGETDIR=""', 750),
449 ("SetDLLDirToSystem32", 'DLLDIR="" and ' + sys32cond, 751),
450 ("SetDLLDirToTarget", 'DLLDIR="" and not ' + sys32cond, 752),
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000451 ("UpdateEditIDLE", None, 1050),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000452 ("CompilePyc", "COMPILEALL", 6800),
453 ("CompilePyo", "COMPILEALL", 6801),
Martin v. Löwis367c79a2008-05-25 16:37:34 +0000454 ("CompileGrammar", "COMPILEALL", 6802),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000455 ])
456 add_data(db, "AdminExecuteSequence",
457 [("InitialTargetDir", 'TARGETDIR=""', 750),
458 ("SetDLLDirToTarget", 'DLLDIR=""', 751),
459 ("CompilePyc", "COMPILEALL", 6800),
460 ("CompilePyo", "COMPILEALL", 6801),
Martin v. Löwis367c79a2008-05-25 16:37:34 +0000461 ("CompileGrammar", "COMPILEALL", 6802),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000462 ])
463
464 #####################################################################
465 # Standard dialogs: FatalError, UserExit, ExitDialog
466 fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title,
467 "Finish", "Finish", "Finish")
468 fatal.title("[ProductName] Installer ended prematurely")
469 fatal.back("< Back", "Finish", active = 0)
470 fatal.cancel("Cancel", "Back", active = 0)
471 fatal.text("Description1", 135, 70, 220, 80, 0x30003,
472 "[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.")
473 fatal.text("Description2", 135, 155, 220, 20, 0x30003,
474 "Click the Finish button to exit the Installer.")
475 c=fatal.next("Finish", "Cancel", name="Finish")
476 # See "ControlEvent Table". Parameters are the event, the parameter
477 # to the action, and optionally the condition for the event, and the order
478 # of events.
479 c.event("EndDialog", "Exit")
Tim Peters66cb0182004-08-26 05:23:19 +0000480
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000481 user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title,
482 "Finish", "Finish", "Finish")
483 user_exit.title("[ProductName] Installer was interrupted")
484 user_exit.back("< Back", "Finish", active = 0)
485 user_exit.cancel("Cancel", "Back", active = 0)
486 user_exit.text("Description1", 135, 70, 220, 80, 0x30003,
487 "[ProductName] setup was interrupted. Your system has not been modified. "
488 "To install this program at a later time, please run the installation again.")
489 user_exit.text("Description2", 135, 155, 220, 20, 0x30003,
490 "Click the Finish button to exit the Installer.")
491 c = user_exit.next("Finish", "Cancel", name="Finish")
492 c.event("EndDialog", "Exit")
Tim Peters66cb0182004-08-26 05:23:19 +0000493
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000494 exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title,
495 "Finish", "Finish", "Finish")
496 exit_dialog.title("Completing the [ProductName] Installer")
497 exit_dialog.back("< Back", "Finish", active = 0)
498 exit_dialog.cancel("Cancel", "Back", active = 0)
Martin v. Löwis2dd2a282004-08-22 17:10:12 +0000499 exit_dialog.text("Acknowledgements", 135, 95, 220, 120, 0x30003,
500 "Special Windows thanks to:\n"
Martin v. Löwisd3f61a22004-08-30 09:22:30 +0000501 " Mark Hammond, without whose years of freely \n"
502 " shared Windows expertise, Python for Windows \n"
503 " would still be Python for DOS.")
Tim Peters66cb0182004-08-26 05:23:19 +0000504
Martin v. Löwis8c7c56e2006-03-05 14:04:26 +0000505 c = exit_dialog.text("warning", 135, 200, 220, 40, 0x30003,
506 "{\\VerdanaRed9}Warning: Python 2.5.x is the last "
507 "Python release for Windows 9x.")
Martin v. Löwisdf511792006-03-28 07:51:51 +0000508 c.condition("Hide", "NOT Version9X")
Martin v. Löwis8c7c56e2006-03-05 14:04:26 +0000509
Martin v. Löwis2dd2a282004-08-22 17:10:12 +0000510 exit_dialog.text("Description", 135, 235, 220, 20, 0x30003,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000511 "Click the Finish button to exit the Installer.")
512 c = exit_dialog.next("Finish", "Cancel", name="Finish")
513 c.event("EndDialog", "Return")
514
515 #####################################################################
516 # Required dialog: FilesInUse, ErrorDlg
517 inuse = PyDialog(db, "FilesInUse",
518 x, y, w, h,
519 19, # KeepModeless|Modal|Visible
520 title,
521 "Retry", "Retry", "Retry", bitmap=False)
522 inuse.text("Title", 15, 6, 200, 15, 0x30003,
523 r"{\DlgFontBold8}Files in Use")
524 inuse.text("Description", 20, 23, 280, 20, 0x30003,
525 "Some files that need to be updated are currently in use.")
526 inuse.text("Text", 20, 55, 330, 50, 3,
527 "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.")
528 inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess",
529 None, None, None)
530 c=inuse.back("Exit", "Ignore", name="Exit")
531 c.event("EndDialog", "Exit")
532 c=inuse.next("Ignore", "Retry", name="Ignore")
533 c.event("EndDialog", "Ignore")
534 c=inuse.cancel("Retry", "Exit", name="Retry")
535 c.event("EndDialog","Retry")
536
537
538 # See "Error Dialog". See "ICE20" for the required names of the controls.
539 error = Dialog(db, "ErrorDlg",
540 50, 10, 330, 101,
541 65543, # Error|Minimize|Modal|Visible
542 title,
543 "ErrorText", None, None)
544 error.text("ErrorText", 50,9,280,48,3, "")
545 error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None)
546 error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo")
547 error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes")
548 error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort")
549 error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel")
550 error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore")
551 error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk")
552 error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry")
553
554 #####################################################################
555 # Global "Query Cancel" dialog
556 cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title,
557 "No", "No", "No")
Tim Peters66cb0182004-08-26 05:23:19 +0000558 cancel.text("Text", 48, 15, 194, 30, 3,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000559 "Are you sure you want to cancel [ProductName] installation?")
560 cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
561 "py.ico", None, None)
562 c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No")
563 c.event("EndDialog", "Exit")
Tim Peters66cb0182004-08-26 05:23:19 +0000564
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000565 c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes")
566 c.event("EndDialog", "Return")
567
568 #####################################################################
569 # Global "Wait for costing" dialog
570 costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title,
571 "Return", "Return", "Return")
572 costing.text("Text", 48, 15, 194, 30, 3,
573 "Please wait while the installer finishes determining your disk space requirements.")
574 costing.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
575 "py.ico", None, None)
576 c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None)
577 c.event("EndDialog", "Exit")
578
579 #####################################################################
580 # Preparation dialog: no user input except cancellation
581 prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title,
582 "Cancel", "Cancel", "Cancel")
583 prep.text("Description", 135, 70, 220, 40, 0x30003,
584 "Please wait while the Installer prepares to guide you through the installation.")
585 prep.title("Welcome to the [ProductName] Installer")
586 c=prep.text("ActionText", 135, 110, 220, 20, 0x30003, "Pondering...")
587 c.mapping("ActionText", "Text")
588 c=prep.text("ActionData", 135, 135, 220, 30, 0x30003, None)
589 c.mapping("ActionData", "Text")
590 prep.back("Back", None, active=0)
591 prep.next("Next", None, active=0)
592 c=prep.cancel("Cancel", None)
593 c.event("SpawnDialog", "CancelDlg")
594
595 #####################################################################
596 # Target directory selection
597 seldlg = PyDialog(db, "SelectDirectoryDlg", x, y, w, h, modal, title,
598 "Next", "Next", "Cancel")
599 seldlg.title("Select Destination Directory")
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000600 c = seldlg.text("Existing", 135, 25, 235, 30, 0x30003,
601 "{\VerdanaRed9}This update will replace your existing [ProductLine] installation.")
602 c.condition("Hide", 'REMOVEOLDVERSION="" and REMOVEOLDSNAPSHOT=""')
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000603 seldlg.text("Description", 135, 50, 220, 40, 0x30003,
604 "Please select a directory for the [ProductName] files.")
605
606 seldlg.back("< Back", None, active=0)
607 c = seldlg.next("Next >", "Cancel")
608 c.event("DoAction", "CheckDir", "TargetExistsOk<>1", order=1)
609 # If the target exists, but we found that we are going to remove old versions, don't bother
610 # confirming that the target directory exists. Strictly speaking, we should determine that
611 # the target directory is indeed the target of the product that we are going to remove, but
612 # I don't know how to do that.
613 c.event("SpawnDialog", "ExistingDirectoryDlg", 'TargetExists=1 and REMOVEOLDVERSION="" and REMOVEOLDSNAPSHOT=""', 2)
614 c.event("SetTargetPath", "TARGETDIR", 'TargetExists=0 or REMOVEOLDVERSION<>"" or REMOVEOLDSNAPSHOT<>""', 3)
615 c.event("SpawnWaitDialog", "WaitForCostingDlg", "CostingComplete=1", 4)
616 c.event("NewDialog", "SelectFeaturesDlg", 'TargetExists=0 or REMOVEOLDVERSION<>"" or REMOVEOLDSNAPSHOT<>""', 5)
617
618 c = seldlg.cancel("Cancel", "DirectoryCombo")
619 c.event("SpawnDialog", "CancelDlg")
620
621 seldlg.control("DirectoryCombo", "DirectoryCombo", 135, 70, 172, 80, 393219,
622 "TARGETDIR", None, "DirectoryList", None)
623 seldlg.control("DirectoryList", "DirectoryList", 135, 90, 208, 136, 3, "TARGETDIR",
624 None, "PathEdit", None)
625 seldlg.control("PathEdit", "PathEdit", 135, 230, 206, 16, 3, "TARGETDIR", None, "Next", None)
626 c = seldlg.pushbutton("Up", 306, 70, 18, 18, 3, "Up", None)
627 c.event("DirectoryListUp", "0")
628 c = seldlg.pushbutton("NewDir", 324, 70, 30, 18, 3, "New", None)
629 c.event("DirectoryListNew", "0")
630
631 #####################################################################
632 # SelectFeaturesDlg
633 features = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal|track_disk_space,
634 title, "Tree", "Next", "Cancel")
635 features.title("Customize [ProductName]")
636 features.text("Description", 135, 35, 220, 15, 0x30003,
637 "Select the way you want features to be installed.")
638 features.text("Text", 135,45,220,30, 3,
639 "Click on the icons in the tree below to change the way features will be installed.")
640
641 c=features.back("< Back", "Next")
642 c.event("NewDialog", "SelectDirectoryDlg")
643
644 c=features.next("Next >", "Cancel")
645 c.mapping("SelectionNoItems", "Enabled")
646 c.event("SpawnDialog", "DiskCostDlg", "OutOfDiskSpace=1", order=1)
647 c.event("EndDialog", "Return", "OutOfDiskSpace<>1", order=2)
648
649 c=features.cancel("Cancel", "Tree")
650 c.event("SpawnDialog", "CancelDlg")
651
Tim Peters66cb0182004-08-26 05:23:19 +0000652 # 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 +0000653 features.control("Tree", "SelectionTree", 135, 75, 220, 95, 7, "_BrowseProperty",
654 "Tree of selections", "Back", None)
655
656 #c=features.pushbutton("Reset", 42, 243, 56, 17, 3, "Reset", "DiskCost")
657 #c.mapping("SelectionNoItems", "Enabled")
658 #c.event("Reset", "0")
Tim Peters66cb0182004-08-26 05:23:19 +0000659
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000660 features.control("Box", "GroupBox", 135, 170, 225, 90, 1, None, None, None, None)
661
662 c=features.xbutton("DiskCost", "Disk &Usage", None, 0.10)
663 c.mapping("SelectionNoItems","Enabled")
664 c.event("SpawnDialog", "DiskCostDlg")
665
666 c=features.xbutton("Advanced", "Advanced", None, 0.30)
667 c.event("SpawnDialog", "AdvancedDlg")
668
669 c=features.text("ItemDescription", 140, 180, 210, 30, 3,
670 "Multiline description of the currently selected item.")
671 c.mapping("SelectionDescription","Text")
Tim Peters66cb0182004-08-26 05:23:19 +0000672
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000673 c=features.text("ItemSize", 140, 210, 210, 45, 3,
674 "The size of the currently selected item.")
675 c.mapping("SelectionSize", "Text")
676
677 #####################################################################
678 # Disk cost
679 cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title,
680 "OK", "OK", "OK", bitmap=False)
681 cost.text("Title", 15, 6, 200, 15, 0x30003,
682 "{\DlgFontBold8}Disk Space Requirements")
683 cost.text("Description", 20, 20, 280, 20, 0x30003,
684 "The disk space required for the installation of the selected features.")
685 cost.text("Text", 20, 53, 330, 60, 3,
686 "The highlighted volumes (if any) do not have enough disk space "
687 "available for the currently selected features. You can either "
688 "remove some files from the highlighted volumes, or choose to "
689 "install less features onto local drive(s), or select different "
690 "destination drive(s).")
691 cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223,
692 None, "{120}{70}{70}{70}{70}", None, None)
693 cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return")
694
695 #####################################################################
696 # WhichUsers Dialog. Only available on NT, and for privileged users.
697 # This must be run before FindRelatedProducts, because that will
698 # take into account whether the previous installation was per-user
699 # or per-machine. We currently don't support going back to this
700 # dialog after "Next" was selected; to support this, we would need to
701 # find how to reset the ALLUSERS property, and how to re-run
702 # FindRelatedProducts.
703 # On Windows9x, the ALLUSERS property is ignored on the command line
704 # and in the Property table, but installer fails according to the documentation
705 # if a dialog attempts to set ALLUSERS.
706 whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title,
707 "AdminInstall", "Next", "Cancel")
708 whichusers.title("Select whether to install [ProductName] for all users of this computer.")
709 # A radio group with two options: allusers, justme
Martin v. Löwiseba774b2008-10-13 11:23:35 +0000710 g = whichusers.radiogroup("AdminInstall", 135, 60, 235, 80, 3,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000711 "WhichUsers", "", "Next")
Martin v. Löwiseba774b2008-10-13 11:23:35 +0000712 g.condition("Disable", "VersionNT=600") # Not available on Vista and Windows 2008
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000713 g.add("ALL", 0, 5, 150, 20, "Install for all users")
Martin v. Löwiseba774b2008-10-13 11:23:35 +0000714 g.add("JUSTME", 0, 25, 235, 20, "Install just for me (not available on Windows Vista)")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000715
Tim Peters66cb0182004-08-26 05:23:19 +0000716 whichusers.back("Back", None, active=0)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000717
718 c = whichusers.next("Next >", "Cancel")
719 c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1)
720 c.event("EndDialog", "Return", order = 2)
721
722 c = whichusers.cancel("Cancel", "AdminInstall")
723 c.event("SpawnDialog", "CancelDlg")
Tim Peters66cb0182004-08-26 05:23:19 +0000724
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000725 #####################################################################
726 # Advanced Dialog.
727 advanced = PyDialog(db, "AdvancedDlg", x, y, w, h, modal, title,
Martin v. Löwis63470982008-11-19 13:51:44 +0000728 "CompilePyc", "Ok", "Ok")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000729 advanced.title("Advanced Options for [ProductName]")
730 # A radio group with two options: allusers, justme
731 advanced.checkbox("CompilePyc", 135, 60, 230, 50, 3,
Martin v. Löwis63470982008-11-19 13:51:44 +0000732 "COMPILEALL", "Compile .py files to byte code after installation", "Ok")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000733
Martin v. Löwis63470982008-11-19 13:51:44 +0000734 c = advanced.cancel("Ok", "CompilePyc", name="Ok") # Button just has location of cancel button.
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000735 c.event("EndDialog", "Return")
736
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000737 #####################################################################
Tim Peters66cb0182004-08-26 05:23:19 +0000738 # Existing Directory dialog
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000739 dlg = Dialog(db, "ExistingDirectoryDlg", 50, 30, 200, 80, modal, title,
740 "No", "No", "No")
741 dlg.text("Title", 10, 20, 180, 40, 3,
742 "[TARGETDIR] exists. Are you sure you want to overwrite existing files?")
743 c=dlg.pushbutton("Yes", 30, 60, 55, 17, 3, "Yes", "No")
744 c.event("[TargetExists]", "0", order=1)
745 c.event("[TargetExistsOk]", "1", order=2)
746 c.event("EndDialog", "Return", order=3)
747 c=dlg.pushbutton("No", 115, 60, 55, 17, 3, "No", "Yes")
748 c.event("EndDialog", "Return")
749
750 #####################################################################
751 # Installation Progress dialog (modeless)
752 progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title,
753 "Cancel", "Cancel", "Cancel", bitmap=False)
754 progress.text("Title", 20, 15, 200, 15, 0x30003,
755 "{\DlgFontBold8}[Progress1] [ProductName]")
756 progress.text("Text", 35, 65, 300, 30, 3,
757 "Please wait while the Installer [Progress2] [ProductName]. "
758 "This may take several minutes.")
759 progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:")
760
761 c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...")
762 c.mapping("ActionText", "Text")
763
764 #c=progress.text("ActionData", 35, 140, 300, 20, 3, None)
765 #c.mapping("ActionData", "Text")
766
767 c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537,
768 None, "Progress done", None, None)
769 c.mapping("SetProgress", "Progress")
770
771 progress.back("< Back", "Next", active=False)
772 progress.next("Next >", "Cancel", active=False)
773 progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg")
774
775 # Maintenance type: repair/uninstall
776 maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title,
777 "Next", "Next", "Cancel")
778 maint.title("Welcome to the [ProductName] Setup Wizard")
779 maint.text("BodyText", 135, 63, 230, 42, 3,
780 "Select whether you want to repair or remove [ProductName].")
781 g=maint.radiogroup("RepairRadioGroup", 135, 108, 230, 60, 3,
782 "MaintenanceForm_Action", "", "Next")
783 g.add("Change", 0, 0, 200, 17, "&Change [ProductName]")
784 g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]")
785 g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]")
Tim Peters66cb0182004-08-26 05:23:19 +0000786
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000787 maint.back("< Back", None, active=False)
788 c=maint.next("Finish", "Cancel")
789 # Change installation: Change progress dialog to "Change", then ask
790 # for feature selection
791 c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1)
792 c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2)
793
794 # Reinstall: Change progress dialog to "Repair", then invoke reinstall
795 # Also set list of reinstalled features to "ALL"
796 c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5)
797 c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6)
Raymond Hettinger72f08012004-11-07 07:08:25 +0000798 c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000799 c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8)
800
801 # Uninstall: Change progress to "Remove", then invoke uninstall
802 # Also set list of removed features to "ALL"
803 c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11)
804 c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12)
805 c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13)
806 c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14)
807
Tim Peters66cb0182004-08-26 05:23:19 +0000808 # Close dialog when maintenance action scheduled
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000809 c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20)
810 c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21)
Tim Peters66cb0182004-08-26 05:23:19 +0000811
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000812 maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg")
Tim Peters66cb0182004-08-26 05:23:19 +0000813
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000814
815# See "Feature Table". The feature level is 1 for all features,
816# and the feature attributes are 0 for the DefaultFeature, and
817# FollowParent for all other features. The numbers are the Display
818# column.
819def add_features(db):
820 # feature attributes:
821 # msidbFeatureAttributesFollowParent == 2
822 # msidbFeatureAttributesDisallowAdvertise == 8
823 # Features that need to be installed with together with the main feature
824 # (i.e. additional Python libraries) need to follow the parent feature.
825 # Features that have no advertisement trigger (e.g. the test suite)
826 # must not support advertisement
Martin v. Löwise411f892008-04-07 14:54:16 +0000827 global default_feature, tcltk, htmlfiles, tools, testsuite, ext_feature, private_crt
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000828 default_feature = Feature(db, "DefaultFeature", "Python",
829 "Python Interpreter and Libraries",
830 1, directory = "TARGETDIR")
Martin v. Löwis4dc34152008-04-05 15:48:36 +0000831 shared_crt = Feature(db, "SharedCRT", "MSVCRT", "C Run-Time (system-wide)", 0,
832 level=0)
833 private_crt = Feature(db, "PrivateCRT", "MSVCRT", "C Run-Time (private)", 0,
834 level=0)
835 add_data(db, "Condition", [("SharedCRT", 1, sys32cond),
836 ("PrivateCRT", 1, "not "+sys32cond)])
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000837 # We don't support advertisement of extensions
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000838 ext_feature = Feature(db, "Extensions", "Register Extensions",
839 "Make this Python installation the default Python installation", 3,
Martin v. Löwisdff68d02004-09-10 09:20:10 +0000840 parent = default_feature, attributes=2|8)
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000841 if have_tcl:
842 tcltk = Feature(db, "TclTk", "Tcl/Tk", "Tkinter, IDLE, pydoc", 5,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000843 parent = default_feature, attributes=2)
844 htmlfiles = Feature(db, "Documentation", "Documentation",
845 "Python HTMLHelp File", 7, parent = default_feature)
846 tools = Feature(db, "Tools", "Utility Scripts",
Tim Peters66cb0182004-08-26 05:23:19 +0000847 "Python utility scripts (Tools/", 9,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000848 parent = default_feature, attributes=2)
849 testsuite = Feature(db, "Testsuite", "Test suite",
850 "Python test suite (Lib/test/)", 11,
851 parent = default_feature, attributes=2|8)
Tim Peters66cb0182004-08-26 05:23:19 +0000852
Christian Heimes9acba042007-12-04 14:57:30 +0000853def extract_msvcr90():
Martin v. Löwis03dc56c2008-02-28 22:20:50 +0000854 # Find the redistributable files
Martin v. Löwise1d9dca2008-09-19 19:20:03 +0000855 if msilib.Win64:
856 arch = "amd64"
857 else:
858 arch = "x86"
859 dir = os.path.join(os.environ['VS90COMNTOOLS'], r"..\..\VC\redist\%s\Microsoft.VC90.CRT" % arch)
Christian Heimes9acba042007-12-04 14:57:30 +0000860
Martin v. Löwisd9759c42008-02-28 19:57:34 +0000861 result = []
Christian Heimes9acba042007-12-04 14:57:30 +0000862 installer = msilib.MakeInstaller()
Martin v. Löwisd9759c42008-02-28 19:57:34 +0000863 # omit msvcm90 and msvcp90, as they aren't really needed
864 files = ["Microsoft.VC90.CRT.manifest", "msvcr90.dll"]
865 for f in files:
866 path = os.path.join(dir, f)
867 kw = {'src':path}
868 if f.endswith('.dll'):
869 kw['version'] = installer.FileVersion(path, 0)
870 kw['language'] = installer.FileVersion(path, 1)
871 result.append((f, kw))
872 return result
Christian Heimes9acba042007-12-04 14:57:30 +0000873
Martin v. Löwisdcc86202008-05-25 11:56:23 +0000874def generate_license():
875 import shutil, glob
876 out = open("LICENSE.txt", "w")
877 shutil.copyfileobj(open(os.path.join(srcdir, "LICENSE")), out)
Martin v. Löwis4eb18f92008-09-14 20:22:39 +0000878 shutil.copyfileobj(open("crtlicense.txt"), out)
Martin v. Löwisb8a18ea2008-06-12 20:07:53 +0000879 for name, pat, file in (("bzip2","bzip2-*", "LICENSE"),
880 ("Berkeley DB", "db-*", "LICENSE"),
881 ("openssl", "openssl-*", "LICENSE"),
882 ("Tcl", "tcl8*", "license.terms"),
883 ("Tk", "tk8*", "license.terms"),
884 ("Tix", "tix-*", "license.terms")):
885 out.write("\nThis copy of Python includes a copy of %s, which is licensed under the following terms:\n\n" % name)
886 dirs = glob.glob(srcdir+"/../"+pat)
Martin v. Löwisdcc86202008-05-25 11:56:23 +0000887 if not dirs:
Martin v. Löwisb8a18ea2008-06-12 20:07:53 +0000888 raise ValueError, "Could not find "+srcdir+"/../"+pat
Martin v. Löwisdcc86202008-05-25 11:56:23 +0000889 if len(dirs) > 2:
Martin v. Löwisb8a18ea2008-06-12 20:07:53 +0000890 raise ValueError, "Multiple copies of "+pat
Martin v. Löwisdcc86202008-05-25 11:56:23 +0000891 dir = dirs[0]
892 shutil.copyfileobj(open(os.path.join(dir, file)), out)
893 out.close()
894
895
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000896class PyDirectory(Directory):
897 """By default, all components in the Python installer
898 can run from source."""
899 def __init__(self, *args, **kw):
900 if not kw.has_key("componentflags"):
901 kw['componentflags'] = 2 #msidbComponentAttributesOptional
902 Directory.__init__(self, *args, **kw)
903
904# See "File Table", "Component Table", "Directory Table",
905# "FeatureComponents Table"
906def add_files(db):
907 cab = CAB("python")
908 tmpfiles = []
909 # Add all executables, icons, text files into the TARGETDIR component
910 root = PyDirectory(db, cab, None, srcdir, "TARGETDIR", "SourceDir")
911 default_feature.set_current()
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000912 if not msilib.Win64:
Christian Heimes9acba042007-12-04 14:57:30 +0000913 root.add_file("%s/w9xpopen.exe" % PCBUILD)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000914 root.add_file("README.txt", src="README")
915 root.add_file("NEWS.txt", src="Misc/NEWS")
Martin v. Löwisdcc86202008-05-25 11:56:23 +0000916 generate_license()
917 root.add_file("LICENSE.txt", src=os.path.abspath("LICENSE.txt"))
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000918 root.start_component("python.exe", keyfile="python.exe")
Christian Heimes9acba042007-12-04 14:57:30 +0000919 root.add_file("%s/python.exe" % PCBUILD)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000920 root.start_component("pythonw.exe", keyfile="pythonw.exe")
Christian Heimes9acba042007-12-04 14:57:30 +0000921 root.add_file("%s/pythonw.exe" % PCBUILD)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000922
923 # msidbComponentAttributesSharedDllRefCount = 8, see "Component Table"
Martin v. Löwis0f3e7692008-10-17 13:43:01 +0000924 dlldir = PyDirectory(db, cab, root, srcdir, "DLLDIR", ".")
Martin v. Löwisd9759c42008-02-28 19:57:34 +0000925
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000926 pydll = "python%s%s.dll" % (major, minor)
Christian Heimes9acba042007-12-04 14:57:30 +0000927 pydllsrc = os.path.join(srcdir, PCBUILD, pydll)
Martin v. Löwis141f41a2005-03-15 00:39:40 +0000928 dlldir.start_component("DLLDIR", flags = 8, keyfile = pydll, uuid = pythondll_uuid)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000929 installer = msilib.MakeInstaller()
930 pyversion = installer.FileVersion(pydllsrc, 0)
931 if not snapshot:
932 # For releases, the Python DLL has the same version as the
933 # installer package.
934 assert pyversion.split(".")[:3] == current_version.split(".")
Christian Heimes9acba042007-12-04 14:57:30 +0000935 dlldir.add_file("%s/python%s%s.dll" % (PCBUILD, major, minor),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000936 version=pyversion,
937 language=installer.FileVersion(pydllsrc, 1))
Martin v. Löwisd9759c42008-02-28 19:57:34 +0000938 DLLs = PyDirectory(db, cab, root, srcdir + "/" + PCBUILD, "DLLs", "DLLS|DLLs")
Martin v. Löwis1e72fec2008-04-07 16:34:04 +0000939
940 # msvcr90.dll: Need to place the DLL and the manifest into the root directory,
941 # plus another copy of the manifest in the DLLs directory, with the manifest
942 # pointing to the root directory
Martin v. Löwis46a8be72008-04-07 14:55:53 +0000943 root.start_component("msvcr90", feature=private_crt)
Martin v. Löwis1e72fec2008-04-07 16:34:04 +0000944 # Results are ID,keyword pairs
945 manifest, crtdll = extract_msvcr90()
946 root.add_file(manifest[0], **manifest[1])
947 root.add_file(crtdll[0], **crtdll[1])
948 # Copy the manifest
Martin v. Löwisddd82632008-11-06 19:46:03 +0000949 # Actually, don't do that anymore - no DLL in DLLs should have a manifest
950 # dependency on msvcr90.dll anymore, so this should not be necessary
951 #manifest_dlls = manifest[0]+".root"
952 #open(manifest_dlls, "w").write(open(manifest[1]['src']).read().replace("msvcr","../msvcr"))
953 #DLLs.start_component("msvcr90_dlls", feature=private_crt)
954 #DLLs.add_file(manifest[0], src=os.path.abspath(manifest_dlls))
Martin v. Löwis1e72fec2008-04-07 16:34:04 +0000955
956 # Now start the main component for the DLLs directory;
957 # no regular files have been added to the directory yet.
958 DLLs.start_component()
Tim Peters66cb0182004-08-26 05:23:19 +0000959
Martin v. Löwis38325b72006-08-25 00:03:34 +0000960 # Check if _ctypes.pyd exists
Christian Heimes9acba042007-12-04 14:57:30 +0000961 have_ctypes = os.path.exists(srcdir+"/%s/_ctypes.pyd" % PCBUILD)
Martin v. Löwis38325b72006-08-25 00:03:34 +0000962 if not have_ctypes:
963 print "WARNING: _ctypes.pyd not found, ctypes will not be included"
964 extensions.remove("_ctypes.pyd")
Tim Peters147f9ae2006-08-25 22:05:39 +0000965
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000966 # Add all .py files in Lib, except lib-tk, test
967 dirs={}
968 pydirs = [(root,"Lib")]
969 while pydirs:
Martin v. Löwisc9798fc2008-06-13 18:58:47 +0000970 # Commit every now and then, or else installer will complain
971 db.Commit()
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000972 parent, dir = pydirs.pop()
Martin v. Löwis9ca9f562006-01-03 06:29:53 +0000973 if dir == ".svn" or dir.startswith("plat-"):
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000974 continue
Georg Brandl6634bf22008-05-20 07:13:37 +0000975 elif dir in ["lib-tk", "idlelib", "Icons"]:
Martin v. Löwise0f780d2004-09-01 14:51:06 +0000976 if not have_tcl:
977 continue
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000978 tcltk.set_current()
Martin v. Löwis5c9e55e2004-12-30 14:08:18 +0000979 elif dir in ['test', 'tests', 'data', 'output']:
Martin v. Löwis59c3acc2006-04-03 12:07:46 +0000980 # test: Lib, Lib/email, Lib/bsddb, Lib/ctypes, Lib/sqlite3
Martin v. Löwis5c9e55e2004-12-30 14:08:18 +0000981 # tests: Lib/distutils
982 # data: Lib/email/test
983 # output: Lib/test
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000984 testsuite.set_current()
Martin v. Löwis38325b72006-08-25 00:03:34 +0000985 elif not have_ctypes and dir == "ctypes":
986 continue
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000987 else:
988 default_feature.set_current()
989 lib = PyDirectory(db, cab, parent, dir, dir, "%s|%s" % (parent.make_short(dir), dir))
990 # Add additional files
991 dirs[dir]=lib
992 lib.glob("*.txt")
993 if dir=='site-packages':
Martin v. Löwis6d60c092004-11-21 10:16:26 +0000994 lib.add_file("README.txt", src="README")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +0000995 continue
996 files = lib.glob("*.py")
997 files += lib.glob("*.pyw")
998 if files:
999 # Add an entry to the RemoveFile table to remove bytecode files.
1000 lib.remove_pyc()
Martin v. Löwis64ed0432006-04-21 10:00:46 +00001001 if dir.endswith('.egg-info'):
1002 lib.add_file('entry_points.txt')
1003 lib.add_file('PKG-INFO')
1004 lib.add_file('top_level.txt')
1005 lib.add_file('zip-safe')
1006 continue
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001007 if dir=='test' and parent.physical=='Lib':
1008 lib.add_file("185test.db")
1009 lib.add_file("audiotest.au")
1010 lib.add_file("cfgparser.1")
Martin v. Löwisc0fdb182006-09-12 19:49:20 +00001011 lib.add_file("sgml_input.html")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001012 lib.add_file("testtar.tar")
Martin v. Löwis7d3755d2004-09-06 06:31:12 +00001013 lib.add_file("test_difflib_expect.html")
Martin v. Löwis59c3acc2006-04-03 12:07:46 +00001014 lib.add_file("check_soundcard.vbs")
Thomas Heller3bd33152006-04-04 18:41:13 +00001015 lib.add_file("empty.vbs")
Martin v. Löwisea598b62009-06-01 04:10:03 +00001016 lib.add_file("Sine-1000Hz-300ms.aif")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001017 lib.glob("*.uue")
Martin v. Löwis0ffdacd2007-11-20 02:46:02 +00001018 lib.glob("*.pem")
Martin v. Löwis6b449f42007-12-03 19:20:02 +00001019 lib.glob("*.pck")
Martin v. Löwis91a1a072009-04-04 18:44:44 +00001020 lib.add_file("zipdir.zip")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001021 if dir=='decimaltestdata':
1022 lib.glob("*.decTest")
Florent Xicluna13ba1a12010-03-13 11:18:49 +00001023 if dir=='xmltestdata':
1024 lib.glob("*.xml")
1025 lib.add_file("test.xml.out")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001026 if dir=='output':
1027 lib.glob("test_*")
1028 if dir=='idlelib':
1029 lib.glob("*.def")
1030 lib.add_file("idle.bat")
1031 if dir=="Icons":
1032 lib.glob("*.gif")
1033 lib.add_file("idle.icns")
Martin v. Löwis64ed0432006-04-21 10:00:46 +00001034 if dir=="command" and parent.physical=="distutils":
Martin v. Löwis023b9f92008-04-09 18:56:20 +00001035 lib.glob("wininst*.exe")
Martin v. Löwis64ed0432006-04-21 10:00:46 +00001036 if dir=="setuptools":
1037 lib.add_file("cli.exe")
1038 lib.add_file("gui.exe")
Martin v. Löwis367c79a2008-05-25 16:37:34 +00001039 if dir=="lib2to3":
1040 lib.removefile("pickle", "*.pickle")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001041 if dir=="data" and parent.physical=="test" and parent.basedir.physical=="email":
Martin v. Löwis9ca9f562006-01-03 06:29:53 +00001042 # This should contain all non-.svn files listed in subversion
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001043 for f in os.listdir(lib.absolute):
Martin v. Löwis9ca9f562006-01-03 06:29:53 +00001044 if f.endswith(".txt") or f==".svn":continue
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001045 if f.endswith(".au") or f.endswith(".gif"):
1046 lib.add_file(f)
1047 else:
1048 print "WARNING: New file %s in email/test/data" % f
1049 for f in os.listdir(lib.absolute):
1050 if os.path.isdir(os.path.join(lib.absolute, f)):
1051 pydirs.append((lib, f))
1052 # Add DLLs
1053 default_feature.set_current()
Martin v. Löwisd9759c42008-02-28 19:57:34 +00001054 lib = DLLs
Christian Heimes7e28e492008-01-01 13:52:57 +00001055 lib.add_file("py.ico", src=srcdir+"/PC/py.ico")
Christian Heimese1c6af02008-01-01 13:58:16 +00001056 lib.add_file("pyc.ico", src=srcdir+"/PC/pyc.ico")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001057 dlls = []
1058 tclfiles = []
1059 for f in extensions:
1060 if f=="_tkinter.pyd":
1061 continue
Christian Heimes9acba042007-12-04 14:57:30 +00001062 if not os.path.exists(srcdir + "/" + PCBUILD + "/" + f):
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001063 print "WARNING: Missing extension", f
1064 continue
1065 dlls.append(f)
1066 lib.add_file(f)
Martin v. Löwis88ef6372006-07-06 06:55:58 +00001067 # Add sqlite
1068 if msilib.msi_type=="Intel64;1033":
1069 sqlite_arch = "/ia64"
1070 elif msilib.msi_type=="x64;1033":
1071 sqlite_arch = "/amd64"
Martin v. Löwis0e795e72008-02-29 20:54:44 +00001072 tclsuffix = "64"
Martin v. Löwis88ef6372006-07-06 06:55:58 +00001073 else:
1074 sqlite_arch = ""
Martin v. Löwis0e795e72008-02-29 20:54:44 +00001075 tclsuffix = ""
Martin v. Löwisc9798fc2008-06-13 18:58:47 +00001076 lib.add_file("sqlite3.dll")
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001077 if have_tcl:
Christian Heimes9acba042007-12-04 14:57:30 +00001078 if not os.path.exists("%s/%s/_tkinter.pyd" % (srcdir, PCBUILD)):
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001079 print "WARNING: Missing _tkinter.pyd"
1080 else:
1081 lib.start_component("TkDLLs", tcltk)
1082 lib.add_file("_tkinter.pyd")
1083 dlls.append("_tkinter.pyd")
Martin v. Löwis0e795e72008-02-29 20:54:44 +00001084 tcldir = os.path.normpath(srcdir+("/../tcltk%s/bin" % tclsuffix))
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001085 for f in glob.glob1(tcldir, "*.dll"):
1086 lib.add_file(f, src=os.path.join(tcldir, f))
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001087 # check whether there are any unknown extensions
Christian Heimes9acba042007-12-04 14:57:30 +00001088 for f in glob.glob1(srcdir+"/"+PCBUILD, "*.pyd"):
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001089 if f.endswith("_d.pyd"): continue # debug version
1090 if f in dlls: continue
1091 print "WARNING: Unknown extension", f
Tim Peters66cb0182004-08-26 05:23:19 +00001092
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001093 # Add headers
1094 default_feature.set_current()
1095 lib = PyDirectory(db, cab, root, "include", "include", "INCLUDE|include")
1096 lib.glob("*.h")
1097 lib.add_file("pyconfig.h", src="../PC/pyconfig.h")
1098 # Add import libraries
Christian Heimes9acba042007-12-04 14:57:30 +00001099 lib = PyDirectory(db, cab, root, PCBUILD, "libs", "LIBS|libs")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001100 for f in dlls:
1101 lib.add_file(f.replace('pyd','lib'))
1102 lib.add_file('python%s%s.lib' % (major, minor))
Martin v. Löwis9fda9312004-12-22 13:41:49 +00001103 # Add the mingw-format library
1104 if have_mingw:
Tim Peters5a9fb3c2005-01-07 16:01:32 +00001105 lib.add_file('libpython%s%s.a' % (major, minor))
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001106 if have_tcl:
1107 # Add Tcl/Tk
Martin v. Löwis0e795e72008-02-29 20:54:44 +00001108 tcldirs = [(root, '../tcltk%s/lib' % tclsuffix, 'tcl')]
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001109 tcltk.set_current()
1110 while tcldirs:
1111 parent, phys, dir = tcldirs.pop()
1112 lib = PyDirectory(db, cab, parent, phys, dir, "%s|%s" % (parent.make_short(dir), dir))
1113 if not os.path.exists(lib.absolute):
1114 continue
1115 for f in os.listdir(lib.absolute):
1116 if os.path.isdir(os.path.join(lib.absolute, f)):
1117 tcldirs.append((lib, f, f))
1118 else:
1119 lib.add_file(f)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001120 # Add tools
1121 tools.set_current()
1122 tooldir = PyDirectory(db, cab, root, "Tools", "Tools", "TOOLS|Tools")
1123 for f in ['i18n', 'pynche', 'Scripts', 'versioncheck', 'webchecker']:
1124 lib = PyDirectory(db, cab, tooldir, f, f, "%s|%s" % (tooldir.make_short(f), f))
1125 lib.glob("*.py")
1126 lib.glob("*.pyw", exclude=['pydocgui.pyw'])
1127 lib.remove_pyc()
1128 lib.glob("*.txt")
1129 if f == "pynche":
1130 x = PyDirectory(db, cab, lib, "X", "X", "X|X")
1131 x.glob("*.txt")
Martin v. Löwis4d930be2004-12-01 21:46:35 +00001132 if os.path.exists(os.path.join(lib.absolute, "README")):
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001133 lib.add_file("README.txt", src="README")
Martin v. Löwis4d930be2004-12-01 21:46:35 +00001134 if f == 'Scripts':
Martin v. Löwisd69c9042008-10-01 11:19:50 +00001135 lib.add_file("2to3.py", src="2to3")
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001136 if have_tcl:
1137 lib.start_component("pydocgui.pyw", tcltk, keyfile="pydocgui.pyw")
1138 lib.add_file("pydocgui.pyw")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001139 # Add documentation
1140 htmlfiles.set_current()
1141 lib = PyDirectory(db, cab, root, "Doc", "Doc", "DOC|Doc")
Martin v. Löwis344d0662008-05-09 18:21:55 +00001142 lib.start_component("documentation", keyfile=docfile)
1143 lib.add_file(docfile, src="build/htmlhelp/"+docfile)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001144
1145 cab.commit(db)
1146
1147 for f in tmpfiles:
1148 os.unlink(f)
1149
1150# See "Registry Table", "Component Table"
1151def add_registry(db):
1152 # File extensions, associated with the REGISTRY.def component
1153 # IDLE verbs depend on the tcltk feature.
1154 # msidbComponentAttributesRegistryKeyPath = 4
1155 # -1 for Root specifies "dependent on ALLUSERS property"
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001156 tcldata = []
1157 if have_tcl:
1158 tcldata = [
Martin v. Löwis1ab4a1f2007-08-31 10:01:07 +00001159 ("REGISTRY.tcl", msilib.gen_uuid(), "TARGETDIR", registry_component, None,
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001160 "py.IDLE")]
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001161 add_data(db, "Component",
1162 # msidbComponentAttributesRegistryKeyPath = 4
Martin v. Löwis1ab4a1f2007-08-31 10:01:07 +00001163 [("REGISTRY", msilib.gen_uuid(), "TARGETDIR", registry_component, None,
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001164 "InstallPath"),
Martin v. Löwis1ab4a1f2007-08-31 10:01:07 +00001165 ("REGISTRY.doc", msilib.gen_uuid(), "TARGETDIR", registry_component, None,
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001166 "Documentation"),
Martin v. Löwis1ab4a1f2007-08-31 10:01:07 +00001167 ("REGISTRY.def", msilib.gen_uuid(), "TARGETDIR", registry_component,
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001168 None, None)] + tcldata)
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001169 # See "FeatureComponents Table".
1170 # The association between TclTk and pythonw.exe is necessary to make ICE59
1171 # happy, because the installer otherwise believes that the IDLE and PyDoc
1172 # shortcuts might get installed without pythonw.exe being install. This
1173 # is not true, since installing TclTk will install the default feature, which
1174 # will cause pythonw.exe to be installed.
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001175 # REGISTRY.tcl is not associated with any feature, as it will be requested
1176 # through a custom action
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001177 tcldata = []
1178 if have_tcl:
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001179 tcldata = [(tcltk.id, "pythonw.exe")]
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001180 add_data(db, "FeatureComponents",
1181 [(default_feature.id, "REGISTRY"),
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001182 (htmlfiles.id, "REGISTRY.doc"),
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001183 (ext_feature.id, "REGISTRY.def")] +
1184 tcldata
1185 )
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001186 # Extensions are not advertised. For advertised extensions,
1187 # we would need separate binaries that install along with the
1188 # extension.
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001189 pat = r"Software\Classes\%sPython.%sFile\shell\%s\command"
1190 ewi = "Edit with IDLE"
1191 pat2 = r"Software\Classes\%sPython.%sFile\DefaultIcon"
1192 pat3 = r"Software\Classes\%sPython.%sFile"
Martin v. Löwis3d6f8ff2008-11-07 18:51:50 +00001193 pat4 = r"Software\Classes\%sPython.%sFile\shellex\DropHandler"
Martin v. Löwiseac02e62004-11-18 08:00:33 +00001194 tcl_verbs = []
1195 if have_tcl:
1196 tcl_verbs=[
1197 ("py.IDLE", -1, pat % (testprefix, "", ewi), "",
Martin v. Löwis588b54b2009-05-05 16:10:16 +00001198 r'"[TARGETDIR]pythonw.exe" "[TARGETDIR]Lib\idlelib\idle.pyw" -e "%1"',
Martin v. Löwiseac02e62004-11-18 08:00:33 +00001199 "REGISTRY.tcl"),
1200 ("pyw.IDLE", -1, pat % (testprefix, "NoCon", ewi), "",
Martin v. Löwis588b54b2009-05-05 16:10:16 +00001201 r'"[TARGETDIR]pythonw.exe" "[TARGETDIR]Lib\idlelib\idle.pyw" -e "%1"',
Martin v. Löwiseac02e62004-11-18 08:00:33 +00001202 "REGISTRY.tcl"),
1203 ]
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001204 add_data(db, "Registry",
1205 [# Extensions
1206 ("py.ext", -1, r"Software\Classes\."+ext, "",
1207 "Python.File", "REGISTRY.def"),
1208 ("pyw.ext", -1, r"Software\Classes\."+ext+'w', "",
1209 "Python.NoConFile", "REGISTRY.def"),
1210 ("pyc.ext", -1, r"Software\Classes\."+ext+'c', "",
1211 "Python.CompiledFile", "REGISTRY.def"),
1212 ("pyo.ext", -1, r"Software\Classes\."+ext+'o', "",
1213 "Python.CompiledFile", "REGISTRY.def"),
1214 # MIME types
1215 ("py.mime", -1, r"Software\Classes\."+ext, "Content Type",
1216 "text/plain", "REGISTRY.def"),
1217 ("pyw.mime", -1, r"Software\Classes\."+ext+'w', "Content Type",
1218 "text/plain", "REGISTRY.def"),
1219 #Verbs
1220 ("py.open", -1, pat % (testprefix, "", "open"), "",
1221 r'"[TARGETDIR]python.exe" "%1" %*', "REGISTRY.def"),
1222 ("pyw.open", -1, pat % (testprefix, "NoCon", "open"), "",
1223 r'"[TARGETDIR]pythonw.exe" "%1" %*', "REGISTRY.def"),
1224 ("pyc.open", -1, pat % (testprefix, "Compiled", "open"), "",
1225 r'"[TARGETDIR]python.exe" "%1" %*', "REGISTRY.def"),
Martin v. Löwiseac02e62004-11-18 08:00:33 +00001226 ] + tcl_verbs + [
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001227 #Icons
1228 ("py.icon", -1, pat2 % (testprefix, ""), "",
Martin v. Löwis1319bb12006-05-12 13:57:36 +00001229 r'[DLLs]py.ico', "REGISTRY.def"),
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001230 ("pyw.icon", -1, pat2 % (testprefix, "NoCon"), "",
Martin v. Löwis1319bb12006-05-12 13:57:36 +00001231 r'[DLLs]py.ico', "REGISTRY.def"),
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001232 ("pyc.icon", -1, pat2 % (testprefix, "Compiled"), "",
Martin v. Löwis1319bb12006-05-12 13:57:36 +00001233 r'[DLLs]pyc.ico', "REGISTRY.def"),
Martin v. Löwisdff68d02004-09-10 09:20:10 +00001234 # Descriptions
1235 ("py.txt", -1, pat3 % (testprefix, ""), "",
1236 "Python File", "REGISTRY.def"),
1237 ("pyw.txt", -1, pat3 % (testprefix, "NoCon"), "",
1238 "Python File (no console)", "REGISTRY.def"),
1239 ("pyc.txt", -1, pat3 % (testprefix, "Compiled"), "",
1240 "Compiled Python File", "REGISTRY.def"),
Martin v. Löwis3d6f8ff2008-11-07 18:51:50 +00001241 # Drop Handler
1242 ("py.drop", -1, pat4 % (testprefix, ""), "",
1243 "{60254CA5-953B-11CF-8C96-00AA00B8708C}", "REGISTRY.def"),
1244 ("pyw.drop", -1, pat4 % (testprefix, "NoCon"), "",
1245 "{60254CA5-953B-11CF-8C96-00AA00B8708C}", "REGISTRY.def"),
1246 ("pyc.drop", -1, pat4 % (testprefix, "Compiled"), "",
1247 "{60254CA5-953B-11CF-8C96-00AA00B8708C}", "REGISTRY.def"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001248 ])
Tim Peters66cb0182004-08-26 05:23:19 +00001249
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001250 # Registry keys
1251 prefix = r"Software\%sPython\PythonCore\%s" % (testprefix, short_version)
1252 add_data(db, "Registry",
1253 [("InstallPath", -1, prefix+r"\InstallPath", "", "[TARGETDIR]", "REGISTRY"),
1254 ("InstallGroup", -1, prefix+r"\InstallPath\InstallGroup", "",
1255 "Python %s" % short_version, "REGISTRY"),
1256 ("PythonPath", -1, prefix+r"\PythonPath", "",
Martin v. Löwisf13337d2004-09-19 18:36:45 +00001257 r"[TARGETDIR]Lib;[TARGETDIR]DLLs;[TARGETDIR]Lib\lib-tk", "REGISTRY"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001258 ("Documentation", -1, prefix+r"\Help\Main Python Documentation", "",
Martin v. Löwis344d0662008-05-09 18:21:55 +00001259 "[TARGETDIR]Doc\\"+docfile , "REGISTRY.doc"),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001260 ("Modules", -1, prefix+r"\Modules", "+", None, "REGISTRY"),
1261 ("AppPaths", -1, r"Software\Microsoft\Windows\CurrentVersion\App Paths\Python.exe",
Martin v. Löwiscb6c3592008-11-30 11:08:26 +00001262 "", r"[TARGETDIR]Python.exe", "REGISTRY.def"),
1263 ("DisplayIcon", -1,
1264 r"Software\Microsoft\Windows\CurrentVersion\Uninstall\%s" % product_code,
Martin v. Löwisecb4f952009-06-28 09:32:39 +00001265 "DisplayIcon", "[TARGETDIR]python.exe", "REGISTRY")
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001266 ])
1267 # Shortcuts, see "Shortcut Table"
1268 add_data(db, "Directory",
1269 [("ProgramMenuFolder", "TARGETDIR", "."),
1270 ("MenuDir", "ProgramMenuFolder", "PY%s%s|%sPython %s.%s" % (major,minor,testprefix,major,minor))])
1271 add_data(db, "RemoveFile",
1272 [("MenuDir", "TARGETDIR", None, "MenuDir", 2)])
Martin v. Löwise0f780d2004-09-01 14:51:06 +00001273 tcltkshortcuts = []
1274 if have_tcl:
1275 tcltkshortcuts = [
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001276 ("IDLE", "MenuDir", "IDLE|IDLE (Python GUI)", "pythonw.exe",
Martin v. Löwisac191da2004-12-22 12:55:44 +00001277 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 +00001278 ("PyDoc", "MenuDir", "MODDOCS|Module Docs", "pythonw.exe",
Martin v. Löwisac191da2004-12-22 12:55:44 +00001279 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 +00001280 ]
1281 add_data(db, "Shortcut",
1282 tcltkshortcuts +
1283 [# Advertised shortcuts: targets are features, not files
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001284 ("Python", "MenuDir", "PYTHON|Python (command line)", "python.exe",
1285 default_feature.id, None, None, None, "python_icon.exe", 2, None, "TARGETDIR"),
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001286 # Advertising the Manual breaks on (some?) Win98, and the shortcut lacks an
1287 # icon first.
1288 #("Manual", "MenuDir", "MANUAL|Python Manuals", "documentation",
1289 # htmlfiles.id, None, None, None, None, None, None, None),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001290 ## Non-advertised shortcuts: must be associated with a registry component
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001291 ("Manual", "MenuDir", "MANUAL|Python Manuals", "REGISTRY.doc",
Martin v. Löwis344d0662008-05-09 18:21:55 +00001292 "[#%s]" % docfile, None,
Martin v. Löwis141f41a2005-03-15 00:39:40 +00001293 None, None, None, None, None, None),
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001294 ("Uninstall", "MenuDir", "UNINST|Uninstall Python", "REGISTRY",
1295 SystemFolderName+"msiexec", "/x%s" % product_code,
1296 None, None, None, None, None, None),
1297 ])
1298 db.Commit()
1299
Martin v. Löwis34360642010-03-16 18:49:28 +00001300db,msiname = build_database()
Martin v. Löwis8ffe9ab2004-08-22 13:34:34 +00001301try:
1302 add_features(db)
1303 add_ui(db)
1304 add_files(db)
1305 add_registry(db)
1306 remove_old_versions(db)
1307 db.Commit()
1308finally:
1309 del db
Martin v. Löwis34360642010-03-16 18:49:28 +00001310
1311# Merge CRT into MSI file. This requires the database to be closed.
1312mod_dir = os.path.join(os.environ["ProgramFiles"], "Common Files", "Merge Modules")
1313if msilib.Win64:
1314 modules = ["Microsoft_VC90_CRT_x86_x64.msm", "policy_9_0_Microsoft_VC90_CRT_x86_x64.msm"]
1315else:
1316 modules = ["Microsoft_VC90_CRT_x86.msm","policy_9_0_Microsoft_VC90_CRT_x86.msm"]
1317
1318for i, n in enumerate(modules):
1319 modules[i] = os.path.join(mod_dir, n)
1320
1321def merge(msi, feature, rootdir, modules):
1322 cab_and_filecount = []
1323 # Step 1: Merge databases, extract cabfiles
1324 m = msilib.MakeMerge2()
1325 m.OpenLog("merge.log")
1326 m.OpenDatabase(msi)
1327 for module in modules:
1328 print module
1329 m.OpenModule(module,0)
1330 m.Merge(feature, rootdir)
1331 print "Errors:"
1332 for e in m.Errors:
1333 print e.Type, e.ModuleTable, e.DatabaseTable
1334 print " Modkeys:",
1335 for s in e.ModuleKeys: print s,
1336 print
1337 print " DBKeys:",
1338 for s in e.DatabaseKeys: print s,
1339 print
1340 cabname = tempfile.mktemp(suffix=".cab")
1341 m.ExtractCAB(cabname)
1342 cab_and_filecount.append((cabname, len(m.ModuleFiles)))
1343 m.CloseModule()
1344 m.CloseDatabase(True)
1345 m.CloseLog()
1346
1347 # Step 2: Add CAB files
1348 i = msilib.MakeInstaller()
1349 db = i.OpenDatabase(msi, constants.msiOpenDatabaseModeTransact)
1350
1351 v = db.OpenView("SELECT LastSequence FROM Media")
1352 v.Execute(None)
1353 maxmedia = -1
1354 while 1:
1355 r = v.Fetch()
1356 if not r: break
1357 seq = r.IntegerData(1)
1358 if seq > maxmedia:
1359 maxmedia = seq
1360 print "Start of Media", maxmedia
1361
1362 for cabname, count in cab_and_filecount:
1363 stream = "merged%d" % maxmedia
1364 msilib.add_data(db, "Media",
1365 [(maxmedia+1, maxmedia+count, None, "#"+stream, None, None)])
1366 msilib.add_stream(db, stream, cabname)
1367 os.unlink(cabname)
1368 maxmedia += count
1369 # The merge module sets ALLUSERS to 1 in the property table.
1370 # This is undesired; delete that
1371 v = db.OpenView("DELETE FROM Property WHERE Property='ALLUSERS'")
1372 v.Execute(None)
1373 v.Close()
1374 db.Commit()
1375
1376merge(msiname, "SharedCRT", "TARGETDIR", modules)
1377
1378# certname (from config.py) should be (a substring of)
1379# the certificate subject, e.g. "Python Software Foundation"
1380if certname:
1381 os.system('signtool sign /n "%s" /t http://timestamp.verisign.com/scripts/timestamp.dll %s' % (certname, msiname))