+++ /dev/null
-# -*- coding: iso-8859-1 -*-\r
-# Copyright (C) 2005, 2006 Martin von Löwis\r
-# Licensed to PSF under a Contributor Agreement.\r
-# The bdist_wininst command proper\r
-# based on bdist_wininst\r
-"""\r
-Implements the bdist_msi command.\r
-"""\r
-import sys, os\r
-from sysconfig import get_python_version\r
-\r
-from distutils.core import Command\r
-from distutils.dir_util import remove_tree\r
-from distutils.version import StrictVersion\r
-from distutils.errors import DistutilsOptionError\r
-from distutils import log\r
-from distutils.util import get_platform\r
-\r
-import msilib\r
-from msilib import schema, sequence, text\r
-from msilib import Directory, Feature, Dialog, add_data\r
-\r
-class PyDialog(Dialog):\r
- """Dialog class with a fixed layout: controls at the top, then a ruler,\r
- then a list of buttons: back, next, cancel. Optionally a bitmap at the\r
- left."""\r
- def __init__(self, *args, **kw):\r
- """Dialog(database, name, x, y, w, h, attributes, title, first,\r
- default, cancel, bitmap=true)"""\r
- Dialog.__init__(self, *args)\r
- ruler = self.h - 36\r
- #if kw.get("bitmap", True):\r
- # self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin")\r
- self.line("BottomLine", 0, ruler, self.w, 0)\r
-\r
- def title(self, title):\r
- "Set the title text of the dialog at the top."\r
- # name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix,\r
- # text, in VerdanaBold10\r
- self.text("Title", 15, 10, 320, 60, 0x30003,\r
- r"{\VerdanaBold10}%s" % title)\r
-\r
- def back(self, title, next, name = "Back", active = 1):\r
- """Add a back button with a given title, the tab-next button,\r
- its name in the Control table, possibly initially disabled.\r
-\r
- Return the button, so that events can be associated"""\r
- if active:\r
- flags = 3 # Visible|Enabled\r
- else:\r
- flags = 1 # Visible\r
- return self.pushbutton(name, 180, self.h-27 , 56, 17, flags, title, next)\r
-\r
- def cancel(self, title, next, name = "Cancel", active = 1):\r
- """Add a cancel button with a given title, the tab-next button,\r
- its name in the Control table, possibly initially disabled.\r
-\r
- Return the button, so that events can be associated"""\r
- if active:\r
- flags = 3 # Visible|Enabled\r
- else:\r
- flags = 1 # Visible\r
- return self.pushbutton(name, 304, self.h-27, 56, 17, flags, title, next)\r
-\r
- def next(self, title, next, name = "Next", active = 1):\r
- """Add a Next button with a given title, the tab-next button,\r
- its name in the Control table, possibly initially disabled.\r
-\r
- Return the button, so that events can be associated"""\r
- if active:\r
- flags = 3 # Visible|Enabled\r
- else:\r
- flags = 1 # Visible\r
- return self.pushbutton(name, 236, self.h-27, 56, 17, flags, title, next)\r
-\r
- def xbutton(self, name, title, next, xpos):\r
- """Add a button with a given title, the tab-next button,\r
- its name in the Control table, giving its x position; the\r
- y-position is aligned with the other buttons.\r
-\r
- Return the button, so that events can be associated"""\r
- return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next)\r
-\r
-class bdist_msi (Command):\r
-\r
- description = "create a Microsoft Installer (.msi) binary distribution"\r
-\r
- user_options = [('bdist-dir=', None,\r
- "temporary directory for creating the distribution"),\r
- ('plat-name=', 'p',\r
- "platform name to embed in generated filenames "\r
- "(default: %s)" % get_platform()),\r
- ('keep-temp', 'k',\r
- "keep the pseudo-installation tree around after " +\r
- "creating the distribution archive"),\r
- ('target-version=', None,\r
- "require a specific python version" +\r
- " on the target system"),\r
- ('no-target-compile', 'c',\r
- "do not compile .py to .pyc on the target system"),\r
- ('no-target-optimize', 'o',\r
- "do not compile .py to .pyo (optimized)"\r
- "on the target system"),\r
- ('dist-dir=', 'd',\r
- "directory to put final built distributions in"),\r
- ('skip-build', None,\r
- "skip rebuilding everything (for testing/debugging)"),\r
- ('install-script=', None,\r
- "basename of installation script to be run after"\r
- "installation or before deinstallation"),\r
- ('pre-install-script=', None,\r
- "Fully qualified filename of a script to be run before "\r
- "any files are installed. This script need not be in the "\r
- "distribution"),\r
- ]\r
-\r
- boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize',\r
- 'skip-build']\r
-\r
- all_versions = ['2.0', '2.1', '2.2', '2.3', '2.4',\r
- '2.5', '2.6', '2.7', '2.8', '2.9',\r
- '3.0', '3.1', '3.2', '3.3', '3.4',\r
- '3.5', '3.6', '3.7', '3.8', '3.9']\r
- other_version = 'X'\r
-\r
- def initialize_options (self):\r
- self.bdist_dir = None\r
- self.plat_name = None\r
- self.keep_temp = 0\r
- self.no_target_compile = 0\r
- self.no_target_optimize = 0\r
- self.target_version = None\r
- self.dist_dir = None\r
- self.skip_build = 0\r
- self.install_script = None\r
- self.pre_install_script = None\r
- self.versions = None\r
-\r
- def finalize_options (self):\r
- if self.bdist_dir is None:\r
- bdist_base = self.get_finalized_command('bdist').bdist_base\r
- self.bdist_dir = os.path.join(bdist_base, 'msi')\r
- short_version = get_python_version()\r
- if (not self.target_version) and self.distribution.has_ext_modules():\r
- self.target_version = short_version\r
- if self.target_version:\r
- self.versions = [self.target_version]\r
- if not self.skip_build and self.distribution.has_ext_modules()\\r
- and self.target_version != short_version:\r
- raise DistutilsOptionError, \\r
- "target version can only be %s, or the '--skip-build'" \\r
- " option must be specified" % (short_version,)\r
- else:\r
- self.versions = list(self.all_versions)\r
-\r
- self.set_undefined_options('bdist',\r
- ('dist_dir', 'dist_dir'),\r
- ('plat_name', 'plat_name'),\r
- )\r
-\r
- if self.pre_install_script:\r
- raise DistutilsOptionError, "the pre-install-script feature is not yet implemented"\r
-\r
- if self.install_script:\r
- for script in self.distribution.scripts:\r
- if self.install_script == os.path.basename(script):\r
- break\r
- else:\r
- raise DistutilsOptionError, \\r
- "install_script '%s' not found in scripts" % \\r
- self.install_script\r
- self.install_script_key = None\r
- # finalize_options()\r
-\r
-\r
- def run (self):\r
- if not self.skip_build:\r
- self.run_command('build')\r
-\r
- install = self.reinitialize_command('install', reinit_subcommands=1)\r
- install.prefix = self.bdist_dir\r
- install.skip_build = self.skip_build\r
- install.warn_dir = 0\r
-\r
- install_lib = self.reinitialize_command('install_lib')\r
- # we do not want to include pyc or pyo files\r
- install_lib.compile = 0\r
- install_lib.optimize = 0\r
-\r
- if self.distribution.has_ext_modules():\r
- # If we are building an installer for a Python version other\r
- # than the one we are currently running, then we need to ensure\r
- # our build_lib reflects the other Python version rather than ours.\r
- # Note that for target_version!=sys.version, we must have skipped the\r
- # build step, so there is no issue with enforcing the build of this\r
- # version.\r
- target_version = self.target_version\r
- if not target_version:\r
- assert self.skip_build, "Should have already checked this"\r
- target_version = sys.version[0:3]\r
- plat_specifier = ".%s-%s" % (self.plat_name, target_version)\r
- build = self.get_finalized_command('build')\r
- build.build_lib = os.path.join(build.build_base,\r
- 'lib' + plat_specifier)\r
-\r
- log.info("installing to %s", self.bdist_dir)\r
- install.ensure_finalized()\r
-\r
- # avoid warning of 'install_lib' about installing\r
- # into a directory not in sys.path\r
- sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB'))\r
-\r
- install.run()\r
-\r
- del sys.path[0]\r
-\r
- self.mkpath(self.dist_dir)\r
- fullname = self.distribution.get_fullname()\r
- installer_name = self.get_installer_filename(fullname)\r
- installer_name = os.path.abspath(installer_name)\r
- if os.path.exists(installer_name): os.unlink(installer_name)\r
-\r
- metadata = self.distribution.metadata\r
- author = metadata.author\r
- if not author:\r
- author = metadata.maintainer\r
- if not author:\r
- author = "UNKNOWN"\r
- version = metadata.get_version()\r
- # ProductVersion must be strictly numeric\r
- # XXX need to deal with prerelease versions\r
- sversion = "%d.%d.%d" % StrictVersion(version).version\r
- # Prefix ProductName with Python x.y, so that\r
- # it sorts together with the other Python packages\r
- # in Add-Remove-Programs (APR)\r
- fullname = self.distribution.get_fullname()\r
- if self.target_version:\r
- product_name = "Python %s %s" % (self.target_version, fullname)\r
- else:\r
- product_name = "Python %s" % (fullname)\r
- self.db = msilib.init_database(installer_name, schema,\r
- product_name, msilib.gen_uuid(),\r
- sversion, author)\r
- msilib.add_tables(self.db, sequence)\r
- props = [('DistVersion', version)]\r
- email = metadata.author_email or metadata.maintainer_email\r
- if email:\r
- props.append(("ARPCONTACT", email))\r
- if metadata.url:\r
- props.append(("ARPURLINFOABOUT", metadata.url))\r
- if props:\r
- add_data(self.db, 'Property', props)\r
-\r
- self.add_find_python()\r
- self.add_files()\r
- self.add_scripts()\r
- self.add_ui()\r
- self.db.Commit()\r
-\r
- if hasattr(self.distribution, 'dist_files'):\r
- tup = 'bdist_msi', self.target_version or 'any', fullname\r
- self.distribution.dist_files.append(tup)\r
-\r
- if not self.keep_temp:\r
- remove_tree(self.bdist_dir, dry_run=self.dry_run)\r
-\r
- def add_files(self):\r
- db = self.db\r
- cab = msilib.CAB("distfiles")\r
- rootdir = os.path.abspath(self.bdist_dir)\r
-\r
- root = Directory(db, cab, None, rootdir, "TARGETDIR", "SourceDir")\r
- f = Feature(db, "Python", "Python", "Everything",\r
- 0, 1, directory="TARGETDIR")\r
-\r
- items = [(f, root, '')]\r
- for version in self.versions + [self.other_version]:\r
- target = "TARGETDIR" + version\r
- name = default = "Python" + version\r
- desc = "Everything"\r
- if version is self.other_version:\r
- title = "Python from another location"\r
- level = 2\r
- else:\r
- title = "Python %s from registry" % version\r
- level = 1\r
- f = Feature(db, name, title, desc, 1, level, directory=target)\r
- dir = Directory(db, cab, root, rootdir, target, default)\r
- items.append((f, dir, version))\r
- db.Commit()\r
-\r
- seen = {}\r
- for feature, dir, version in items:\r
- todo = [dir]\r
- while todo:\r
- dir = todo.pop()\r
- for file in os.listdir(dir.absolute):\r
- afile = os.path.join(dir.absolute, file)\r
- if os.path.isdir(afile):\r
- short = "%s|%s" % (dir.make_short(file), file)\r
- default = file + version\r
- newdir = Directory(db, cab, dir, file, default, short)\r
- todo.append(newdir)\r
- else:\r
- if not dir.component:\r
- dir.start_component(dir.logical, feature, 0)\r
- if afile not in seen:\r
- key = seen[afile] = dir.add_file(file)\r
- if file==self.install_script:\r
- if self.install_script_key:\r
- raise DistutilsOptionError(\r
- "Multiple files with name %s" % file)\r
- self.install_script_key = '[#%s]' % key\r
- else:\r
- key = seen[afile]\r
- add_data(self.db, "DuplicateFile",\r
- [(key + version, dir.component, key, None, dir.logical)])\r
- db.Commit()\r
- cab.commit(db)\r
-\r
- def add_find_python(self):\r
- """Adds code to the installer to compute the location of Python.\r
-\r
- Properties PYTHON.MACHINE.X.Y and PYTHON.USER.X.Y will be set from the\r
- registry for each version of Python.\r
-\r
- Properties TARGETDIRX.Y will be set from PYTHON.USER.X.Y if defined,\r
- else from PYTHON.MACHINE.X.Y.\r
-\r
- Properties PYTHONX.Y will be set to TARGETDIRX.Y\\python.exe"""\r
-\r
- start = 402\r
- for ver in self.versions:\r
- install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % ver\r
- machine_reg = "python.machine." + ver\r
- user_reg = "python.user." + ver\r
- machine_prop = "PYTHON.MACHINE." + ver\r
- user_prop = "PYTHON.USER." + ver\r
- machine_action = "PythonFromMachine" + ver\r
- user_action = "PythonFromUser" + ver\r
- exe_action = "PythonExe" + ver\r
- target_dir_prop = "TARGETDIR" + ver\r
- exe_prop = "PYTHON" + ver\r
- if msilib.Win64:\r
- # type: msidbLocatorTypeRawValue + msidbLocatorType64bit\r
- Type = 2+16\r
- else:\r
- Type = 2\r
- add_data(self.db, "RegLocator",\r
- [(machine_reg, 2, install_path, None, Type),\r
- (user_reg, 1, install_path, None, Type)])\r
- add_data(self.db, "AppSearch",\r
- [(machine_prop, machine_reg),\r
- (user_prop, user_reg)])\r
- add_data(self.db, "CustomAction",\r
- [(machine_action, 51+256, target_dir_prop, "[" + machine_prop + "]"),\r
- (user_action, 51+256, target_dir_prop, "[" + user_prop + "]"),\r
- (exe_action, 51+256, exe_prop, "[" + target_dir_prop + "]\\python.exe"),\r
- ])\r
- add_data(self.db, "InstallExecuteSequence",\r
- [(machine_action, machine_prop, start),\r
- (user_action, user_prop, start + 1),\r
- (exe_action, None, start + 2),\r
- ])\r
- add_data(self.db, "InstallUISequence",\r
- [(machine_action, machine_prop, start),\r
- (user_action, user_prop, start + 1),\r
- (exe_action, None, start + 2),\r
- ])\r
- add_data(self.db, "Condition",\r
- [("Python" + ver, 0, "NOT TARGETDIR" + ver)])\r
- start += 4\r
- assert start < 500\r
-\r
- def add_scripts(self):\r
- if self.install_script:\r
- start = 6800\r
- for ver in self.versions + [self.other_version]:\r
- install_action = "install_script." + ver\r
- exe_prop = "PYTHON" + ver\r
- add_data(self.db, "CustomAction",\r
- [(install_action, 50, exe_prop, self.install_script_key)])\r
- add_data(self.db, "InstallExecuteSequence",\r
- [(install_action, "&Python%s=3" % ver, start)])\r
- start += 1\r
- # XXX pre-install scripts are currently refused in finalize_options()\r
- # but if this feature is completed, it will also need to add\r
- # entries for each version as the above code does\r
- if self.pre_install_script:\r
- scriptfn = os.path.join(self.bdist_dir, "preinstall.bat")\r
- f = open(scriptfn, "w")\r
- # The batch file will be executed with [PYTHON], so that %1\r
- # is the path to the Python interpreter; %0 will be the path\r
- # of the batch file.\r
- # rem ="""\r
- # %1 %0\r
- # exit\r
- # """\r
- # <actual script>\r
- f.write('rem ="""\n%1 %0\nexit\n"""\n')\r
- f.write(open(self.pre_install_script).read())\r
- f.close()\r
- add_data(self.db, "Binary",\r
- [("PreInstall", msilib.Binary(scriptfn))\r
- ])\r
- add_data(self.db, "CustomAction",\r
- [("PreInstall", 2, "PreInstall", None)\r
- ])\r
- add_data(self.db, "InstallExecuteSequence",\r
- [("PreInstall", "NOT Installed", 450)])\r
-\r
-\r
- def add_ui(self):\r
- db = self.db\r
- x = y = 50\r
- w = 370\r
- h = 300\r
- title = "[ProductName] Setup"\r
-\r
- # see "Dialog Style Bits"\r
- modal = 3 # visible | modal\r
- modeless = 1 # visible\r
-\r
- # UI customization properties\r
- add_data(db, "Property",\r
- # See "DefaultUIFont Property"\r
- [("DefaultUIFont", "DlgFont8"),\r
- # See "ErrorDialog Style Bit"\r
- ("ErrorDialog", "ErrorDlg"),\r
- ("Progress1", "Install"), # modified in maintenance type dlg\r
- ("Progress2", "installs"),\r
- ("MaintenanceForm_Action", "Repair"),\r
- # possible values: ALL, JUSTME\r
- ("WhichUsers", "ALL")\r
- ])\r
-\r
- # Fonts, see "TextStyle Table"\r
- add_data(db, "TextStyle",\r
- [("DlgFont8", "Tahoma", 9, None, 0),\r
- ("DlgFontBold8", "Tahoma", 8, None, 1), #bold\r
- ("VerdanaBold10", "Verdana", 10, None, 1),\r
- ("VerdanaRed9", "Verdana", 9, 255, 0),\r
- ])\r
-\r
- # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table"\r
- # Numbers indicate sequence; see sequence.py for how these action integrate\r
- add_data(db, "InstallUISequence",\r
- [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140),\r
- ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141),\r
- # In the user interface, assume all-users installation if privileged.\r
- ("SelectFeaturesDlg", "Not Installed", 1230),\r
- # XXX no support for resume installations yet\r
- #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240),\r
- ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250),\r
- ("ProgressDlg", None, 1280)])\r
-\r
- add_data(db, 'ActionText', text.ActionText)\r
- add_data(db, 'UIText', text.UIText)\r
- #####################################################################\r
- # Standard dialogs: FatalError, UserExit, ExitDialog\r
- fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title,\r
- "Finish", "Finish", "Finish")\r
- fatal.title("[ProductName] Installer ended prematurely")\r
- fatal.back("< Back", "Finish", active = 0)\r
- fatal.cancel("Cancel", "Back", active = 0)\r
- fatal.text("Description1", 15, 70, 320, 80, 0x30003,\r
- "[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.")\r
- fatal.text("Description2", 15, 155, 320, 20, 0x30003,\r
- "Click the Finish button to exit the Installer.")\r
- c=fatal.next("Finish", "Cancel", name="Finish")\r
- c.event("EndDialog", "Exit")\r
-\r
- user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title,\r
- "Finish", "Finish", "Finish")\r
- user_exit.title("[ProductName] Installer was interrupted")\r
- user_exit.back("< Back", "Finish", active = 0)\r
- user_exit.cancel("Cancel", "Back", active = 0)\r
- user_exit.text("Description1", 15, 70, 320, 80, 0x30003,\r
- "[ProductName] setup was interrupted. Your system has not been modified. "\r
- "To install this program at a later time, please run the installation again.")\r
- user_exit.text("Description2", 15, 155, 320, 20, 0x30003,\r
- "Click the Finish button to exit the Installer.")\r
- c = user_exit.next("Finish", "Cancel", name="Finish")\r
- c.event("EndDialog", "Exit")\r
-\r
- exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title,\r
- "Finish", "Finish", "Finish")\r
- exit_dialog.title("Completing the [ProductName] Installer")\r
- exit_dialog.back("< Back", "Finish", active = 0)\r
- exit_dialog.cancel("Cancel", "Back", active = 0)\r
- exit_dialog.text("Description", 15, 235, 320, 20, 0x30003,\r
- "Click the Finish button to exit the Installer.")\r
- c = exit_dialog.next("Finish", "Cancel", name="Finish")\r
- c.event("EndDialog", "Return")\r
-\r
- #####################################################################\r
- # Required dialog: FilesInUse, ErrorDlg\r
- inuse = PyDialog(db, "FilesInUse",\r
- x, y, w, h,\r
- 19, # KeepModeless|Modal|Visible\r
- title,\r
- "Retry", "Retry", "Retry", bitmap=False)\r
- inuse.text("Title", 15, 6, 200, 15, 0x30003,\r
- r"{\DlgFontBold8}Files in Use")\r
- inuse.text("Description", 20, 23, 280, 20, 0x30003,\r
- "Some files that need to be updated are currently in use.")\r
- inuse.text("Text", 20, 55, 330, 50, 3,\r
- "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.")\r
- inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess",\r
- None, None, None)\r
- c=inuse.back("Exit", "Ignore", name="Exit")\r
- c.event("EndDialog", "Exit")\r
- c=inuse.next("Ignore", "Retry", name="Ignore")\r
- c.event("EndDialog", "Ignore")\r
- c=inuse.cancel("Retry", "Exit", name="Retry")\r
- c.event("EndDialog","Retry")\r
-\r
- # See "Error Dialog". See "ICE20" for the required names of the controls.\r
- error = Dialog(db, "ErrorDlg",\r
- 50, 10, 330, 101,\r
- 65543, # Error|Minimize|Modal|Visible\r
- title,\r
- "ErrorText", None, None)\r
- error.text("ErrorText", 50,9,280,48,3, "")\r
- #error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None)\r
- error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo")\r
- error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes")\r
- error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort")\r
- error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel")\r
- error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore")\r
- error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk")\r
- error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry")\r
-\r
- #####################################################################\r
- # Global "Query Cancel" dialog\r
- cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title,\r
- "No", "No", "No")\r
- cancel.text("Text", 48, 15, 194, 30, 3,\r
- "Are you sure you want to cancel [ProductName] installation?")\r
- #cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,\r
- # "py.ico", None, None)\r
- c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No")\r
- c.event("EndDialog", "Exit")\r
-\r
- c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes")\r
- c.event("EndDialog", "Return")\r
-\r
- #####################################################################\r
- # Global "Wait for costing" dialog\r
- costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title,\r
- "Return", "Return", "Return")\r
- costing.text("Text", 48, 15, 194, 30, 3,\r
- "Please wait while the installer finishes determining your disk space requirements.")\r
- c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None)\r
- c.event("EndDialog", "Exit")\r
-\r
- #####################################################################\r
- # Preparation dialog: no user input except cancellation\r
- prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title,\r
- "Cancel", "Cancel", "Cancel")\r
- prep.text("Description", 15, 70, 320, 40, 0x30003,\r
- "Please wait while the Installer prepares to guide you through the installation.")\r
- prep.title("Welcome to the [ProductName] Installer")\r
- c=prep.text("ActionText", 15, 110, 320, 20, 0x30003, "Pondering...")\r
- c.mapping("ActionText", "Text")\r
- c=prep.text("ActionData", 15, 135, 320, 30, 0x30003, None)\r
- c.mapping("ActionData", "Text")\r
- prep.back("Back", None, active=0)\r
- prep.next("Next", None, active=0)\r
- c=prep.cancel("Cancel", None)\r
- c.event("SpawnDialog", "CancelDlg")\r
-\r
- #####################################################################\r
- # Feature (Python directory) selection\r
- seldlg = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal, title,\r
- "Next", "Next", "Cancel")\r
- seldlg.title("Select Python Installations")\r
-\r
- seldlg.text("Hint", 15, 30, 300, 20, 3,\r
- "Select the Python locations where %s should be installed."\r
- % self.distribution.get_fullname())\r
-\r
- seldlg.back("< Back", None, active=0)\r
- c = seldlg.next("Next >", "Cancel")\r
- order = 1\r
- c.event("[TARGETDIR]", "[SourceDir]", ordering=order)\r
- for version in self.versions + [self.other_version]:\r
- order += 1\r
- c.event("[TARGETDIR]", "[TARGETDIR%s]" % version,\r
- "FEATURE_SELECTED AND &Python%s=3" % version,\r
- ordering=order)\r
- c.event("SpawnWaitDialog", "WaitForCostingDlg", ordering=order + 1)\r
- c.event("EndDialog", "Return", ordering=order + 2)\r
- c = seldlg.cancel("Cancel", "Features")\r
- c.event("SpawnDialog", "CancelDlg")\r
-\r
- c = seldlg.control("Features", "SelectionTree", 15, 60, 300, 120, 3,\r
- "FEATURE", None, "PathEdit", None)\r
- c.event("[FEATURE_SELECTED]", "1")\r
- ver = self.other_version\r
- install_other_cond = "FEATURE_SELECTED AND &Python%s=3" % ver\r
- dont_install_other_cond = "FEATURE_SELECTED AND &Python%s<>3" % ver\r
-\r
- c = seldlg.text("Other", 15, 200, 300, 15, 3,\r
- "Provide an alternate Python location")\r
- c.condition("Enable", install_other_cond)\r
- c.condition("Show", install_other_cond)\r
- c.condition("Disable", dont_install_other_cond)\r
- c.condition("Hide", dont_install_other_cond)\r
-\r
- c = seldlg.control("PathEdit", "PathEdit", 15, 215, 300, 16, 1,\r
- "TARGETDIR" + ver, None, "Next", None)\r
- c.condition("Enable", install_other_cond)\r
- c.condition("Show", install_other_cond)\r
- c.condition("Disable", dont_install_other_cond)\r
- c.condition("Hide", dont_install_other_cond)\r
-\r
- #####################################################################\r
- # Disk cost\r
- cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title,\r
- "OK", "OK", "OK", bitmap=False)\r
- cost.text("Title", 15, 6, 200, 15, 0x30003,\r
- "{\DlgFontBold8}Disk Space Requirements")\r
- cost.text("Description", 20, 20, 280, 20, 0x30003,\r
- "The disk space required for the installation of the selected features.")\r
- cost.text("Text", 20, 53, 330, 60, 3,\r
- "The highlighted volumes (if any) do not have enough disk space "\r
- "available for the currently selected features. You can either "\r
- "remove some files from the highlighted volumes, or choose to "\r
- "install less features onto local drive(s), or select different "\r
- "destination drive(s).")\r
- cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223,\r
- None, "{120}{70}{70}{70}{70}", None, None)\r
- cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return")\r
-\r
- #####################################################################\r
- # WhichUsers Dialog. Only available on NT, and for privileged users.\r
- # This must be run before FindRelatedProducts, because that will\r
- # take into account whether the previous installation was per-user\r
- # or per-machine. We currently don't support going back to this\r
- # dialog after "Next" was selected; to support this, we would need to\r
- # find how to reset the ALLUSERS property, and how to re-run\r
- # FindRelatedProducts.\r
- # On Windows9x, the ALLUSERS property is ignored on the command line\r
- # and in the Property table, but installer fails according to the documentation\r
- # if a dialog attempts to set ALLUSERS.\r
- whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title,\r
- "AdminInstall", "Next", "Cancel")\r
- whichusers.title("Select whether to install [ProductName] for all users of this computer.")\r
- # A radio group with two options: allusers, justme\r
- g = whichusers.radiogroup("AdminInstall", 15, 60, 260, 50, 3,\r
- "WhichUsers", "", "Next")\r
- g.add("ALL", 0, 5, 150, 20, "Install for all users")\r
- g.add("JUSTME", 0, 25, 150, 20, "Install just for me")\r
-\r
- whichusers.back("Back", None, active=0)\r
-\r
- c = whichusers.next("Next >", "Cancel")\r
- c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1)\r
- c.event("EndDialog", "Return", ordering = 2)\r
-\r
- c = whichusers.cancel("Cancel", "AdminInstall")\r
- c.event("SpawnDialog", "CancelDlg")\r
-\r
- #####################################################################\r
- # Installation Progress dialog (modeless)\r
- progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title,\r
- "Cancel", "Cancel", "Cancel", bitmap=False)\r
- progress.text("Title", 20, 15, 200, 15, 0x30003,\r
- "{\DlgFontBold8}[Progress1] [ProductName]")\r
- progress.text("Text", 35, 65, 300, 30, 3,\r
- "Please wait while the Installer [Progress2] [ProductName]. "\r
- "This may take several minutes.")\r
- progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:")\r
-\r
- c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...")\r
- c.mapping("ActionText", "Text")\r
-\r
- #c=progress.text("ActionData", 35, 140, 300, 20, 3, None)\r
- #c.mapping("ActionData", "Text")\r
-\r
- c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537,\r
- None, "Progress done", None, None)\r
- c.mapping("SetProgress", "Progress")\r
-\r
- progress.back("< Back", "Next", active=False)\r
- progress.next("Next >", "Cancel", active=False)\r
- progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg")\r
-\r
- ###################################################################\r
- # Maintenance type: repair/uninstall\r
- maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title,\r
- "Next", "Next", "Cancel")\r
- maint.title("Welcome to the [ProductName] Setup Wizard")\r
- maint.text("BodyText", 15, 63, 330, 42, 3,\r
- "Select whether you want to repair or remove [ProductName].")\r
- g=maint.radiogroup("RepairRadioGroup", 15, 108, 330, 60, 3,\r
- "MaintenanceForm_Action", "", "Next")\r
- #g.add("Change", 0, 0, 200, 17, "&Change [ProductName]")\r
- g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]")\r
- g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]")\r
-\r
- maint.back("< Back", None, active=False)\r
- c=maint.next("Finish", "Cancel")\r
- # Change installation: Change progress dialog to "Change", then ask\r
- # for feature selection\r
- #c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1)\r
- #c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2)\r
-\r
- # Reinstall: Change progress dialog to "Repair", then invoke reinstall\r
- # Also set list of reinstalled features to "ALL"\r
- c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5)\r
- c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6)\r
- c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7)\r
- c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8)\r
-\r
- # Uninstall: Change progress to "Remove", then invoke uninstall\r
- # Also set list of removed features to "ALL"\r
- c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11)\r
- c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12)\r
- c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13)\r
- c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14)\r
-\r
- # Close dialog when maintenance action scheduled\r
- c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20)\r
- #c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21)\r
-\r
- maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg")\r
-\r
- def get_installer_filename(self, fullname):\r
- # Factored out to allow overriding in subclasses\r
- if self.target_version:\r
- base_name = "%s.%s-py%s.msi" % (fullname, self.plat_name,\r
- self.target_version)\r
- else:\r
- base_name = "%s.%s.msi" % (fullname, self.plat_name)\r
- installer_name = os.path.join(self.dist_dir, base_name)\r
- return installer_name\r