summaryrefslogtreecommitdiff
path: root/Tools/msi
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/msi')
-rw-r--r--Tools/msi/merge.py84
-rw-r--r--Tools/msi/msi.py137
-rw-r--r--Tools/msi/msilib.py65
-rw-r--r--Tools/msi/uuids.py47
4 files changed, 169 insertions, 164 deletions
diff --git a/Tools/msi/merge.py b/Tools/msi/merge.py
deleted file mode 100644
index 85de209a09..0000000000
--- a/Tools/msi/merge.py
+++ /dev/null
@@ -1,84 +0,0 @@
-import msilib,os,win32com,tempfile,sys
-PCBUILD="PCBuild"
-certname = None
-from config import *
-
-Win64 = "amd64" in PCBUILD
-
-mod_dir = os.path.join(os.environ["ProgramFiles"], "Common Files", "Merge Modules")
-msi = None
-if len(sys.argv)==2:
- msi = sys.argv[1]
-if Win64:
- modules = ["Microsoft_VC90_CRT_x86_x64.msm", "policy_9_0_Microsoft_VC90_CRT_x86_x64.msm"]
- if not msi: msi = "python-%s.amd64.msi" % full_current_version
-else:
- modules = ["Microsoft_VC90_CRT_x86.msm","policy_9_0_Microsoft_VC90_CRT_x86.msm"]
- if not msi: msi = "python-%s.msi" % full_current_version
-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")
- print "Opened Log"
- m.OpenDatabase(msi)
- print "Opened DB"
- for module in modules:
- print module
- m.OpenModule(module,0)
- print "Opened Module",module
- 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, win32com.client.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(msi, "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, msi))
diff --git a/Tools/msi/msi.py b/Tools/msi/msi.py
index b668c7aaec..53e652d57f 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()
diff --git a/Tools/msi/msilib.py b/Tools/msi/msilib.py
index 6f49b4cf8c..5795d0ec6b 100644
--- a/Tools/msi/msilib.py
+++ b/Tools/msi/msilib.py
@@ -5,7 +5,7 @@ import win32com.client.gencache
import win32com.client
import pythoncom, pywintypes
from win32com.client import constants
-import re, string, os, sets, glob, subprocess, sys, _winreg, struct
+import re, string, os, sets, glob, subprocess, sys, _winreg, struct, _msi
try:
basestring
@@ -350,7 +350,7 @@ def gen_uuid():
class CAB:
def __init__(self, name):
self.name = name
- self.file = open(name+".txt", "wt")
+ self.files = []
self.filenames = sets.Set()
self.index = 0
@@ -369,51 +369,18 @@ class CAB:
if not logical:
logical = self.gen_id(dir, file)
self.index += 1
- if full.find(" ")!=-1:
- print >>self.file, '"%s" %s' % (full, logical)
- else:
- print >>self.file, '%s %s' % (full, logical)
+ self.files.append((full, logical))
return self.index, logical
def commit(self, db):
- self.file.close()
try:
os.unlink(self.name+".cab")
except OSError:
pass
- for k, v in [(r"Software\Microsoft\VisualStudio\7.1\Setup\VS", "VS7CommonBinDir"),
- (r"Software\Microsoft\VisualStudio\8.0\Setup\VS", "VS7CommonBinDir"),
- (r"Software\Microsoft\VisualStudio\9.0\Setup\VS", "VS7CommonBinDir"),
- (r"Software\Microsoft\Win32SDK\Directories", "Install Dir"),
- ]:
- try:
- key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, k)
- dir = _winreg.QueryValueEx(key, v)[0]
- _winreg.CloseKey(key)
- except (WindowsError, IndexError):
- continue
- cabarc = os.path.join(dir, r"Bin", "cabarc.exe")
- if not os.path.exists(cabarc):
- continue
- break
- else:
- print "WARNING: cabarc.exe not found in registry"
- cabarc = "cabarc.exe"
- cmd = r'"%s" -m lzx:21 n %s.cab @%s.txt' % (cabarc, self.name, self.name)
- p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- for line in p.stdout:
- if line.startswith(" -- adding "):
- sys.stdout.write(".")
- else:
- sys.stdout.write(line)
- sys.stdout.flush()
- if not os.path.exists(self.name+".cab"):
- raise IOError, "cabarc failed"
+ _msi.FCICreate(self.name+".cab", self.files)
add_data(db, "Media",
[(1, self.index, None, "#"+self.name, None, None)])
add_stream(db, self.name, self.name+".cab")
- os.unlink(self.name+".txt")
os.unlink(self.name+".cab")
db.Commit()
@@ -451,6 +418,12 @@ class Directory:
else:
self.absolute = physical
blogical = None
+ # initially assume that all files in this directory are unpackaged
+ # as files from self.absolute get added, this set is reduced
+ self.unpackaged_files = set()
+ for f in os.listdir(self.absolute):
+ if os.path.isfile(os.path.join(self.absolute, f)):
+ self.unpackaged_files.add(f)
add_data(db, "Directory", [(logical, blogical, default)])
def start_component(self, component = None, feature = None, flags = None, keyfile = None, uuid=None):
@@ -527,6 +500,11 @@ class Directory:
src = file
file = os.path.basename(file)
absolute = os.path.join(self.absolute, src)
+ if absolute.startswith(self.absolute):
+ # mark file as packaged
+ relative = absolute[len(self.absolute)+1:]
+ if relative in self.unpackaged_files:
+ self.unpackaged_files.remove(relative)
assert not re.search(r'[\?|><:/*]"', file) # restrictions on long names
if self.keyfiles.has_key(file):
logical = self.keyfiles[file]
@@ -572,10 +550,17 @@ class Directory:
return files
def remove_pyc(self):
- "Remove .pyc/.pyo files on uninstall"
+ "Remove .pyc/.pyo files from __pycache__ on uninstall"
+ directory = self.logical + "_pycache"
+ add_data(self.db, "Directory", [(directory, self.logical, "__PYCA~1|__pycache__")])
+ flags = 256 if Win64 else 0
+ add_data(self.db, "Component",
+ [(directory, gen_uuid(), directory, flags, None, None)])
+ add_data(self.db, "FeatureComponents", [(current_feature.id, directory)])
+ add_data(self.db, "CreateFolder", [(directory, directory)])
add_data(self.db, "RemoveFile",
- [(self.component+"c", self.component, "*.pyc", self.logical, 2),
- (self.component+"o", self.component, "*.pyo", self.logical, 2)])
+ [(self.component, self.component, "*.*", directory, 2),
+ ])
def removefile(self, key, pattern):
"Add a RemoveFile entry"
diff --git a/Tools/msi/uuids.py b/Tools/msi/uuids.py
index a5e5cd21f0..09da40accf 100644
--- a/Tools/msi/uuids.py
+++ b/Tools/msi/uuids.py
@@ -7,23 +7,6 @@
# generated. For official releases, we record the product codes,
# so people can refer to them.
product_codes = {
- '2.4.101': '{0e9b4d8e-6cda-446e-a208-7b92f3ddffa0}', # 2.4a1, released as a snapshot
- '2.4.102': '{1b998745-4901-4edb-bc52-213689e1b922}', # 2.4a2
- '2.4.103': '{33fc8bd2-1e8f-4add-a40a-ade2728d5942}', # 2.4a3
- '2.4.111': '{51a7e2a8-2025-4ef0-86ff-e6aab742d1fa}', # 2.4b1
- '2.4.112': '{4a5e7c1d-c659-4fe3-b8c9-7c65bd9c95a5}', # 2.4b2
- '2.4.121': '{75508821-a8e9-40a8-95bd-dbe6033ddbea}', # 2.4c1
- '2.4.122': '{83a9118b-4bdd-473b-afc3-bcb142feca9e}', # 2.4c2
- '2.4.150': '{82d9302e-f209-4805-b548-52087047483a}', # 2.4.0
- '2.4.1121':'{be027411-8e6b-4440-a29b-b07df0690230}', # 2.4.1c1
- '2.4.1122':'{02818752-48bf-4074-a281-7a4114c4f1b1}', # 2.4.1c2
- '2.4.1150':'{4d4f5346-7e4a-40b5-9387-fdb6181357fc}', # 2.4.1
- '2.4.2121':'{5ef9d6b6-df78-45d2-ab09-14786a3c5a99}', # 2.4.2c1
- '2.4.2150':'{b191e49c-ea23-43b2-b28a-14e0784069b8}', # 2.4.2
- '2.4.3121':'{f669ed4d-1dce-41c4-9617-d985397187a1}', # 2.4.3c1
- '2.4.3150':'{75e71add-042c-4f30-bfac-a9ec42351313}', # 2.4.3
- '2.4.4121':'{cd2862db-22a4-4688-8772-85407ea21550}', # 2.4.4c1
- '2.4.4150':'{60e2c8c9-6cf3-4b1a-9618-e304946c94e6}', # 2.4.4
'2.5.101': '{bc14ce3e-5e72-4a64-ac1f-bf59a571898c}', # 2.5a1
'2.5.102': '{5eed51c1-8e9d-4071-94c5-b40de5d49ba5}', # 2.5a2
'2.5.103': '{73dcd966-ffec-415f-bb39-8342c1f47017}', # 2.5a3
@@ -50,6 +33,23 @@ product_codes = {
'2.6.1150':'{9cc89170-000b-457d-91f1-53691f85b223}', # 2.6.1
'2.6.2121':'{adac412b-b209-4c15-b6ab-dca1b6e47144}', # 2.6.2c1
'2.6.2150':'{24aab420-4e30-4496-9739-3e216f3de6ae}', # 2.6.2
+ '2.6.3121':'{a73e0254-dcda-4fe4-bf37-c7e1c4f4ebb6}', # 2.6.3c1
+ '2.6.3150':'{3d9ac095-e115-4e94-bdef-7f7edf17697d}', # 2.6.3
+ '2.6.4121':'{727de605-0359-4606-a94b-c2033652379b}', # 2.6.4c1
+ '2.6.4122':'{4f7603c6-6352-4299-a398-150a31b19acc}', # 2.6.4c2
+ '2.6.4150':'{e7394a0f-3f80-45b1-87fc-abcd51893246}', # 2.6.4
+ '2.6.5121':'{e0e273d7-7598-4701-8325-c90c069fd5ff}', # 2.6.5c1
+ '2.6.5122':'{fa227b76-0671-4dc6-b826-c2ff2a70dfd5}', # 2.6.5c2
+ '2.6.5150':'{4723f199-fa64-4233-8e6e-9fccc95a18ee}', # 2.6.5
+ '2.7.101': '{eca1bbef-432c-49ae-a667-c213cc7bbf22}', # 2.7a1
+ '2.7.102': '{21ce16ed-73c4-460d-9b11-522f417b2090}', # 2.7a2
+ '2.7.103': '{6e7dbd55-ba4a-48ac-a688-6c75db4d7500}', # 2.7a3
+ '2.7.104': '{ee774ba3-74a5-48d9-b425-b35a287260c8}', # 2.7a4
+ '2.7.111': '{9cfd9ec7-a9c7-4980-a1c6-054fc6493eb3}', # 2.7b1
+ '2.7.112': '{9a72faf6-c304-4165-8595-9291ff30cac6}', # 2.7b2
+ '2.7.121': '{f530c94a-dd53-4de9-948e-b632b9cb48d2}', # 2.7c1
+ '2.7.122': '{f80905d2-dd8d-4b8e-8a40-c23c93dca07d}', # 2.7c2
+ '2.7.150': '{20c31435-2a0a-4580-be8b-ac06fc243ca4}', # 2.7.0
'3.0.101': '{8554263a-3242-4857-9359-aa87bc2c58c2}', # 3.0a1
'3.0.102': '{692d6e2c-f0ac-40b8-a133-7191aeeb67f9}', # 3.0a2
'3.0.103': '{49cb2995-751a-4753-be7a-d0b1bb585e06}', # 3.0a3
@@ -77,7 +77,14 @@ product_codes = {
'3.1.1150':'{7ff90460-89b7-435b-b583-b37b2815ccc7}', # 3.1.1
'3.1.2121':'{ec45624a-378c-43be-91f3-3f7a59b0d90c}', # 3.1.2c1
'3.1.2150':'{d40af016-506c-43fb-a738-bd54fa8c1e85}', # 3.1.2
- '3.1.3121':'{a1e436f8-92fc-4ddb-af18-a12529c57aaf}', # 3.1.3rc1
- '3.1.3122':'{2fc19026-a3e5-493e-92a0-c1f3b4a272ae}', # 3.1.3rc2
- '3.1.3150':'{f719d8a6-46fc-4d71-94c6-ffd17a8c9f35}', # 3.1.3
+ '3.2.101' :'{b411f168-7a36-4fff-902c-a554d1c78a4f}', # 3.2a1
+ '3.2.102' :'{79ff73b7-8359-410f-b9c5-152d2026f8c8}', # 3.2a2
+ '3.2.103' :'{e7635c65-c221-4b9b-b70a-5611b8369d77}', # 3.2a3
+ '3.2.104' :'{748cd139-75b8-4ca8-98a7-58262298181e}', # 3.2a4
+ '3.2.111' :'{20bfc16f-c7cd-4fc0-8f96-9914614a3c50}', # 3.2b1
+ '3.2.112' :'{0e350c98-8d73-4993-b686-cfe87160046e}', # 3.2b2
+ '3.2.121' :'{2094968d-7583-47f6-a7fd-22304532e09f}', # 3.2rc1
+ '3.2.122' :'{4f3edfa6-cf70-469a-825f-e1206aa7f412}', # 3.2rc2
+ '3.2.123' :'{90c673d7-8cfd-4969-9816-f7d70bad87f3}', # 3.2rc3
+ '3.2.150' :'{b2042d5e-986d-44ec-aee3-afe4108ccc93}', # 3.2.0
}