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