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