ยปCore Development>Code coverage>Tools/msi/msi.py

Python code coverage for Tools/msi/msi.py

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