summaryrefslogtreecommitdiffstats
path: root/Tools/msi/msi.py
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/msi/msi.py')
-rw-r--r--Tools/msi/msi.py137
1 files changed, 117 insertions, 20 deletions
diff --git a/Tools/msi/msi.py b/Tools/msi/msi.py
index b668c7a..53e652d 100644
--- a/Tools/msi/msi.py
+++ b/Tools/msi/msi.py
@@ -7,6 +7,7 @@ import uisample
from win32com.client import constants
from distutils.spawn import find_executable
from uuids import product_codes
+import tempfile
# Settings can be overridden in config.py below
# 0 for official python.org releases
@@ -28,6 +29,8 @@ have_tcl = True
PCBUILD="PCbuild"
# msvcrt version
MSVCR = "90"
+# Name of certificate in default store to sign MSI with
+certname = None
# Make a zip file containing the PDB files for this build?
pdbzip = True
@@ -115,6 +118,7 @@ pythondll_uuid = {
"27":"{4fe21c76-1760-437b-a2f2-99909130a175}",
"30":"{6953bc3b-6768-4291-8410-7914ce6e2ca8}",
"31":"{4afcba0b-13e4-47c3-bebe-477428b46913}",
+ "32":"{3ff95315-1096-4d31-bd86-601d5438ad5e}",
} [major+minor]
# Compute the name that Sphinx gives to the docfile
@@ -221,7 +225,8 @@ def build_database():
# schema represents the installer 2.0 database schema.
# sequence is the set of standard sequences
# (ui/execute, admin/advt/install)
- db = msilib.init_database("python-%s%s.msi" % (full_current_version, msilib.arch_ext),
+ msiname = "python-%s%s.msi" % (full_current_version, msilib.arch_ext)
+ db = msilib.init_database(msiname,
schema, ProductName="Python "+full_current_version+productsuffix,
ProductCode=product_code,
ProductVersion=current_version,
@@ -244,7 +249,7 @@ def build_database():
("ProductLine", "Python%s%s" % (major, minor)),
])
db.Commit()
- return db
+ return db, msiname
def remove_old_versions(db):
"Fill the upgrade table."
@@ -876,7 +881,6 @@ def generate_license():
shutil.copyfileobj(open(os.path.join(srcdir, "LICENSE")), out)
shutil.copyfileobj(open("crtlicense.txt"), out)
for name, pat, file in (("bzip2","bzip2-*", "LICENSE"),
- ("Berkeley DB", "db-*", "LICENSE"),
("openssl", "openssl-*", "LICENSE"),
("Tcl", "tcl8*", "license.terms"),
("Tk", "tk8*", "license.terms"),
@@ -900,6 +904,13 @@ class PyDirectory(Directory):
kw['componentflags'] = 2 #msidbComponentAttributesOptional
Directory.__init__(self, *args, **kw)
+ def check_unpackaged(self):
+ self.unpackaged_files.discard('__pycache__')
+ self.unpackaged_files.discard('.svn')
+ if self.unpackaged_files:
+ print "Warning: Unpackaged files in %s" % self.absolute
+ print self.unpackaged_files
+
# See "File Table", "Component Table", "Directory Table",
# "FeatureComponents Table"
def add_files(db):
@@ -963,13 +974,13 @@ def add_files(db):
extensions.remove("_ctypes.pyd")
# Add all .py files in Lib, except tkinter, test
- dirs={}
+ dirs = []
pydirs = [(root,"Lib")]
while pydirs:
# Commit every now and then, or else installer will complain
db.Commit()
parent, dir = pydirs.pop()
- if dir == ".svn" or dir.startswith("plat-"):
+ if dir == ".svn" or dir == '__pycache__' or dir.startswith("plat-"):
continue
elif dir in ["tkinter", "idlelib", "Icons"]:
if not have_tcl:
@@ -987,7 +998,7 @@ def add_files(db):
default_feature.set_current()
lib = PyDirectory(db, cab, parent, dir, dir, "%s|%s" % (parent.make_short(dir), dir))
# Add additional files
- dirs[dir]=lib
+ dirs.append(lib)
lib.glob("*.txt")
if dir=='site-packages':
lib.add_file("README.txt", src="README")
@@ -997,19 +1008,14 @@ def add_files(db):
if files:
# Add an entry to the RemoveFile table to remove bytecode files.
lib.remove_pyc()
- if dir.endswith('.egg-info'):
- lib.add_file('entry_points.txt')
- lib.add_file('PKG-INFO')
- lib.add_file('top_level.txt')
- lib.add_file('zip-safe')
- continue
+ # package READMEs if present
+ lib.glob("README")
+ if dir=='Lib':
+ lib.add_file('wsgiref.egg-info')
if dir=='test' and parent.physical=='Lib':
lib.add_file("185test.db")
lib.add_file("audiotest.au")
- lib.add_file("cfgparser.1")
lib.add_file("sgml_input.html")
- lib.add_file("test.xml")
- lib.add_file("test.xml.out")
lib.add_file("testtar.tar")
lib.add_file("test_difflib_expect.html")
lib.add_file("check_soundcard.vbs")
@@ -1018,26 +1024,41 @@ def add_files(db):
lib.glob("*.uue")
lib.glob("*.pem")
lib.glob("*.pck")
+ lib.glob("cfgparser.*")
+ lib.add_file("zip_cp437_header.zip")
lib.add_file("zipdir.zip")
+ if dir=='capath':
+ lib.glob("*.0")
if dir=='tests' and parent.physical=='distutils':
lib.add_file("Setup.sample")
if dir=='decimaltestdata':
lib.glob("*.decTest")
+ if dir=='xmltestdata':
+ lib.glob("*.xml")
+ lib.add_file("test.xml.out")
if dir=='output':
lib.glob("test_*")
+ if dir=='sndhdrdata':
+ lib.glob("sndhdr.*")
if dir=='idlelib':
lib.glob("*.def")
lib.add_file("idle.bat")
+ lib.add_file("ChangeLog")
if dir=="Icons":
lib.glob("*.gif")
lib.add_file("idle.icns")
if dir=="command" and parent.physical=="distutils":
lib.glob("wininst*.exe")
- if dir=="setuptools":
- lib.add_file("cli.exe")
- lib.add_file("gui.exe")
+ lib.add_file("command_template")
if dir=="lib2to3":
lib.removefile("pickle", "*.pickle")
+ if dir=="macholib":
+ lib.add_file("README.ctypes")
+ lib.glob("fetch_macholib*")
+ if dir=='turtledemo':
+ lib.add_file("turtle.cfg")
+ if dir=="pydoc_data":
+ lib.add_file("_pydoc.css")
if dir=="data" and parent.physical=="test" and parent.basedir.physical=="email":
# This should contain all non-.svn files listed in subversion
for f in os.listdir(lib.absolute):
@@ -1049,6 +1070,8 @@ def add_files(db):
for f in os.listdir(lib.absolute):
if os.path.isdir(os.path.join(lib.absolute, f)):
pydirs.append((lib, f))
+ for d in dirs:
+ d.check_unpackaged()
# Add DLLs
default_feature.set_current()
lib = DLLs
@@ -1064,6 +1087,7 @@ def add_files(db):
continue
dlls.append(f)
lib.add_file(f)
+ lib.add_file('python3.dll')
# Add sqlite
if msilib.msi_type=="Intel64;1033":
sqlite_arch = "/ia64"
@@ -1100,6 +1124,7 @@ def add_files(db):
for f in dlls:
lib.add_file(f.replace('pyd','lib'))
lib.add_file('python%s%s.lib' % (major, minor))
+ lib.add_file('python3.lib')
# Add the mingw-format library
if have_mingw:
lib.add_file('libpython%s%s.a' % (major, minor))
@@ -1120,7 +1145,7 @@ def add_files(db):
# Add tools
tools.set_current()
tooldir = PyDirectory(db, cab, root, "Tools", "Tools", "TOOLS|Tools")
- for f in ['i18n', 'pynche', 'Scripts', 'webchecker']:
+ for f in ['i18n', 'pynche', 'Scripts']:
lib = PyDirectory(db, cab, tooldir, f, f, "%s|%s" % (tooldir.make_short(f), f))
lib.glob("*.py")
lib.glob("*.pyw", exclude=['pydocgui.pyw'])
@@ -1307,7 +1332,7 @@ def build_pdbzip():
pdbzip.write(os.path.join(srcdir, PCBUILD, f), f)
pdbzip.close()
-db = build_database()
+db,msiname = build_database()
try:
add_features(db)
add_ui(db)
@@ -1318,5 +1343,77 @@ try:
finally:
del db
+# Merge CRT into MSI file. This requires the database to be closed.
+mod_dir = os.path.join(os.environ["ProgramFiles"], "Common Files", "Merge Modules")
+if msilib.Win64:
+ modules = ["Microsoft_VC90_CRT_x86_x64.msm", "policy_9_0_Microsoft_VC90_CRT_x86_x64.msm"]
+else:
+ modules = ["Microsoft_VC90_CRT_x86.msm","policy_9_0_Microsoft_VC90_CRT_x86.msm"]
+
+for i, n in enumerate(modules):
+ modules[i] = os.path.join(mod_dir, n)
+
+def merge(msi, feature, rootdir, modules):
+ cab_and_filecount = []
+ # Step 1: Merge databases, extract cabfiles
+ m = msilib.MakeMerge2()
+ m.OpenLog("merge.log")
+ m.OpenDatabase(msi)
+ for module in modules:
+ print module
+ m.OpenModule(module,0)
+ m.Merge(feature, rootdir)
+ print "Errors:"
+ for e in m.Errors:
+ print e.Type, e.ModuleTable, e.DatabaseTable
+ print " Modkeys:",
+ for s in e.ModuleKeys: print s,
+ print
+ print " DBKeys:",
+ for s in e.DatabaseKeys: print s,
+ print
+ cabname = tempfile.mktemp(suffix=".cab")
+ m.ExtractCAB(cabname)
+ cab_and_filecount.append((cabname, len(m.ModuleFiles)))
+ m.CloseModule()
+ m.CloseDatabase(True)
+ m.CloseLog()
+
+ # Step 2: Add CAB files
+ i = msilib.MakeInstaller()
+ db = i.OpenDatabase(msi, constants.msiOpenDatabaseModeTransact)
+
+ v = db.OpenView("SELECT LastSequence FROM Media")
+ v.Execute(None)
+ maxmedia = -1
+ while 1:
+ r = v.Fetch()
+ if not r: break
+ seq = r.IntegerData(1)
+ if seq > maxmedia:
+ maxmedia = seq
+ print "Start of Media", maxmedia
+
+ for cabname, count in cab_and_filecount:
+ stream = "merged%d" % maxmedia
+ msilib.add_data(db, "Media",
+ [(maxmedia+1, maxmedia+count, None, "#"+stream, None, None)])
+ msilib.add_stream(db, stream, cabname)
+ os.unlink(cabname)
+ maxmedia += count
+ # The merge module sets ALLUSERS to 1 in the property table.
+ # This is undesired; delete that
+ v = db.OpenView("DELETE FROM Property WHERE Property='ALLUSERS'")
+ v.Execute(None)
+ v.Close()
+ db.Commit()
+
+merge(msiname, "SharedCRT", "TARGETDIR", modules)
+
+# certname (from config.py) should be (a substring of)
+# the certificate subject, e.g. "Python Software Foundation"
+if certname:
+ os.system('signtool sign /n "%s" /t http://timestamp.verisign.com/scripts/timestamp.dll %s' % (certname, msiname))
+
if pdbzip:
build_pdbzip()