| 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
|
|---|
| 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 | # Where is sqlite3.dll located, relative to srcdir?
|
|---|
| 28 | sqlite_dir = "../sqlite-source-3.3.4"
|
|---|
| 29 |
|
|---|
| 30 | try:
|
|---|
| 31 | from config import *
|
|---|
| 32 | except ImportError:
|
|---|
| 33 | pass
|
|---|
| 34 |
|
|---|
| 35 | # Extract current version from Include/patchlevel.h
|
|---|
| 36 | lines = open(srcdir + "/Include/patchlevel.h").readlines()
|
|---|
| 37 | major = minor = micro = level = serial = None
|
|---|
| 38 | levels = {
|
|---|
| 39 | 'PY_RELEASE_LEVEL_ALPHA':0xA,
|
|---|
| 40 | 'PY_RELEASE_LEVEL_BETA': 0xB,
|
|---|
| 41 | 'PY_RELEASE_LEVEL_GAMMA':0xC,
|
|---|
| 42 | 'PY_RELEASE_LEVEL_FINAL':0xF
|
|---|
| 43 | }
|
|---|
| 44 | for l in lines:
|
|---|
| 45 | if not l.startswith("#define"):
|
|---|
| 46 | continue
|
|---|
| 47 | l = l.split()
|
|---|
| 48 | if len(l) != 3:
|
|---|
| 49 | continue
|
|---|
| 50 | _, name, value = l
|
|---|
| 51 | if name == 'PY_MAJOR_VERSION': major = value
|
|---|
| 52 | if name == 'PY_MINOR_VERSION': minor = value
|
|---|
| 53 | if name == 'PY_MICRO_VERSION': micro = value
|
|---|
| 54 | if name == 'PY_RELEASE_LEVEL': level = levels[value]
|
|---|
| 55 | if name == 'PY_RELEASE_SERIAL': serial = value
|
|---|
| 56 |
|
|---|
| 57 | short_version = major+"."+minor
|
|---|
| 58 | # See PC/make_versioninfo.c
|
|---|
| 59 | FIELD3 = 1000*int(micro) + 10*level + int(serial)
|
|---|
| 60 | current_version = "%s.%d" % (short_version, FIELD3)
|
|---|
| 61 |
|
|---|
| 62 | # This should never change. The UpgradeCode of this package can be
|
|---|
| 63 | # used in the Upgrade table of future packages to make the future
|
|---|
| 64 | # package replace this one. See "UpgradeCode Property".
|
|---|
| 65 | upgrade_code_snapshot='{92A24481-3ECB-40FC-8836-04B7966EC0D5}'
|
|---|
| 66 | upgrade_code='{65E6DE48-A358-434D-AA4F-4AF72DB4718F}'
|
|---|
| 67 |
|
|---|
| 68 | if snapshot:
|
|---|
| 69 | current_version = "%s.%s.%s" % (major, minor, int(time.time()/3600/24))
|
|---|
| 70 | product_code = msilib.gen_uuid()
|
|---|
| 71 | else:
|
|---|
| 72 | product_code = product_codes[current_version]
|
|---|
| 73 |
|
|---|
| 74 | if full_current_version is None:
|
|---|
| 75 | full_current_version = current_version
|
|---|
| 76 |
|
|---|
| 77 | extensions = [
|
|---|
| 78 | 'bz2.pyd',
|
|---|
| 79 | 'pyexpat.pyd',
|
|---|
| 80 | 'select.pyd',
|
|---|
| 81 | 'unicodedata.pyd',
|
|---|
| 82 | 'winsound.pyd',
|
|---|
| 83 | '_elementtree.pyd',
|
|---|
| 84 | '_bsddb.pyd',
|
|---|
| 85 | '_socket.pyd',
|
|---|
| 86 | '_ssl.pyd',
|
|---|
| 87 | '_testcapi.pyd',
|
|---|
| 88 | '_tkinter.pyd',
|
|---|
| 89 | '_msi.pyd',
|
|---|
| 90 | '_ctypes.pyd',
|
|---|
| 91 | '_ctypes_test.pyd',
|
|---|
| 92 | '_sqlite3.pyd',
|
|---|
| 93 | '_hashlib.pyd'
|
|---|
| 94 | ]
|
|---|
| 95 |
|
|---|
| 96 | # Well-known component UUIDs
|
|---|
| 97 | # These are needed for SharedDLLs reference counter; if
|
|---|
| 98 | # a different UUID was used for each incarnation of, say,
|
|---|
| 99 | # python24.dll, an upgrade would set the reference counter
|
|---|
| 100 | # from 1 to 2 (due to what I consider a bug in MSI)
|
|---|
| 101 | # Using the same UUID is fine since these files are versioned,
|
|---|
| 102 | # so Installer will always keep the newest version.
|
|---|
| 103 | msvcr71_uuid = "{8666C8DD-D0B4-4B42-928E-A69E32FA5D4D}"
|
|---|
| 104 | pythondll_uuid = {
|
|---|
| 105 | "24":"{9B81E618-2301-4035-AC77-75D9ABEB7301}",
|
|---|
| 106 | "25":"{2e41b118-38bd-4c1b-a840-6977efd1b911}"
|
|---|
| 107 | } [major+minor]
|
|---|
| 108 |
|
|---|
| 109 | # Build the mingw import library, libpythonXY.a
|
|---|
| 110 | # This requires 'nm' and 'dlltool' executables on your PATH
|
|---|
| 111 | def build_mingw_lib(lib_file, def_file, dll_file, mingw_lib):
|
|---|
| 112 | warning = "WARNING: %s - libpythonXX.a not built"
|
|---|
| 113 | nm = find_executable('nm')
|
|---|
| 114 | dlltool = find_executable('dlltool')
|
|---|
| 115 |
|
|---|
| 116 | if not nm or not dlltool:
|
|---|
| 117 | print warning % "nm and/or dlltool were not found"
|
|---|
| 118 | return False
|
|---|
| 119 |
|
|---|
| 120 | nm_command = '%s -Cs %s' % (nm, lib_file)
|
|---|
| 121 | dlltool_command = "%s --dllname %s --def %s --output-lib %s" % \
|
|---|
| 122 | (dlltool, dll_file, def_file, mingw_lib)
|
|---|
| 123 | export_match = re.compile(r"^_imp__(.*) in python\d+\.dll").match
|
|---|
| 124 |
|
|---|
| 125 | f = open(def_file,'w')
|
|---|
| 126 | print >>f, "LIBRARY %s" % dll_file
|
|---|
| 127 | print >>f, "EXPORTS"
|
|---|
| 128 |
|
|---|
| 129 | nm_pipe = os.popen(nm_command)
|
|---|
| 130 | for line in nm_pipe.readlines():
|
|---|
| 131 | m = export_match(line)
|
|---|
| 132 | if m:
|
|---|
| 133 | print >>f, m.group(1)
|
|---|
| 134 | f.close()
|
|---|
| 135 | exit = nm_pipe.close()
|
|---|
| 136 |
|
|---|
| 137 | if exit:
|
|---|
| 138 | print warning % "nm did not run successfully"
|
|---|
| 139 | return False
|
|---|
| 140 |
|
|---|
| 141 | if os.system(dlltool_command) != 0:
|
|---|
| 142 | print warning % "dlltool did not run successfully"
|
|---|
| 143 | return False
|
|---|
| 144 |
|
|---|
| 145 | return True
|
|---|
| 146 |
|
|---|
| 147 | # Target files (.def and .a) go in PCBuild directory
|
|---|
| 148 | lib_file = os.path.join(srcdir, "PCBuild", "python%s%s.lib" % (major, minor))
|
|---|
| 149 | def_file = os.path.join(srcdir, "PCBuild", "python%s%s.def" % (major, minor))
|
|---|
| 150 | dll_file = "python%s%s.dll" % (major, minor)
|
|---|
| 151 | mingw_lib = os.path.join(srcdir, "PCBuild", "libpython%s%s.a" % (major, minor))
|
|---|
| 152 |
|
|---|
| 153 | have_mingw = build_mingw_lib(lib_file, def_file, dll_file, mingw_lib)
|
|---|
| 154 |
|
|---|
| 155 | # Determine the target architechture
|
|---|
| 156 | dll_path = os.path.join(srcdir, "PCBuild", dll_file)
|
|---|
| 157 | msilib.set_arch_from_file(dll_path)
|
|---|
| 158 | if msilib.pe_type(dll_path) != msilib.pe_type("msisupport.dll"):
|
|---|
| 159 | raise SystemError, "msisupport.dll for incorrect architecture"
|
|---|
| 160 |
|
|---|
| 161 | if testpackage:
|
|---|
| 162 | ext = 'px'
|
|---|
| 163 | testprefix = 'x'
|
|---|
| 164 | else:
|
|---|
| 165 | ext = 'py'
|
|---|
| 166 | testprefix = ''
|
|---|
| 167 |
|
|---|
| 168 | if msilib.Win64:
|
|---|
| 169 | SystemFolderName = "[SystemFolder64]"
|
|---|
| 170 | else:
|
|---|
| 171 | SystemFolderName = "[SystemFolder]"
|
|---|
| 172 |
|
|---|
| 173 | msilib.reset()
|
|---|
| 174 |
|
|---|
| 175 | # condition in which to install pythonxy.dll in system32:
|
|---|
| 176 | # a) it is Windows 9x or
|
|---|
| 177 | # b) it is NT, the user is privileged, and has chosen per-machine installation
|
|---|
| 178 | sys32cond = "(Windows9x or (Privileged and ALLUSERS))"
|
|---|
| 179 |
|
|---|
| 180 | def build_database():
|
|---|
| 181 | """Generate an empty database, with just the schema and the
|
|---|
| 182 | Summary information stream."""
|
|---|
| 183 | if snapshot:
|
|---|
| 184 | uc = upgrade_code_snapshot
|
|---|
| 185 | else:
|
|---|
| 186 | uc = upgrade_code
|
|---|
| 187 | # schema represents the installer 2.0 database schema.
|
|---|
| 188 | # sequence is the set of standard sequences
|
|---|
| 189 | # (ui/execute, admin/advt/install)
|
|---|
| 190 | db = msilib.init_database("python-%s%s.msi" % (full_current_version, msilib.arch_ext),
|
|---|
| 191 | schema, ProductName="Python "+full_current_version,
|
|---|
| 192 | ProductCode=product_code,
|
|---|
| 193 | ProductVersion=current_version,
|
|---|
| 194 | Manufacturer=u"Martin v. L\xf6wis")
|
|---|
| 195 | # The default sequencing of the RemoveExistingProducts action causes
|
|---|
| 196 | # removal of files that got just installed. Place it after
|
|---|
| 197 | # InstallInitialize, so we first uninstall everything, but still roll
|
|---|
| 198 | # back in case the installation is interrupted
|
|---|
| 199 | msilib.change_sequence(sequence.InstallExecuteSequence,
|
|---|
| 200 | "RemoveExistingProducts", 1510)
|
|---|
| 201 | msilib.add_tables(db, sequence)
|
|---|
| 202 | # We cannot set ALLUSERS in the property table, as this cannot be
|
|---|
| 203 | # reset if the user choses a per-user installation. Instead, we
|
|---|
| 204 | # maintain WhichUsers, which can be "ALL" or "JUSTME". The UI manages
|
|---|
| 205 | # this property, and when the execution starts, ALLUSERS is set
|
|---|
| 206 | # accordingly.
|
|---|
| 207 | add_data(db, "Property", [("UpgradeCode", uc),
|
|---|
| 208 | ("WhichUsers", "ALL"),
|
|---|
| 209 | ("ProductLine", "Python%s%s" % (major, minor)),
|
|---|
| 210 | ])
|
|---|
| 211 | db.Commit()
|
|---|
| 212 | return db
|
|---|
| 213 |
|
|---|
| 214 | def remove_old_versions(db):
|
|---|
| 215 | "Fill the upgrade table."
|
|---|
| 216 | start = "%s.%s.0" % (major, minor)
|
|---|
| 217 | # This requests that feature selection states of an older
|
|---|
| 218 | # installation should be forwarded into this one. Upgrading
|
|---|
| 219 | # requires that both the old and the new installation are
|
|---|
| 220 | # either both per-machine or per-user.
|
|---|
| 221 | migrate_features = 1
|
|---|
| 222 | # See "Upgrade Table". We remove releases with the same major and
|
|---|
| 223 | # minor version. For an snapshot, we remove all earlier snapshots. For
|
|---|
| 224 | # a release, we remove all snapshots, and all earlier releases.
|
|---|
| 225 | if snapshot:
|
|---|
| 226 | add_data(db, "Upgrade",
|
|---|
| 227 | [(upgrade_code_snapshot, start,
|
|---|
| 228 | current_version,
|
|---|
| 229 | None, # Ignore language
|
|---|
| 230 | migrate_features,
|
|---|
| 231 | None, # Migrate ALL features
|
|---|
| 232 | "REMOVEOLDSNAPSHOT")])
|
|---|
| 233 | props = "REMOVEOLDSNAPSHOT"
|
|---|
| 234 | else:
|
|---|
| 235 | add_data(db, "Upgrade",
|
|---|
| 236 | [(upgrade_code, start, current_version,
|
|---|
| 237 | None, migrate_features, None, "REMOVEOLDVERSION"),
|
|---|
| 238 | (upgrade_code_snapshot, start, "%s.%d.0" % (major, int(minor)+1),
|
|---|
| 239 | None, migrate_features, None, "REMOVEOLDSNAPSHOT")])
|
|---|
| 240 | props = "REMOVEOLDSNAPSHOT;REMOVEOLDVERSION"
|
|---|
| 241 | # Installer collects the product codes of the earlier releases in
|
|---|
| 242 | # these properties. In order to allow modification of the properties,
|
|---|
| 243 | # they must be declared as secure. See "SecureCustomProperties Property"
|
|---|
| 244 | add_data(db, "Property", [("SecureCustomProperties", props)])
|
|---|
| 245 |
|
|---|
| 246 | class PyDialog(Dialog):
|
|---|
| 247 | """Dialog class with a fixed layout: controls at the top, then a ruler,
|
|---|
| 248 | then a list of buttons: back, next, cancel. Optionally a bitmap at the
|
|---|
| 249 | left."""
|
|---|
| 250 | def __init__(self, *args, **kw):
|
|---|
| 251 | """Dialog(database, name, x, y, w, h, attributes, title, first,
|
|---|
| 252 | default, cancel, bitmap=true)"""
|
|---|
| 253 | Dialog.__init__(self, *args)
|
|---|
| 254 | ruler = self.h - 36
|
|---|
| 255 | bmwidth = 152*ruler/328
|
|---|
| 256 | if kw.get("bitmap", True):
|
|---|
| 257 | self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin")
|
|---|
| 258 | self.line("BottomLine", 0, ruler, self.w, 0)
|
|---|
| 259 |
|
|---|
| 260 | def title(self, title):
|
|---|
| 261 | "Set the title text of the dialog at the top."
|
|---|
| 262 | # name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix,
|
|---|
| 263 | # text, in VerdanaBold10
|
|---|
| 264 | self.text("Title", 135, 10, 220, 60, 0x30003,
|
|---|
| 265 | r"{\VerdanaBold10}%s" % title)
|
|---|
| 266 |
|
|---|
| 267 | def back(self, title, next, name = "Back", active = 1):
|
|---|
| 268 | """Add a back button with a given title, the tab-next button,
|
|---|
| 269 | its name in the Control table, possibly initially disabled.
|
|---|
| 270 |
|
|---|
| 271 | Return the button, so that events can be associated"""
|
|---|
| 272 | if active:
|
|---|
| 273 | flags = 3 # Visible|Enabled
|
|---|
| 274 | else:
|
|---|
| 275 | flags = 1 # Visible
|
|---|
| 276 | return self.pushbutton(name, 180, self.h-27 , 56, 17, flags, title, next)
|
|---|
| 277 |
|
|---|
| 278 | def cancel(self, title, next, name = "Cancel", active = 1):
|
|---|
| 279 | """Add a cancel button with a given title, the tab-next button,
|
|---|
| 280 | its name in the Control table, possibly initially disabled.
|
|---|
| 281 |
|
|---|
| 282 | Return the button, so that events can be associated"""
|
|---|
| 283 | if active:
|
|---|
| 284 | flags = 3 # Visible|Enabled
|
|---|
| 285 | else:
|
|---|
| 286 | flags = 1 # Visible
|
|---|
| 287 | return self.pushbutton(name, 304, self.h-27, 56, 17, flags, title, next)
|
|---|
| 288 |
|
|---|
| 289 | def next(self, title, next, name = "Next", active = 1):
|
|---|
| 290 | """Add a Next button with a given title, the tab-next button,
|
|---|
| 291 | its name in the Control table, possibly initially disabled.
|
|---|
| 292 |
|
|---|
| 293 | Return the button, so that events can be associated"""
|
|---|
| 294 | if active:
|
|---|
| 295 | flags = 3 # Visible|Enabled
|
|---|
| 296 | else:
|
|---|
| 297 | flags = 1 # Visible
|
|---|
| 298 | return self.pushbutton(name, 236, self.h-27, 56, 17, flags, title, next)
|
|---|
| 299 |
|
|---|
| 300 | def xbutton(self, name, title, next, xpos):
|
|---|
| 301 | """Add a button with a given title, the tab-next button,
|
|---|
| 302 | its name in the Control table, giving its x position; the
|
|---|
| 303 | y-position is aligned with the other buttons.
|
|---|
| 304 |
|
|---|
| 305 | Return the button, so that events can be associated"""
|
|---|
| 306 | return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next)
|
|---|
| 307 |
|
|---|
| 308 | def add_ui(db):
|
|---|
| 309 | x = y = 50
|
|---|
| 310 | w = 370
|
|---|
| 311 | h = 300
|
|---|
| 312 | title = "[ProductName] Setup"
|
|---|
| 313 |
|
|---|
| 314 | # see "Dialog Style Bits"
|
|---|
| 315 | modal = 3 # visible | modal
|
|---|
| 316 | modeless = 1 # visible
|
|---|
| 317 | track_disk_space = 32
|
|---|
| 318 |
|
|---|
| 319 | add_data(db, 'ActionText', uisample.ActionText)
|
|---|
| 320 | add_data(db, 'UIText', uisample.UIText)
|
|---|
| 321 |
|
|---|
| 322 | # Bitmaps
|
|---|
| 323 | if not os.path.exists(srcdir+r"\PC\python_icon.exe"):
|
|---|
| 324 | raise "Run icons.mak in PC directory"
|
|---|
| 325 | add_data(db, "Binary",
|
|---|
| 326 | [("PythonWin", msilib.Binary(srcdir+r"\PCbuild\installer.bmp")), # 152x328 pixels
|
|---|
| 327 | ("py.ico",msilib.Binary(srcdir+r"\PC\py.ico")),
|
|---|
| 328 | ])
|
|---|
| 329 | add_data(db, "Icon",
|
|---|
| 330 | [("python_icon.exe", msilib.Binary(srcdir+r"\PC\python_icon.exe"))])
|
|---|
| 331 |
|
|---|
| 332 | # Scripts
|
|---|
| 333 | # CheckDir sets TargetExists if TARGETDIR exists.
|
|---|
| 334 | # UpdateEditIDLE sets the REGISTRY.tcl component into
|
|---|
| 335 | # the installed/uninstalled state according to both the
|
|---|
| 336 | # Extensions and TclTk features.
|
|---|
| 337 | if os.system("nmake /nologo /c /f msisupport.mak") != 0:
|
|---|
| 338 | raise "'nmake /f msisupport.mak' failed"
|
|---|
| 339 | add_data(db, "Binary", [("Script", msilib.Binary("msisupport.dll"))])
|
|---|
| 340 | # See "Custom Action Type 1"
|
|---|
| 341 | if msilib.Win64:
|
|---|
| 342 | CheckDir = "CheckDir"
|
|---|
| 343 | UpdateEditIDLE = "UpdateEditIDLE"
|
|---|
| 344 | else:
|
|---|
| 345 | CheckDir = "_CheckDir@4"
|
|---|
| 346 | UpdateEditIDLE = "_UpdateEditIDLE@4"
|
|---|
| 347 | add_data(db, "CustomAction",
|
|---|
| 348 | [("CheckDir", 1, "Script", CheckDir)])
|
|---|
| 349 | if have_tcl:
|
|---|
| 350 | add_data(db, "CustomAction",
|
|---|
| 351 | [("UpdateEditIDLE", 1, "Script", UpdateEditIDLE)])
|
|---|
| 352 |
|
|---|
| 353 | # UI customization properties
|
|---|
| 354 | add_data(db, "Property",
|
|---|
| 355 | # See "DefaultUIFont Property"
|
|---|
| 356 | [("DefaultUIFont", "DlgFont8"),
|
|---|
| 357 | # See "ErrorDialog Style Bit"
|
|---|
| 358 | ("ErrorDialog", "ErrorDlg"),
|
|---|
| 359 | ("Progress1", "Install"), # modified in maintenance type dlg
|
|---|
| 360 | ("Progress2", "installs"),
|
|---|
| 361 | ("MaintenanceForm_Action", "Repair")])
|
|---|
| 362 |
|
|---|
| 363 | # Fonts, see "TextStyle Table"
|
|---|
| 364 | add_data(db, "TextStyle",
|
|---|
| 365 | [("DlgFont8", "Tahoma", 9, None, 0),
|
|---|
| 366 | ("DlgFontBold8", "Tahoma", 8, None, 1), #bold
|
|---|
| 367 | ("VerdanaBold10", "Verdana", 10, None, 1),
|
|---|
| 368 | ("VerdanaRed9", "Verdana", 9, 255, 0),
|
|---|
| 369 | ])
|
|---|
| 370 |
|
|---|
| 371 | compileargs = r'-Wi "[TARGETDIR]Lib\compileall.py" -f -x bad_coding|badsyntax|site-packages "[TARGETDIR]Lib"'
|
|---|
| 372 | # See "CustomAction Table"
|
|---|
| 373 | add_data(db, "CustomAction", [
|
|---|
| 374 | # msidbCustomActionTypeFirstSequence + msidbCustomActionTypeTextData + msidbCustomActionTypeProperty
|
|---|
| 375 | # See "Custom Action Type 51",
|
|---|
| 376 | # "Custom Action Execution Scheduling Options"
|
|---|
| 377 | ("InitialTargetDir", 307, "TARGETDIR",
|
|---|
| 378 | "[WindowsVolume]Python%s%s" % (major, minor)),
|
|---|
| 379 | ("SetDLLDirToTarget", 307, "DLLDIR", "[TARGETDIR]"),
|
|---|
| 380 | ("SetDLLDirToSystem32", 307, "DLLDIR", SystemFolderName),
|
|---|
| 381 | # msidbCustomActionTypeExe + msidbCustomActionTypeSourceFile
|
|---|
| 382 | # See "Custom Action Type 18"
|
|---|
| 383 | ("CompilePyc", 18, "python.exe", compileargs),
|
|---|
| 384 | ("CompilePyo", 18, "python.exe", "-O "+compileargs),
|
|---|
| 385 | ])
|
|---|
| 386 |
|
|---|
| 387 | # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table"
|
|---|
| 388 | # Numbers indicate sequence; see sequence.py for how these action integrate
|
|---|
| 389 | add_data(db, "InstallUISequence",
|
|---|
| 390 | [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140),
|
|---|
| 391 | ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141),
|
|---|
| 392 | ("InitialTargetDir", 'TARGETDIR=""', 750),
|
|---|
| 393 | # In the user interface, assume all-users installation if privileged.
|
|---|
| 394 | ("SetDLLDirToSystem32", 'DLLDIR="" and ' + sys32cond, 751),
|
|---|
| 395 | ("SetDLLDirToTarget", 'DLLDIR="" and not ' + sys32cond, 752),
|
|---|
| 396 | ("SelectDirectoryDlg", "Not Installed", 1230),
|
|---|
| 397 | # XXX no support for resume installations yet
|
|---|
| 398 | #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240),
|
|---|
| 399 | ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250),
|
|---|
| 400 | ("ProgressDlg", None, 1280)])
|
|---|
| 401 | add_data(db, "AdminUISequence",
|
|---|
| 402 | [("InitialTargetDir", 'TARGETDIR=""', 750),
|
|---|
| 403 | ("SetDLLDirToTarget", 'DLLDIR=""', 751),
|
|---|
| 404 | ])
|
|---|
| 405 |
|
|---|
| 406 | # Execute Sequences
|
|---|
| 407 | add_data(db, "InstallExecuteSequence",
|
|---|
| 408 | [("InitialTargetDir", 'TARGETDIR=""', 750),
|
|---|
| 409 | ("SetDLLDirToSystem32", 'DLLDIR="" and ' + sys32cond, 751),
|
|---|
| 410 | ("SetDLLDirToTarget", 'DLLDIR="" and not ' + sys32cond, 752),
|
|---|
| 411 | ("UpdateEditIDLE", None, 1050),
|
|---|
| 412 | ("CompilePyc", "COMPILEALL", 6800),
|
|---|
| 413 | ("CompilePyo", "COMPILEALL", 6801),
|
|---|
| 414 | ])
|
|---|
| 415 | add_data(db, "AdminExecuteSequence",
|
|---|
| 416 | [("InitialTargetDir", 'TARGETDIR=""', 750),
|
|---|
| 417 | ("SetDLLDirToTarget", 'DLLDIR=""', 751),
|
|---|
| 418 | ("CompilePyc", "COMPILEALL", 6800),
|
|---|
| 419 | ("CompilePyo", "COMPILEALL", 6801),
|
|---|
| 420 | ])
|
|---|
| 421 |
|
|---|
| 422 | #####################################################################
|
|---|
| 423 | # Standard dialogs: FatalError, UserExit, ExitDialog
|
|---|
| 424 | fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title,
|
|---|
| 425 | "Finish", "Finish", "Finish")
|
|---|
| 426 | fatal.title("[ProductName] Installer ended prematurely")
|
|---|
| 427 | fatal.back("< Back", "Finish", active = 0)
|
|---|
| 428 | fatal.cancel("Cancel", "Back", active = 0)
|
|---|
| 429 | fatal.text("Description1", 135, 70, 220, 80, 0x30003,
|
|---|
| 430 | "[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.")
|
|---|
| 431 | fatal.text("Description2", 135, 155, 220, 20, 0x30003,
|
|---|
| 432 | "Click the Finish button to exit the Installer.")
|
|---|
| 433 | c=fatal.next("Finish", "Cancel", name="Finish")
|
|---|
| 434 | # See "ControlEvent Table". Parameters are the event, the parameter
|
|---|
| 435 | # to the action, and optionally the condition for the event, and the order
|
|---|
| 436 | # of events.
|
|---|
| 437 | c.event("EndDialog", "Exit")
|
|---|
| 438 |
|
|---|
| 439 | user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title,
|
|---|
| 440 | "Finish", "Finish", "Finish")
|
|---|
| 441 | user_exit.title("[ProductName] Installer was interrupted")
|
|---|
| 442 | user_exit.back("< Back", "Finish", active = 0)
|
|---|
| 443 | user_exit.cancel("Cancel", "Back", active = 0)
|
|---|
| 444 | user_exit.text("Description1", 135, 70, 220, 80, 0x30003,
|
|---|
| 445 | "[ProductName] setup was interrupted. Your system has not been modified. "
|
|---|
| 446 | "To install this program at a later time, please run the installation again.")
|
|---|
| 447 | user_exit.text("Description2", 135, 155, 220, 20, 0x30003,
|
|---|
| 448 | "Click the Finish button to exit the Installer.")
|
|---|
| 449 | c = user_exit.next("Finish", "Cancel", name="Finish")
|
|---|
| 450 | c.event("EndDialog", "Exit")
|
|---|
| 451 |
|
|---|
| 452 | exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title,
|
|---|
| 453 | "Finish", "Finish", "Finish")
|
|---|
| 454 | exit_dialog.title("Completing the [ProductName] Installer")
|
|---|
| 455 | exit_dialog.back("< Back", "Finish", active = 0)
|
|---|
| 456 | exit_dialog.cancel("Cancel", "Back", active = 0)
|
|---|
| 457 | exit_dialog.text("Acknowledgements", 135, 95, 220, 120, 0x30003,
|
|---|
| 458 | "Special Windows thanks to:\n"
|
|---|
| 459 | " Mark Hammond, without whose years of freely \n"
|
|---|
| 460 | " shared Windows expertise, Python for Windows \n"
|
|---|
| 461 | " would still be Python for DOS.")
|
|---|
| 462 |
|
|---|
| 463 | c = exit_dialog.text("warning", 135, 200, 220, 40, 0x30003,
|
|---|
| 464 | "{\\VerdanaRed9}Warning: Python 2.5.x is the last "
|
|---|
| 465 | "Python release for Windows 9x.")
|
|---|
| 466 | c.condition("Hide", "NOT Version9X")
|
|---|
| 467 |
|
|---|
| 468 | exit_dialog.text("Description", 135, 235, 220, 20, 0x30003,
|
|---|
| 469 | "Click the Finish button to exit the Installer.")
|
|---|
| 470 | c = exit_dialog.next("Finish", "Cancel", name="Finish")
|
|---|
| 471 | c.event("EndDialog", "Return")
|
|---|
| 472 |
|
|---|
| 473 | #####################################################################
|
|---|
| 474 | # Required dialog: FilesInUse, ErrorDlg
|
|---|
| 475 | inuse = PyDialog(db, "FilesInUse",
|
|---|
| 476 | x, y, w, h,
|
|---|
| 477 | 19, # KeepModeless|Modal|Visible
|
|---|
| 478 | title,
|
|---|
| 479 | "Retry", "Retry", "Retry", bitmap=False)
|
|---|
| 480 | inuse.text("Title", 15, 6, 200, 15, 0x30003,
|
|---|
| 481 | r"{\DlgFontBold8}Files in Use")
|
|---|
| 482 | inuse.text("Description", 20, 23, 280, 20, 0x30003,
|
|---|
| 483 | "Some files that need to be updated are currently in use.")
|
|---|
| 484 | inuse.text("Text", 20, 55, 330, 50, 3,
|
|---|
| 485 | "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.")
|
|---|
| 486 | inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess",
|
|---|
| 487 | None, None, None)
|
|---|
| 488 | c=inuse.back("Exit", "Ignore", name="Exit")
|
|---|
| 489 | c.event("EndDialog", "Exit")
|
|---|
| 490 | c=inuse.next("Ignore", "Retry", name="Ignore")
|
|---|
| 491 | c.event("EndDialog", "Ignore")
|
|---|
| 492 | c=inuse.cancel("Retry", "Exit", name="Retry")
|
|---|
| 493 | c.event("EndDialog","Retry")
|
|---|
| 494 |
|
|---|
| 495 |
|
|---|
| 496 | # See "Error Dialog". See "ICE20" for the required names of the controls.
|
|---|
| 497 | error = Dialog(db, "ErrorDlg",
|
|---|
| 498 | 50, 10, 330, 101,
|
|---|
| 499 | 65543, # Error|Minimize|Modal|Visible
|
|---|
| 500 | title,
|
|---|
| 501 | "ErrorText", None, None)
|
|---|
| 502 | error.text("ErrorText", 50,9,280,48,3, "")
|
|---|
| 503 | error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None)
|
|---|
| 504 | error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo")
|
|---|
| 505 | error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes")
|
|---|
| 506 | error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort")
|
|---|
| 507 | error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel")
|
|---|
| 508 | error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore")
|
|---|
| 509 | error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk")
|
|---|
| 510 | error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry")
|
|---|
| 511 |
|
|---|
| 512 | #####################################################################
|
|---|
| 513 | # Global "Query Cancel" dialog
|
|---|
| 514 | cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title,
|
|---|
| 515 | "No", "No", "No")
|
|---|
| 516 | cancel.text("Text", 48, 15, 194, 30, 3,
|
|---|
| 517 | "Are you sure you want to cancel [ProductName] installation?")
|
|---|
| 518 | cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
|
|---|
| 519 | "py.ico", None, None)
|
|---|
| 520 | c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No")
|
|---|
| 521 | c.event("EndDialog", "Exit")
|
|---|
| 522 |
|
|---|
| 523 | c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes")
|
|---|
| 524 | c.event("EndDialog", "Return")
|
|---|
| 525 |
|
|---|
| 526 | #####################################################################
|
|---|
| 527 | # Global "Wait for costing" dialog
|
|---|
| 528 | costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title,
|
|---|
| 529 | "Return", "Return", "Return")
|
|---|
|
|---|