source: python/vendor/Python-2.6.5/Tools/msi/msi.py

Last change on this file was 2, checked in by Yuri Dario, 15 years ago

Initial import for vendor code.

  • Property svn:eol-style set to native
File size: 57.1 KB
Line 
1# Python MSI Generator
2# (C) 2003 Martin v. Loewis
3# See "FOO" in comments refers to MSDN sections with the title FOO.
4import msilib, schema, sequence, os, glob, time, re, shutil
5from msilib import Feature, CAB, Directory, Dialog, Binary, add_data
6import uisample
7from win32com.client import constants
8from distutils.spawn import find_executable
9from uuids import product_codes
10
11# Settings can be overridden in config.py below
12# 0 for official python.org releases
13# 1 for intermediate releases by anybody, with
14# a new product code for every package.
15snapshot = 1
16# 1 means that file extension is px, not py,
17# and binaries start with x
18testpackage = 0
19# Location of build tree
20srcdir = os.path.abspath("../..")
21# Text to be displayed as the version in dialogs etc.
22# goes into file name and ProductCode. Defaults to
23# current_version.day for Snapshot, current_version otherwise
24full_current_version = None
25# Is Tcl available at all?
26have_tcl = True
27# path to PCbuild directory
28PCBUILD="PCbuild"
29# msvcrt version
30MSVCR = "90"
31
32try:
33 from config import *
34except ImportError:
35 pass
36
37# Extract current version from Include/patchlevel.h
38lines = open(srcdir + "/Include/patchlevel.h").readlines()
39major = minor = micro = level = serial = None
40levels = {
41 'PY_RELEASE_LEVEL_ALPHA':0xA,
42 'PY_RELEASE_LEVEL_BETA': 0xB,
43 'PY_RELEASE_LEVEL_GAMMA':0xC,
44 'PY_RELEASE_LEVEL_FINAL':0xF
45 }
46for l in lines:
47 if not l.startswith("#define"):
48 continue
49 l = l.split()
50 if len(l) != 3:
51 continue
52 _, name, value = l
53 if name == 'PY_MAJOR_VERSION': major = value
54 if name == 'PY_MINOR_VERSION': minor = value
55 if name == 'PY_MICRO_VERSION': micro = value
56 if name == 'PY_RELEASE_LEVEL': level = levels[value]
57 if name == 'PY_RELEASE_SERIAL': serial = value
58
59short_version = major+"."+minor
60# See PC/make_versioninfo.c
61FIELD3 = 1000*int(micro) + 10*level + int(serial)
62current_version = "%s.%d" % (short_version, FIELD3)
63
64# This should never change. The UpgradeCode of this package can be
65# used in the Upgrade table of future packages to make the future
66# package replace this one. See "UpgradeCode Property".
67# upgrade_code gets set to upgrade_code_64 when we have determined
68# that the target is Win64.
69upgrade_code_snapshot='{92A24481-3ECB-40FC-8836-04B7966EC0D5}'
70upgrade_code='{65E6DE48-A358-434D-AA4F-4AF72DB4718F}'
71upgrade_code_64='{6A965A0C-6EE6-4E3A-9983-3263F56311EC}'
72
73if snapshot:
74 current_version = "%s.%s.%s" % (major, minor, int(time.time()/3600/24))
75 product_code = msilib.gen_uuid()
76else:
77 product_code = product_codes[current_version]
78
79if full_current_version is None:
80 full_current_version = current_version
81
82extensions = [
83 'bz2.pyd',
84 'pyexpat.pyd',
85 'select.pyd',
86 'unicodedata.pyd',
87 'winsound.pyd',
88 '_elementtree.pyd',
89 '_bsddb.pyd',
90 '_socket.pyd',
91 '_ssl.pyd',
92 '_testcapi.pyd',
93 '_tkinter.pyd',
94 '_msi.pyd',
95 '_ctypes.pyd',
96 '_ctypes_test.pyd',
97 '_sqlite3.pyd',
98 '_hashlib.pyd',
99 '_multiprocessing.pyd'
100]
101
102# Well-known component UUIDs
103# These are needed for SharedDLLs reference counter; if
104# a different UUID was used for each incarnation of, say,
105# python24.dll, an upgrade would set the reference counter
106# from 1 to 2 (due to what I consider a bug in MSI)
107# Using the same UUID is fine since these files are versioned,
108# so Installer will always keep the newest version.
109# NOTE: All uuids are self generated.
110pythondll_uuid = {
111 "24":"{9B81E618-2301-4035-AC77-75D9ABEB7301}",
112 "25":"{2e41b118-38bd-4c1b-a840-6977efd1b911}",
113 "26":"{34ebecac-f046-4e1c-b0e3-9bac3cdaacfa}",
114 } [major+minor]
115
116# Compute the name that Sphinx gives to the docfile
117docfile = ""
118if micro:
119 docfile = str(micro)
120if level < 0xf:
121 docfile += '%x%s' % (level, serial)
122docfile = 'python%s%s%s.chm' % (major, minor, docfile)
123
124# Build the mingw import library, libpythonXY.a
125# This requires 'nm' and 'dlltool' executables on your PATH
126def build_mingw_lib(lib_file, def_file, dll_file, mingw_lib):
127 warning = "WARNING: %s - libpythonXX.a not built"
128 nm = find_executable('nm')
129 dlltool = find_executable('dlltool')
130
131 if not nm or not dlltool:
132 print warning % "nm and/or dlltool were not found"
133 return False
134
135 nm_command = '%s -Cs %s' % (nm, lib_file)
136 dlltool_command = "%s --dllname %s --def %s --output-lib %s" % \
137 (dlltool, dll_file, def_file, mingw_lib)
138 export_match = re.compile(r"^_imp__(.*) in python\d+\.dll").match
139
140 f = open(def_file,'w')
141 print >>f, "LIBRARY %s" % dll_file
142 print >>f, "EXPORTS"
143
144 nm_pipe = os.popen(nm_command)
145 for line in nm_pipe.readlines():
146 m = export_match(line)
147 if m:
148 print >>f, m.group(1)
149 f.close()
150 exit = nm_pipe.close()
151
152 if exit:
153 print warning % "nm did not run successfully"
154 return False
155
156 if os.system(dlltool_command) != 0:
157 print warning % "dlltool did not run successfully"
158 return False
159
160 return True
161
162# Target files (.def and .a) go in PCBuild directory
163lib_file = os.path.join(srcdir, PCBUILD, "python%s%s.lib" % (major, minor))
164def_file = os.path.join(srcdir, PCBUILD, "python%s%s.def" % (major, minor))
165dll_file = "python%s%s.dll" % (major, minor)
166mingw_lib = os.path.join(srcdir, PCBUILD, "libpython%s%s.a" % (major, minor))
167
168have_mingw = build_mingw_lib(lib_file, def_file, dll_file, mingw_lib)
169
170# Determine the target architechture
171dll_path = os.path.join(srcdir, PCBUILD, dll_file)
172msilib.set_arch_from_file(dll_path)
173if msilib.pe_type(dll_path) != msilib.pe_type("msisupport.dll"):
174 raise SystemError, "msisupport.dll for incorrect architecture"
175if msilib.Win64:
176 upgrade_code = upgrade_code_64
177 # Bump the last digit of the code by one, so that 32-bit and 64-bit
178 # releases get separate product codes
179 digit = hex((int(product_code[-2],16)+1)%16)[-1]
180 product_code = product_code[:-2] + digit + '}'
181
182if testpackage:
183 ext = 'px'
184 testprefix = 'x'
185else:
186 ext = 'py'
187 testprefix = ''
188
189if msilib.Win64:
190 SystemFolderName = "[System64Folder]"
191 registry_component = 4|256
192else:
193 SystemFolderName = "[SystemFolder]"
194 registry_component = 4
195
196msilib.reset()
197
198# condition in which to install pythonxy.dll in system32:
199# a) it is Windows 9x or
200# b) it is NT, the user is privileged, and has chosen per-machine installation
201sys32cond = "(Windows9x or (Privileged and ALLUSERS))"
202
203def build_database():
204 """Generate an empty database, with just the schema and the
205 Summary information stream."""
206 if snapshot:
207 uc = upgrade_code_snapshot
208 else:
209 uc = upgrade_code
210 if msilib.Win64:
211 productsuffix = " (64-bit)"
212 else:
213 productsuffix = ""
214 # schema represents the installer 2.0 database schema.
215 # sequence is the set of standard sequences
216 # (ui/execute, admin/advt/install)
217 db = msilib.init_database("python-%s%s.msi" % (full_current_version, msilib.arch_ext),
218 schema, ProductName="Python "+full_current_version+productsuffix,
219 ProductCode=product_code,
220 ProductVersion=current_version,
221 Manufacturer=u"Python Software Foundation",
222 request_uac = True)
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)
229 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"),
237 ("ProductLine", "Python%s%s" % (major, minor)),
238 ])
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",
255 [(upgrade_code_snapshot, start,
256 current_version,
257 None, # Ignore language
258 migrate_features,
259 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
270 props += ";TARGETDIR;DLLDIR"
271 # Installer collects the product codes of the earlier releases in
272 # these properties. In order to allow modification of the properties,
273 # they must be declared as secure. See "SecureCustomProperties Property"
274 add_data(db, "Property", [("SecureCustomProperties", props)])
275
276class PyDialog(Dialog):
277 """Dialog class with a fixed layout: controls at the top, then a ruler,
278 then a list of buttons: back, next, cancel. Optionally a bitmap at the
279 left."""
280 def __init__(self, *args, **kw):
281 """Dialog(database, name, x, y, w, h, attributes, title, first,
282 default, cancel, bitmap=true)"""
283 Dialog.__init__(self, *args)
284 ruler = self.h - 36
285 bmwidth = 152*ruler/328
286 if kw.get("bitmap", True):
287 self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin")
288 self.line("BottomLine", 0, ruler, self.w, 0)
289
290 def title(self, title):
291 "Set the title text of the dialog at the top."
292 # name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix,
293 # text, in VerdanaBold10
294 self.text("Title", 135, 10, 220, 60, 0x30003,
295 r"{\VerdanaBold10}%s" % title)
296
297 def back(self, title, next, name = "Back", active = 1):
298 """Add a back button with a given title, the tab-next button,
299 its name in the Control table, possibly initially disabled.
300
301 Return the button, so that events can be associated"""
302 if active:
303 flags = 3 # Visible|Enabled
304 else:
305 flags = 1 # Visible
306 return self.pushbutton(name, 180, self.h-27 , 56, 17, flags, title, next)
307
308 def cancel(self, title, next, name = "Cancel", active = 1):
309 """Add a cancel button with a given title, the tab-next button,
310 its name in the Control table, possibly initially disabled.
311
312 Return the button, so that events can be associated"""
313 if active:
314 flags = 3 # Visible|Enabled
315 else:
316 flags = 1 # Visible
317 return self.pushbutton(name, 304, self.h-27, 56, 17, flags, title, next)
318
319 def next(self, title, next, name = "Next", active = 1):
320 """Add a Next button with a given title, the tab-next button,
321 its name in the Control table, possibly initially disabled.
322
323 Return the button, so that events can be associated"""
324 if active:
325 flags = 3 # Visible|Enabled
326 else:
327 flags = 1 # Visible
328 return self.pushbutton(name, 236, self.h-27, 56, 17, flags, title, next)
329
330 def xbutton(self, name, title, next, xpos):
331 """Add a button with a given title, the tab-next button,
332 its name in the Control table, giving its x position; the
333 y-position is aligned with the other buttons.
334
335 Return the button, so that events can be associated"""
336 return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next)
337
338def add_ui(db):
339 x = y = 50
340 w = 370
341 h = 300
342 title = "[ProductName] Setup"
343
344 # see "Dialog Style Bits"
345 modal = 3 # visible | modal
346 modeless = 1 # visible
347 track_disk_space = 32
348
349 add_data(db, 'ActionText', uisample.ActionText)
350 add_data(db, 'UIText', uisample.UIText)
351
352 # Bitmaps
353 if not os.path.exists(srcdir+r"\PC\python_icon.exe"):
354 raise "Run icons.mak in PC directory"
355 add_data(db, "Binary",
356 [("PythonWin", msilib.Binary(r"%s\PCbuild\installer.bmp" % srcdir)), # 152x328 pixels
357 ("py.ico",msilib.Binary(srcdir+r"\PC\py.ico")),
358 ])
359 add_data(db, "Icon",
360 [("python_icon.exe", msilib.Binary(srcdir+r"\PC\python_icon.exe"))])
361
362 # Scripts
363 # CheckDir sets TargetExists if TARGETDIR exists.
364 # UpdateEditIDLE sets the REGISTRY.tcl component into
365 # the installed/uninstalled state according to both the
366 # Extensions and TclTk features.
367 if os.system("nmake /nologo /c /f msisupport.mak") != 0:
368 raise "'nmake /f msisupport.mak' failed"
369 add_data(db, "Binary", [("Script", msilib.Binary("msisupport.dll"))])
370 # See "Custom Action Type 1"
371 if msilib.Win64:
372 CheckDir = "CheckDir"
373 UpdateEditIDLE = "UpdateEditIDLE"
374 else:
375 CheckDir = "_CheckDir@4"
376 UpdateEditIDLE = "_UpdateEditIDLE@4"
377 add_data(db, "CustomAction",
378 [("CheckDir", 1, "Script", CheckDir)])
379 if have_tcl:
380 add_data(db, "CustomAction",
381 [("UpdateEditIDLE", 1, "Script", UpdateEditIDLE)])
382
383 # UI customization properties
384 add_data(db, "Property",
385 # See "DefaultUIFont Property"
386 [("DefaultUIFont", "DlgFont8"),
387 # See "ErrorDialog Style Bit"
388 ("ErrorDialog", "ErrorDlg"),
389 ("Progress1", "Install"), # modified in maintenance type dlg
390 ("Progress2", "installs"),
391 ("MaintenanceForm_Action", "Repair")])
392
393 # Fonts, see "TextStyle Table"
394 add_data(db, "TextStyle",
395 [("DlgFont8", "Tahoma", 9, None, 0),
396 ("DlgFontBold8", "Tahoma", 8, None, 1), #bold
397 ("VerdanaBold10", "Verdana", 10, None, 1),
398 ("VerdanaRed9", "Verdana", 9, 255, 0),
399 ])
400
401 compileargs = r'-Wi "[TARGETDIR]Lib\compileall.py" -f -x bad_coding|badsyntax|site-packages|py3_ "[TARGETDIR]Lib"'
402 lib2to3args = r'-c "import lib2to3.pygram, lib2to3.patcomp;lib2to3.patcomp.PatternCompiler()"'
403 # See "CustomAction Table"
404 add_data(db, "CustomAction", [
405 # msidbCustomActionTypeFirstSequence + msidbCustomActionTypeTextData + msidbCustomActionTypeProperty
406 # See "Custom Action Type 51",
407 # "Custom Action Execution Scheduling Options"
408 ("InitialTargetDir", 307, "TARGETDIR",
409 "[WindowsVolume]Python%s%s" % (major, minor)),
410 ("SetDLLDirToTarget", 307, "DLLDIR", "[TARGETDIR]"),
411 ("SetDLLDirToSystem32", 307, "DLLDIR", SystemFolderName),
412 # msidbCustomActionTypeExe + msidbCustomActionTypeSourceFile
413 # See "Custom Action Type 18"
414 ("CompilePyc", 18, "python.exe", compileargs),
415 ("CompilePyo", 18, "python.exe", "-O "+compileargs),
416 ("CompileGrammar", 18, "python.exe", lib2to3args),
417 ])
418
419 # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table"
420 # Numbers indicate sequence; see sequence.py for how these action integrate
421 add_data(db, "InstallUISequence",
422 [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140),
423 ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141),
424 ("InitialTargetDir", 'TARGETDIR=""', 750),
425 # In the user interface, assume all-users installation if privileged.
426 ("SetDLLDirToSystem32", 'DLLDIR="" and ' + sys32cond, 751),
427 ("SetDLLDirToTarget", 'DLLDIR="" and not ' + sys32cond, 752),
428 ("SelectDirectoryDlg", "Not Installed", 1230),
429 # XXX no support for resume installations yet
430 #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240),
431 ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250),
432 ("ProgressDlg", None, 1280)])
433 add_data(db, "AdminUISequence",
434 [("InitialTargetDir", 'TARGETDIR=""', 750),
435 ("SetDLLDirToTarget", 'DLLDIR=""', 751),
436 ])
437
438 # Execute Sequences
439 add_data(db, "InstallExecuteSequence",
440 [("InitialTargetDir", 'TARGETDIR=""', 750),
441 ("SetDLLDirToSystem32", 'DLLDIR="" and ' + sys32cond, 751),
442 ("SetDLLDirToTarget", 'DLLDIR="" and not ' + sys32cond, 752),
443 ("UpdateEditIDLE", None, 1050),
444 ("CompilePyc", "COMPILEALL", 6800),
445 ("CompilePyo", "COMPILEALL", 6801),
446 ("CompileGrammar", "COMPILEALL", 6802),
447 ])
448 add_data(db, "AdminExecuteSequence",
449 [("InitialTargetDir", 'TARGETDIR=""', 750),
450 ("SetDLLDirToTarget", 'DLLDIR=""', 751),
451 ("CompilePyc", "COMPILEALL", 6800),
452 ("CompilePyo", "COMPILEALL", 6801),
453 ("CompileGrammar", "COMPILEALL", 6802),
454 ])
455
456 #####################################################################
457 # Standard dialogs: FatalError, UserExit, ExitDialog
458 fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title,
459 "Finish", "Finish", "Finish")
460 fatal.title("[ProductName] Installer ended prematurely")
461 fatal.back("< Back", "Finish", active = 0)
462 fatal.cancel("Cancel", "Back", active = 0)
463 fatal.text("Description1", 135, 70, 220, 80, 0x30003,
464 "[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.")
465 fatal.text("Description2", 135, 155, 220, 20, 0x30003,
466 "Click the Finish button to exit the Installer.")
467 c=fatal.next("Finish", "Cancel", name="Finish")
468 # See "ControlEvent Table". Parameters are the event, the parameter
469 # to the action, and optionally the condition for the event, and the order
470 # of events.
471 c.event("EndDialog", "Exit")
472
473 user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title,
474 "Finish", "Finish", "Finish")
475 user_exit.title("[ProductName] Installer was interrupted")
476 user_exit.back("< Back", "Finish", active = 0)
477 user_exit.cancel("Cancel", "Back", active = 0)
478 user_exit.text("Description1", 135, 70, 220, 80, 0x30003,
479 "[ProductName] setup was interrupted. Your system has not been modified. "
480 "To install this program at a later time, please run the installation again.")
481 user_exit.text("Description2", 135, 155, 220, 20, 0x30003,
482 "Click the Finish button to exit the Installer.")
483 c = user_exit.next("Finish", "Cancel", name="Finish")
484 c.event("EndDialog", "Exit")
485
486 exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title,
487 "Finish", "Finish", "Finish")
488 exit_dialog.title("Completing the [ProductName] Installer")
489 exit_dialog.back("< Back", "Finish", active = 0)
490 exit_dialog.cancel("Cancel", "Back", active = 0)
491 exit_dialog.text("Acknowledgements", 135, 95, 220, 120, 0x30003,
492 "Special Windows thanks to:\n"
493 " Mark Hammond, without whose years of freely \n"
494 " shared Windows expertise, Python for Windows \n"
495 " would still be Python for DOS.")
496
497 c = exit_dialog.text("warning", 135, 200, 220, 40, 0x30003,
498 "{\\VerdanaRed9}Warning: Python 2.5.x is the last "
499 "Python release for Windows 9x.")
500 c.condition("Hide", "NOT Version9X")
501
502 exit_dialog.text("Description", 135, 235, 220, 20, 0x30003,
503 "Click the Finish button to exit the Installer.")
504 c = exit_dialog.next("Finish", "Cancel", name="Finish")
505 c.event("EndDialog", "Return")
506
507 #####################################################################
508 # Required dialog: FilesInUse, ErrorDlg
509 inuse = PyDialog(db, "FilesInUse",
510 x, y, w, h,
511 19, # KeepModeless|Modal|Visible
512 title,
513 "Retry", "Retry", "Retry", bitmap=False)
514 inuse.text("Title", 15, 6, 200, 15, 0x30003,
515 r"{\DlgFontBold8}Files in Use")
516 inuse.text("Description", 20, 23, 280, 20, 0x30003,
517 "Some files that need to be updated are currently in use.")
518 inuse.text("Text", 20, 55, 330, 50, 3,
519 "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.")
520 inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess",
521 None, None, None)
522 c=inuse.back("Exit", "Ignore", name="Exit")
523 c.event("EndDialog", "Exit")
524 c=inuse.next("Ignore", "Retry", name="Ignore")
525 c.event("EndDialog", "Ignore")
526 c=inuse.cancel("Retry", "Exit", name="Retry")
527 c.event("EndDialog","Retry")
528
529
530 # See "Error Dialog". See "ICE20" for the required names of the controls.
531 error = Dialog(db, "ErrorDlg",
532 50, 10, 330, 101,
533 65543, # Error|Minimize|Modal|Visible
534 title,
535 "ErrorText", None, None)
536 error.text("ErrorText", 50,9,280,48,3, "")
537 error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None)
538 error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo")
539 error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes")
540 error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort")
541 error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel")
542 error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore")
543 error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk")
544 error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry")
545
546 #####################################################################
547 # Global "Query Cancel" dialog
548 cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title,
549 "No", "No", "No")
550 cancel.text("Text", 48, 15, 194, 30, 3,
551 "Are you sure you want to cancel [ProductName] installation?")
552 cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
553 "py.ico", None, None)
554 c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No")
555 c.event("EndDialog", "Exit")
556
557 c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes")
558 c.event("EndDialog", "Return")
559
560 #####################################################################
561 # Global "Wait for costing" dialog
562 costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title,
563 "Return", "Return", "Return")
564 costing.text("Text", 48, 15, 194, 30, 3,
565 "Please wait while the installer finishes determining your disk space requirements.")
566 costing.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
567 "py.ico", None, None)
568 c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None)
569 c.event("EndDialog", "Exit")
570
571 #####################################################################
572 # Preparation dialog: no user input except cancellation
573 prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title,
574 "Cancel", "Cancel", "Cancel")
575 prep.text("Description", 135, 70, 220, 40, 0x30003,
576 "Please wait while the Installer prepares to guide you through the installation.")
577 prep.title("Welcome to the [ProductName] Installer")
578 c=prep.text("ActionText", 135, 110, 220, 20, 0x30003, "Pondering...")
579 c.mapping("ActionText", "Text")
580 c=prep.text("ActionData", 135, 135, 220, 30, 0x30003, None)
581 c.mapping("ActionData", "Text")
582 prep.back("Back", None, active=0)
583 prep.next("Next", None, active=0)
584 c=prep.cancel("Cancel", None)
585 c.event("SpawnDialog", "CancelDlg")
586
587 #####################################################################
588 # Target directory selection
589 seldlg = PyDialog(db, "SelectDirectoryDlg", x, y, w, h, modal, title,
590 "Next", "Next", "Cancel")
591 seldlg.title("Select Destination Directory")
592 c = seldlg.text("Existing", 135, 25, 235, 30, 0x30003,
593 "{\VerdanaRed9}This update will replace your existing [ProductLine] installation.")
594 c.condition("Hide", 'REMOVEOLDVERSION="" and REMOVEOLDSNAPSHOT=""')
595 seldlg.text("Description", 135, 50, 220, 40, 0x30003,
596 "Please select a directory for the [ProductName] files.")
597
598 seldlg.back("< Back", None, active=0)
599 c = seldlg.next("Next >", "Cancel")
600 c.event("DoAction", "CheckDir", "TargetExistsOk<>1", order=1)
601 # If the target exists, but we found that we are going to remove old versions, don't bother
602 # confirming that the target directory exists. Strictly speaking, we should determine that
603 # the target directory is indeed the target of the product that we are going to remove, but
604 # I don't know how to do that.
605 c.event("SpawnDialog", "ExistingDirectoryDlg", 'TargetExists=1 and REMOVEOLDVERSION="" and REMOVEOLDSNAPSHOT=""', 2)
606 c.event("SetTargetPath", "TARGETDIR", 'TargetExists=0 or REMOVEOLDVERSION<>"" or REMOVEOLDSNAPSHOT<>""', 3)
607 c.event("SpawnWaitDialog", "WaitForCostingDlg", "CostingComplete=1", 4)
608 c.event("NewDialog", "SelectFeaturesDlg", 'TargetExists=0 or REMOVEOLDVERSION<>"" or REMOVEOLDSNAPSHOT<>""', 5)
609
610 c = seldlg.cancel("Cancel", "DirectoryCombo")
611 c.event("SpawnDialog", "CancelDlg")
612
613 seldlg.control("DirectoryCombo", "DirectoryCombo", 135, 70, 172, 80, 393219,
614 "TARGETDIR", None, "DirectoryList", None)
615 seldlg.control("DirectoryList", "DirectoryList", 135, 90, 208, 136, 3, "TARGETDIR",
616 None, "PathEdit", None)
617 seldlg.control("PathEdit", "PathEdit", 135, 230, 206, 16, 3, "TARGETDIR", None, "Next", None)
618 c = seldlg.pushbutton("Up", 306, 70, 18, 18, 3, "Up", None)
619 c.event("DirectoryListUp", "0")
620 c = seldlg.pushbutton("NewDir", 324, 70, 30, 18, 3, "New", None)
621 c.event("DirectoryListNew", "0")
622
623 #####################################################################
624 # SelectFeaturesDlg
625 features = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal|track_disk_space,
626 title, "Tree", "Next", "Cancel")
627 features.title("Customize [ProductName]")
628 features.text("Description", 135, 35, 220, 15, 0x30003,
629 "Select the way you want features to be installed.")
630 features.text("Text", 135,45,220,30, 3,
631 "Click on the icons in the tree below to change the way features will be installed.")
632
633 c=features.back("< Back", "Next")
634 c.event("NewDialog", "SelectDirectoryDlg")
635
636 c=features.next("Next >", "Cancel")
637 c.mapping("SelectionNoItems", "Enabled")
638 c.event("SpawnDialog", "DiskCostDlg", "OutOfDiskSpace=1", order=1)
639 c.event("EndDialog", "Return", "OutOfDiskSpace<>1", order=2)
640
641 c=features.cancel("Cancel", "Tree")
642 c.event("SpawnDialog", "CancelDlg")
643
644 # The browse property is not used, since we have only a single target path (selected already)
645 features.control("Tree", "SelectionTree", 135, 75, 220, 95, 7, "_BrowseProperty",
646 "Tree of selections", "Back", None)
647
648 #c=features.pushbutton("Reset", 42, 243, 56, 17, 3, "Reset", "DiskCost")
649 #c.mapping("SelectionNoItems", "Enabled")
650 #c.event("Reset", "0")
651
652 features.control("Box", "GroupBox", 135, 170, 225, 90, 1, None, None, None, None)
653
654 c=features.xbutton("DiskCost", "Disk &Usage", None, 0.10)
655 c.mapping("SelectionNoItems","Enabled")
656 c.event("SpawnDialog", "DiskCostDlg")
657
658 c=features.xbutton("Advanced", "Advanced", None, 0.30)
659 c.event("SpawnDialog", "AdvancedDlg")
660
661 c=features.text("ItemDescription", 140, 180, 210, 30, 3,
662 "Multiline description of the currently selected item.")
663 c.mapping("SelectionDescription","Text")
664
665 c=features.text("ItemSize", 140, 210, 210, 45, 3,
666 "The size of the currently selected item.")
667 c.mapping("SelectionSize", "Text")
668
669 #####################################################################
670 # Disk cost
671 cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title,
672 "OK", "OK", "OK", bitmap=False)
673 cost.text("Title", 15, 6, 200, 15, 0x30003,
674 "{\DlgFontBold8}Disk Space Requirements")
675 cost.text("Description", 20, 20, 280, 20, 0x30003,
676 "The disk space required for the installation of the selected features.")
677 cost.text("Text", 20, 53, 330, 60, 3,
678 "The highlighted volumes (if any) do not have enough disk space "
679 "available for the currently selected features. You can either "
680 "remove some files from the highlighted volumes, or choose to "
681 "install less features onto local drive(s), or select different "
682 "destination drive(s).")
683 cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223,
684 None, "{120}{70}{70}{70}{70}", None, None)
685 cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return")
686
687 #####################################################################
688 # WhichUsers Dialog. Only available on NT, and for privileged users.
689 # This must be run before FindRelatedProducts, because that will
690 # take into account whether the previous installation was per-user
691 # or per-machine. We currently don't support going back to this
692 # dialog after "Next" was selected; to support this, we would need to
693 # find how to reset the ALLUSERS property, and how to re-run
694 # FindRelatedProducts.
695 # On Windows9x, the ALLUSERS property is ignored on the command line
696 # and in the Property table, but installer fails according to the documentation
697 # if a dialog attempts to set ALLUSERS.
698 whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title,
699 "AdminInstall", "Next", "Cancel")
700 whichusers.title("Select whether to install [ProductName] for all users of this computer.")
701 # A radio group with two options: allusers, justme
702 g = whichusers.radiogroup("AdminInstall", 135, 60, 235, 80, 3,
703 "WhichUsers", "", "Next")
704 g.condition("Disable", "VersionNT=600") # Not available on Vista and Windows 2008
705 g.add("ALL", 0, 5, 150, 20, "Install for all users")
706 g.add("JUSTME", 0, 25, 235, 20, "Install just for me (not available on Windows Vista)")
707
708 whichusers.back("Back", None, active=0)
709
710 c = whichusers.next("Next >", "Cancel")
711 c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1)
712 c.event("EndDialog", "Return", order = 2)
713
714 c = whichusers.cancel("Cancel", "AdminInstall")
715 c.event("SpawnDialog", "CancelDlg")
716
717 #####################################################################
718 # Advanced Dialog.
719 advanced = PyDialog(db, "AdvancedDlg", x, y, w, h, modal, title,
720 "CompilePyc", "Ok", "Ok")
721 advanced.title("Advanced Options for [ProductName]")
722 # A radio group with two options: allusers, justme
723 advanced.checkbox("CompilePyc", 135, 60, 230, 50, 3,
724 "COMPILEALL", "Compile .py files to byte code after installation", "Ok")
725
726 c = advanced.cancel("Ok", "CompilePyc", name="Ok") # Button just has location of cancel button.
727 c.event("EndDialog", "Return")
728
729 #####################################################################
730 # Existing Directory dialog
731 dlg = Dialog(db, "ExistingDirectoryDlg", 50, 30, 200, 80, modal, title,
732 "No", "No", "No")
733 dlg.text("Title", 10, 20, 180, 40, 3,
734 "[TARGETDIR] exists. Are you sure you want to overwrite existing files?")
735 c=dlg.pushbutton("Yes", 30, 60, 55, 17, 3, "Yes", "No")
736 c.event("[TargetExists]", "0", order=1)
737 c.event("[TargetExistsOk]", "1", order=2)
738 c.event("EndDialog", "Return", order=3)
739 c=dlg.pushbutton("No", 115, 60, 55, 17, 3, "No", "Yes")
740 c.event("EndDialog", "Return")
741
742 #####################################################################
743 # Installation Progress dialog (modeless)
744 progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title,
745 "Cancel", "Cancel", "Cancel", bitmap=False)
746 progress.text("Title", 20, 15, 200, 15, 0x30003,
747 "{\DlgFontBold8}[Progress1] [ProductName]")
748 progress.text("Text", 35, 65, 300, 30, 3,
749 "Please wait while the Installer [Progress2] [ProductName]. "
750 "This may take several minutes.")
751 progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:")
752
753 c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...")
754 c.mapping("ActionText", "Text")
755
756 #c=progress.text("ActionData", 35, 140, 300, 20, 3, None)
757 #c.mapping("ActionData", "Text")
758
759 c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537,
760 None, "Progress done", None, None)
761 c.mapping("SetProgress", "Progress")
762
763 progress.back("< Back", "Next", active=False)
764 progress.next("Next >", "Cancel", active=False)
765 progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg")
766
767 # Maintenance type: repair/uninstall
768 maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title,
769 "Next", "Next", "Cancel")
770 maint.title("Welcome to the [ProductName] Setup Wizard")
771 maint.text("BodyText", 135, 63, 230, 42, 3,
772 "Select whether you want to repair or remove [ProductName].")
773 g=maint.radiogroup("RepairRadioGroup", 135, 108, 230, 60, 3,
774 "MaintenanceForm_Action", "", "Next")
775 g.add("Change", 0, 0, 200, 17, "&Change [ProductName]")
776 g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]")
777 g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]")
778
779 maint.back("< Back", None, active=False)
780 c=maint.next("Finish", "Cancel")
781 # Change installation: Change progress dialog to "Change", then ask
782 # for feature selection
783 c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1)
784 c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2)
785
786 # Reinstall: Change progress dialog to "Repair", then invoke reinstall
787 # Also set list of reinstalled features to "ALL"
788 c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5)
789 c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6)
790 c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7)
791 c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8)
792
793 # Uninstall: Change progress to "Remove", then invoke uninstall
794 # Also set list of removed features to "ALL"
795 c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11)
796 c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12)
797 c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13)
798 c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14)
799
800 # Close dialog when maintenance action scheduled
801 c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20)
802 c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21)
803
804 maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg")
805
806
807# See "Feature Table". The feature level is 1 for all features,
808# and the feature attributes are 0 for the DefaultFeature, and
809# FollowParent for all other features. The numbers are the Display
810# column.
811def add_features(db):
812 # feature attributes:
813 # msidbFeatureAttributesFollowParent == 2
814 # msidbFeatureAttributesDisallowAdvertise == 8
815 # Features that need to be installed with together with the main feature
816 # (i.e. additional Python libraries) need to follow the parent feature.
817 # Features that have no advertisement trigger (e.g. the test suite)
818 # must not support advertisement
819 global default_feature, tcltk, htmlfiles, tools, testsuite, ext_feature, private_crt
820 default_feature = Feature(db, "DefaultFeature", "Python",
821 "Python Interpreter and Libraries",
822 1, directory = "TARGETDIR")
823 shared_crt = Feature(db, "SharedCRT", "MSVCRT", "C Run-Time (system-wide)", 0,
824 level=0)
825 private_crt = Feature(db, "PrivateCRT", "MSVCRT", "C Run-Time (private)", 0,
826 level=0)
827 add_data(db, "Condition", [("SharedCRT", 1, sys32cond),
828 ("PrivateCRT", 1, "not "+sys32cond)])
829 # We don't support advertisement of extensions
830 ext_feature = Feature(db, "Extensions", "Register Extensions",
831 "Make this Python installation the default Python installation", 3,
832 parent = default_feature, attributes=2|8)
833 if have_tcl:
834 tcltk = Feature(db, "TclTk", "Tcl/Tk", "Tkinter, IDLE, pydoc", 5,
835 parent = default_feature, attributes=2)
836 htmlfiles = Feature(db, "Documentation", "Documentation",
837 "Python HTMLHelp File", 7, parent = default_feature)
838 tools = Feature(db, "Tools", "Utility Scripts",
839 "Python utility scripts (Tools/", 9,
840 parent = default_feature, attributes=2)
841 testsuite = Feature(db, "Testsuite", "Test suite",
842 "Python test suite (Lib/test/)", 11,
843 parent = default_feature, attributes=2|8)
844
845def extract_msvcr90():
846 # Find the redistributable files
847 if msilib.Win64:
848 arch = "amd64"
849 else:
850 arch = "x86"
851 dir = os.path.join(os.environ['VS90COMNTOOLS'], r"..\..\VC\redist\%s\Microsoft.VC90.CRT" % arch)
852
853 result = []
854 installer = msilib.MakeInstaller()
855 # omit msvcm90 and msvcp90, as they aren't really needed
856 files = ["Microsoft.VC90.CRT.manifest", "msvcr90.dll"]
857 for f in files:
858 path = os.path.join(dir, f)
859 kw = {'src':path}
860 if f.endswith('.dll'):
861 kw['version'] = installer.FileVersion(path, 0)
862 kw['language'] = installer.FileVersion(path, 1)
863 result.append((f, kw))
864 return result
865
866def generate_license():
867 import shutil, glob
868 out = open("LICENSE.txt", "w")
869 shutil.copyfileobj(open(os.path.join(srcdir, "LICENSE")), out)
870 shutil.copyfileobj(open("crtlicense.txt"), out)
871 for name, pat, file in (("bzip2","bzip2-*", "LICENSE"),
872 ("Berkeley DB", "db-*", "LICENSE"),
873 ("openssl", "openssl-*", "LICENSE"),
874 ("Tcl", "tcl8*", "license.terms"),
875 ("Tk", "tk8*", "license.terms"),
876 ("Tix", "tix-*", "license.terms")):
877 out.write("\nThis copy of Python includes a copy of %s, which is licensed under the following terms:\n\n" % name)
878 dirs = glob.glob(srcdir+"/../"+pat)
879 if not dirs:
880 raise ValueError, "Could not find "+srcdir+"/../"+pat
881 if len(dirs) > 2:
882 raise ValueError, "Multiple copies of "+pat
883 dir = dirs[0]
884 shutil.copyfileobj(open(os.path.join(dir, file)), out)
885 out.close()
886
887
888class PyDirectory(Directory):
889 """By default, all components in the Python installer
890 can run from source."""
891 def __init__(self, *args, **kw):
892 if not kw.has_key("componentflags"):
893 kw['componentflags'] = 2 #msidbComponentAttributesOptional
894 Directory.__init__(self, *args, **kw)
895
896# See "File Table", "Component Table", "Directory Table",
897# "FeatureComponents Table"
898def add_files(db):
899 cab = CAB("python")
900 tmpfiles = []
901 # Add all executables, icons, text files into the TARGETDIR component
902 root = PyDirectory(db, cab, None, srcdir, "TARGETDIR", "SourceDir")
903 default_feature.set_current()
904 if not msilib.Win64:
905 root.add_file("%s/w9xpopen.exe" % PCBUILD)
906 root.add_file("README.txt", src="README")
907 root.add_file("NEWS.txt", src="Misc/NEWS")
908 generate_license()
909 root.add_file("LICENSE.txt", src=os.path.abspath("LICENSE.txt"))
910 root.start_component("python.exe", keyfile="python.exe")
911 root.add_file("%s/python.exe" % PCBUILD)
912 root.start_component("pythonw.exe", keyfile="pythonw.exe")
913 root.add_file("%s/pythonw.exe" % PCBUILD)
914
915 # msidbComponentAttributesSharedDllRefCount = 8, see "Component Table"
916 dlldir = PyDirectory(db, cab, root, srcdir, "DLLDIR", ".")
917
918 pydll = "python%s%s.dll" % (major, minor)
919 pydllsrc = os.path.join(srcdir, PCBUILD, pydll)
920 dlldir.start_component("DLLDIR", flags = 8, keyfile = pydll, uuid = pythondll_uuid)
921 installer = msilib.MakeInstaller()
922 pyversion = installer.FileVersion(pydllsrc, 0)
923 if not snapshot:
924 # For releases, the Python DLL has the same version as the
925 # installer package.
926 assert pyversion.split(".")[:3] == current_version.split(".")
927 dlldir.add_file("%s/python%s%s.dll" % (PCBUILD, major, minor),
928 version=pyversion,
929 language=installer.FileVersion(pydllsrc, 1))
930 DLLs = PyDirectory(db, cab, root, srcdir + "/" + PCBUILD, "DLLs", "DLLS|DLLs")
931
932 # msvcr90.dll: Need to place the DLL and the manifest into the root directory,
933 # plus another copy of the manifest in the DLLs directory, with the manifest
934 # pointing to the root directory
935 root.start_component("msvcr90", feature=private_crt)
936 # Results are ID,keyword pairs
937 manifest, crtdll = extract_msvcr90()
938 root.add_file(manifest[0], **manifest[1])
939 root.add_file(crtdll[0], **crtdll[1])
940 # Copy the manifest
941 # Actually, don't do that anymore - no DLL in DLLs should have a manifest
942 # dependency on msvcr90.dll anymore, so this should not be necessary
943 #manifest_dlls = manifest[0]+".root"
944 #open(manifest_dlls, "w").write(open(manifest[1]['src']).read().replace("msvcr","../msvcr"))
945 #DLLs.start_component("msvcr90_dlls", feature=private_crt)
946 #DLLs.add_file(manifest[0], src=os.path.abspath(manifest_dlls))
947
948 # Now start the main component for the DLLs directory;
949 # no regular files have been added to the directory yet.
950 DLLs.start_component()
951
952 # Check if _ctypes.pyd exists
953 have_ctypes = os.path.exists(srcdir+"/%s/_ctypes.pyd" % PCBUILD)
954 if not have_ctypes:
955 print "WARNING: _ctypes.pyd not found, ctypes will not be included"
956 extensions.remove("_ctypes.pyd")
957
958 # Add all .py files in Lib, except lib-tk, test
959 dirs={}
960 pydirs = [(root,"Lib")]
961 while pydirs:
962 # Commit every now and then, or else installer will complain
963 db.Commit()
964 parent, dir = pydirs.pop()
965 if dir == ".svn" or dir.startswith("plat-"):
966 continue
967 elif dir in ["lib-tk", "idlelib", "Icons"]:
968 if not have_tcl:
969 continue
970 tcltk.set_current()
971 elif dir in ['test', 'tests', 'data', 'output']:
972 # test: Lib, Lib/email, Lib/bsddb, Lib/ctypes, Lib/sqlite3
973 # tests: Lib/distutils
974 # data: Lib/email/test
975 # output: Lib/test
976 testsuite.set_current()
977 elif not have_ctypes and dir == "ctypes":
978 continue
979 else:
980 default_feature.set_current()
981 lib = PyDirectory(db, cab, parent, dir, dir, "%s|%s" % (parent.make_short(dir), dir))
982 # Add additional files
983 dirs[dir]=lib
984 lib.glob("*.txt")
985 if dir=='site-packages':
986 lib.add_file("README.txt", src="README")
987 continue
988 files = lib.glob("*.py")
989 files += lib.glob("*.pyw")
990 if files:
991 # Add an entry to the RemoveFile table to remove bytecode files.
992 lib.remove_pyc()
993 if dir.endswith('.egg-info'):
994 lib.add_file('entry_points.txt')
995 lib.add_file('PKG-INFO')
996 lib.add_file('top_level.txt')
997 lib.add_file('zip-safe')
998 continue
999 if dir=='test' and parent.physical=='Lib':
1000 lib.add_file("185test.db")
1001 lib.add_file("audiotest.au")
1002 lib.add_file("cfgparser.1")
1003 lib.add_file("sgml_input.html")
1004 lib.add_file("test.xml")
1005 lib.add_file("test.xml.out")
1006 lib.add_file("testtar.tar")
1007 lib.add_file("test_difflib_expect.html")
1008 lib.add_file("check_soundcard.vbs")
1009 lib.add_file("empty.vbs")
1010 lib.add_file("Sine-1000Hz-300ms.aif")
1011 lib.glob("*.uue")
1012 lib.glob("*.pem")
1013 lib.glob("*.pck")
1014 lib.add_file("readme.txt", src="README")
1015 lib.add_file("zipdir.zip")
1016 if dir=='decimaltestdata':
1017 lib.glob("*.decTest")
1018 if dir=='output':
1019 lib.glob("test_*")
1020 if dir=='idlelib':
1021 lib.glob("*.def")
1022 lib.add_file("idle.bat")
1023 if dir=="Icons":
1024 lib.glob("*.gif")
1025 lib.add_file("idle.icns")
1026 if dir=="command" and parent.physical=="distutils":
1027 lib.glob("wininst*.exe")
1028 if dir=="setuptools":
1029 lib.add_file("cli.exe")
1030 lib.add_file("gui.exe")
1031 if dir=="lib2to3":
1032 lib.removefile("pickle", "*.pickle")
1033 if dir=="data" and parent.physical=="test" and parent.basedir.physical=="email":
1034 # This should contain all non-.svn files listed in subversion
1035 for f in os.listdir(lib.absolute):
1036 if f.endswith(".txt") or f==".svn":continue
1037 if f.endswith(".au") or f.endswith(".gif"):
1038 lib.add_file(f)
1039 else:
1040 print "WARNING: New file %s in email/test/data" % f
1041 for f in os.listdir(lib.absolute):
1042 if os.path.isdir(os.path.join(lib.absolute, f)):
1043 pydirs.append((lib, f))
1044 # Add DLLs
1045 default_feature.set_current()
1046 lib = DLLs
1047 lib.add_file("py.ico", src=srcdir+"/PC/py.ico")
1048 lib.add_file("pyc.ico", src=srcdir+"/PC/pyc.ico")
1049 dlls = []
1050 tclfiles = []
1051 for f in extensions:
1052 if f=="_tkinter.pyd":
1053 continue
1054 if not os.path.exists(srcdir + "/" + PCBUILD + "/" + f):
1055 print "WARNING: Missing extension", f
1056 continue
1057 dlls.append(f)
1058 lib.add_file(f)
1059 # Add sqlite
1060 if msilib.msi_type=="Intel64;1033":
1061 sqlite_arch = "/ia64"
1062 elif msilib.msi_type=="x64;1033":
1063 sqlite_arch = "/amd64"
1064 tclsuffix = "64"
1065 else:
1066 sqlite_arch = ""
1067 tclsuffix = ""
1068 lib.add_file("sqlite3.dll")
1069 if have_tcl:
1070 if not os.path.exists("%s/%s/_tkinter.pyd" % (srcdir, PCBUILD)):
1071 print "WARNING: Missing _tkinter.pyd"
1072 else:
1073 lib.start_component("TkDLLs", tcltk)
1074 lib.add_file("_tkinter.pyd")
1075 dlls.append("_tkinter.pyd")
1076 tcldir = os.path.normpath(srcdir+("/../tcltk%s/bin" % tclsuffix))
1077 for f in glob.glob1(tcldir, "*.dll"):
1078 lib.add_file(f, src=os.path.join(tcldir, f))
1079 # check whether there are any unknown extensions
1080 for f in glob.glob1(srcdir+"/"+PCBUILD, "*.pyd"):
1081 if f.endswith("_d.pyd"): continue # debug version
1082 if f in dlls: continue
1083 print "WARNING: Unknown extension", f
1084
1085 # Add headers
1086 default_feature.set_current()
1087 lib = PyDirectory(db, cab, root, "include", "include", "INCLUDE|include")
1088 lib.glob("*.h")
1089 lib.add_file("pyconfig.h", src="../PC/pyconfig.h")
1090 # Add import libraries
1091 lib = PyDirectory(db, cab, root, PCBUILD, "libs", "LIBS|libs")
1092 for f in dlls:
1093 lib.add_file(f.replace('pyd','lib'))
1094 lib.add_file('python%s%s.lib' % (major, minor))
1095 # Add the mingw-format library
1096 if have_mingw:
1097 lib.add_file('libpython%s%s.a' % (major, minor))
1098 if have_tcl:
1099 # Add Tcl/Tk
1100 tcldirs = [(root, '../tcltk%s/lib' % tclsuffix, 'tcl')]
1101 tcltk.set_current()
1102 while tcldirs:
1103 parent, phys, dir = tcldirs.pop()
1104 lib = PyDirectory(db, cab, parent, phys, dir, "%s|%s" % (parent.make_short(dir), dir))
1105 if not os.path.exists(lib.absolute):
1106 continue
1107 for f in os.listdir(lib.absolute):
1108 if os.path.isdir(os.path.join(lib.absolute, f)):
1109 tcldirs.append((lib, f, f))
1110 else:
1111 lib.add_file(f)
1112 # Add tools
1113 tools.set_current()
1114 tooldir = PyDirectory(db, cab, root, "Tools", "Tools", "TOOLS|Tools")
1115 for f in ['i18n', 'pynche', 'Scripts', 'versioncheck', 'webchecker']:
1116 lib = PyDirectory(db, cab, tooldir, f, f, "%s|%s" % (tooldir.make_short(f), f))
1117 lib.glob("*.py")
1118 lib.glob("*.pyw", exclude=['pydocgui.pyw'])
1119 lib.remove_pyc()
1120 lib.glob("*.txt")
1121 if f == "pynche":
1122 x = PyDirectory(db, cab, lib, "X", "X", "X|X")
1123 x.glob("*.txt")
1124 if os.path.exists(os.path.join(lib.absolute, "README")):
1125 lib.add_file("README.txt", src="README")
1126 if f == 'Scripts':
1127 lib.add_file("2to3.py", src="2to3")
1128 if have_tcl:
1129 lib.start_component("pydocgui.pyw", tcltk, keyfile="pydocgui.pyw")
1130 lib.add_file("pydocgui.pyw")
1131 # Add documentation
1132 htmlfiles.set_current()
1133 lib = PyDirectory(db, cab, root, "Doc", "Doc", "DOC|Doc")
1134 lib.start_component("documentation", keyfile=docfile)
1135 lib.add_file(docfile, src="build/htmlhelp/"+docfile)
1136
1137 cab.commit(db)
1138
1139 for f in tmpfiles:
1140 os.unlink(f)
1141
1142# See "Registry Table", "Component Table"
1143def add_registry(db):
1144 # File extensions, associated with the REGISTRY.def component
1145 # IDLE verbs depend on the tcltk feature.
1146 # msidbComponentAttributesRegistryKeyPath = 4
1147 # -1 for Root specifies "dependent on ALLUSERS property"
1148 tcldata = []
1149 if have_tcl:
1150 tcldata = [
1151 ("REGISTRY.tcl", msilib.gen_uuid(), "TARGETDIR", registry_component, None,
1152 "py.IDLE")]
1153 add_data(db, "Component",
1154 # msidbComponentAttributesRegistryKeyPath = 4
1155 [("REGISTRY", msilib.gen_uuid(), "TARGETDIR", registry_component, None,
1156 "InstallPath"),
1157 ("REGISTRY.doc", msilib.gen_uuid(), "TARGETDIR", registry_component, None,
1158 "Documentation"),
1159 ("REGISTRY.def", msilib.gen_uuid(), "TARGETDIR", registry_component,
1160 None, None)] + tcldata)
1161 # See "FeatureComponents Table".
1162 # The association between TclTk and pythonw.exe is necessary to make ICE59
1163 # happy, because the installer otherwise believes that the IDLE and PyDoc
1164 # shortcuts might get installed without pythonw.exe being install. This
1165 # is not true, since installing TclTk will install the default feature, which
1166 # will cause pythonw.exe to be installed.
1167 # REGISTRY.tcl is not associated with any feature, as it will be requested
1168 # through a custom action
1169 tcldata = []
1170 if have_tcl:
1171 tcldata = [(tcltk.id, "pythonw.exe")]
1172 add_data(db, "FeatureComponents",
1173 [(default_feature.id, "REGISTRY"),
1174 (htmlfiles.id, "REGISTRY.doc"),
1175 (ext_feature.id, "REGISTRY.def")] +
1176 tcldata
1177 )
1178 # Extensions are not advertised. For advertised extensions,
1179 # we would need separate binaries that install along with the
1180 # extension.
1181 pat = r"Software\Classes\%sPython.%sFile\shell\%s\command"
1182 ewi = "Edit with IDLE"
1183 pat2 = r"Software\Classes\%sPython.%sFile\DefaultIcon"
1184 pat3 = r"Software\Classes\%sPython.%sFile"
1185 pat4 = r"Software\Classes\%sPython.%sFile\shellex\DropHandler"
1186 tcl_verbs = []
1187 if have_tcl:
1188 tcl_verbs=[
1189 ("py.IDLE", -1, pat % (testprefix, "", ewi), "",
1190 r'"[TARGETDIR]pythonw.exe" "[TARGETDIR]Lib\idlelib\idle.pyw" -n -e "%1"',
1191 "REGISTRY.tcl"),
1192 ("pyw.IDLE", -1, pat % (testprefix, "NoCon", ewi), "",
1193 r'"[TARGETDIR]pythonw.exe" "[TARGETDIR]Lib\idlelib\idle.pyw" -n -e "%1"',
1194 "REGISTRY.tcl"),
1195 ]
1196 add_data(db, "Registry",
1197 [# Extensions
1198 ("py.ext", -1, r"Software\Classes\."+ext, "",
1199 "Python.File", "REGISTRY.def"),
1200 ("pyw.ext", -1, r"Software\Classes\."+ext+'w', "",
1201 "Python.NoConFile", "REGISTRY.def"),
1202 ("pyc.ext", -1, r"Software\Classes\."+ext+'c', "",
1203 "Python.CompiledFile", "REGISTRY.def"),
1204 ("pyo.ext", -1, r"Software\Classes\."+ext+'o', "",
1205 "Python.CompiledFile", "REGISTRY.def"),
1206 # MIME types
1207 ("py.mime", -1, r"Software\Classes\."+ext, "Content Type",
1208 "text/plain", "REGISTRY.def"),
1209 ("pyw.mime", -1, r"Software\Classes\."+ext+'w', "Content Type",
1210 "text/plain", "REGISTRY.def"),
1211 #Verbs
1212 ("py.open", -1, pat % (testprefix, "", "open"), "",
1213 r'"[TARGETDIR]python.exe" "%1" %*', "REGISTRY.def"),
1214 ("pyw.open", -1, pat % (testprefix, "NoCon", "open"), "",
1215 r'"[TARGETDIR]pythonw.exe" "%1" %*', "REGISTRY.def"),
1216 ("pyc.open", -1, pat % (testprefix, "Compiled", "open"), "",
1217 r'"[TARGETDIR]python.exe" "%1" %*', "REGISTRY.def"),
1218 ] + tcl_verbs + [
1219 #Icons
1220 ("py.icon", -1, pat2 % (testprefix, ""), "",
1221 r'[DLLs]py.ico', "REGISTRY.def"),
1222 ("pyw.icon", -1, pat2 % (testprefix, "NoCon"), "",
1223 r'[DLLs]py.ico', "REGISTRY.def"),
1224 ("pyc.icon", -1, pat2 % (testprefix, "Compiled"), "",
1225 r'[DLLs]pyc.ico', "REGISTRY.def"),
1226 # Descriptions
1227 ("py.txt", -1, pat3 % (testprefix, ""), "",
1228 "Python File", "REGISTRY.def"),
1229 ("pyw.txt", -1, pat3 % (testprefix, "NoCon"), "",
1230 "Python File (no console)", "REGISTRY.def"),
1231 ("pyc.txt", -1, pat3 % (testprefix, "Compiled"), "",
1232 "Compiled Python File", "REGISTRY.def"),
1233 # Drop Handler
1234 ("py.drop", -1, pat4 % (testprefix, ""), "",
1235 "{60254CA5-953B-11CF-8C96-00AA00B8708C}", "REGISTRY.def"),
1236 ("pyw.drop", -1, pat4 % (testprefix, "NoCon"), "",
1237 "{60254CA5-953B-11CF-8C96-00AA00B8708C}", "REGISTRY.def"),
1238 ("pyc.drop", -1, pat4 % (testprefix, "Compiled"), "",
1239 "{60254CA5-953B-11CF-8C96-00AA00B8708C}", "REGISTRY.def"),
1240 ])
1241
1242 # Registry keys
1243 prefix = r"Software\%sPython\PythonCore\%s" % (testprefix, short_version)
1244 add_data(db, "Registry",
1245 [("InstallPath", -1, prefix+r"\InstallPath", "", "[TARGETDIR]", "REGISTRY"),
1246 ("InstallGroup", -1, prefix+r"\InstallPath\InstallGroup", "",
1247 "Python %s" % short_version, "REGISTRY"),
1248 ("PythonPath", -1, prefix+r"\PythonPath", "",
1249 r"[TARGETDIR]Lib;[TARGETDIR]DLLs;[TARGETDIR]Lib\lib-tk", "REGISTRY"),
1250 ("Documentation", -1, prefix+r"\Help\Main Python Documentation", "",
1251 "[TARGETDIR]Doc\\"+docfile , "REGISTRY.doc"),
1252 ("Modules", -1, prefix+r"\Modules", "+", None, "REGISTRY"),
1253 ("AppPaths", -1, r"Software\Microsoft\Windows\CurrentVersion\App Paths\Python.exe",
1254 "", r"[TARGETDIR]Python.exe", "REGISTRY.def"),
1255 ("DisplayIcon", -1,
1256 r"Software\Microsoft\Windows\CurrentVersion\Uninstall\%s" % product_code,
1257 "DisplayIcon", "[TARGETDIR]python.exe", "REGISTRY")
1258 ])
1259 # Shortcuts, see "Shortcut Table"
1260 add_data(db, "Directory",
1261 [("ProgramMenuFolder", "TARGETDIR", "."),
1262 ("MenuDir", "ProgramMenuFolder", "PY%s%s|%sPython %s.%s" % (major,minor,testprefix,major,minor))])
1263 add_data(db, "RemoveFile",
1264 [("MenuDir", "TARGETDIR", None, "MenuDir", 2)])
1265 tcltkshortcuts = []
1266 if have_tcl:
1267 tcltkshortcuts = [
1268 ("IDLE", "MenuDir", "IDLE|IDLE (Python GUI)", "pythonw.exe",
1269 tcltk.id, r'"[TARGETDIR]Lib\idlelib\idle.pyw"', None, None, "python_icon.exe", 0, None, "TARGETDIR"),
1270 ("PyDoc", "MenuDir", "MODDOCS|Module Docs", "pythonw.exe",
1271 tcltk.id, r'"[TARGETDIR]Tools\scripts\pydocgui.pyw"', None, None, "python_icon.exe", 0, None, "TARGETDIR"),
1272 ]
1273 add_data(db, "Shortcut",
1274 tcltkshortcuts +
1275 [# Advertised shortcuts: targets are features, not files
1276 ("Python", "MenuDir", "PYTHON|Python (command line)", "python.exe",
1277 default_feature.id, None, None, None, "python_icon.exe", 2, None, "TARGETDIR"),
1278 # Advertising the Manual breaks on (some?) Win98, and the shortcut lacks an
1279 # icon first.
1280 #("Manual", "MenuDir", "MANUAL|Python Manuals", "documentation",
1281 # htmlfiles.id, None, None, None, None, None, None, None),
1282 ## Non-advertised shortcuts: must be associated with a registry component
1283 ("Manual", "MenuDir", "MANUAL|Python Manuals", "REGISTRY.doc",
1284 "[#%s]" % docfile, None,
1285 None, None, None, None, None, None),
1286 ("Uninstall", "MenuDir", "UNINST|Uninstall Python", "REGISTRY",
1287 SystemFolderName+"msiexec", "/x%s" % product_code,
1288 None, None, None, None, None, None),
1289 ])
1290 db.Commit()
1291
1292db = build_database()
1293try:
1294 add_features(db)
1295 add_ui(db)
1296 add_files(db)
1297 add_registry(db)
1298 remove_old_versions(db)
1299 db.Commit()
1300finally:
1301 del db
Note: See TracBrowser for help on using the repository browser.