144b345460a9c6e38154df8bfc2afe1d26a35849bMartin v. Löwis# Copyright (C) 2005, 2006 Martin von Löwis
2fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis# Licensed to PSF under a Contributor Agreement.
3fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis# The bdist_wininst command proper
4fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis# based on bdist_wininst
5fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis"""
6fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. LöwisImplements the bdist_msi command.
7fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis"""
8fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
93679727939a9d25ccfe057e71e8a4b8be73d47ceTarek Ziadéimport sys, os
10fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwisfrom distutils.core import Command
11fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwisfrom distutils.dir_util import remove_tree
123679727939a9d25ccfe057e71e8a4b8be73d47ceTarek Ziadéfrom distutils.sysconfig import get_python_version
13fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwisfrom distutils.version import StrictVersion
14fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwisfrom distutils.errors import DistutilsOptionError
153679727939a9d25ccfe057e71e8a4b8be73d47ceTarek Ziadéfrom distutils.util import get_platform
16fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwisfrom distutils import log
17fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwisimport msilib
18477c8d5e70240744d24631b18341ad892c8a8e1cThomas Woutersfrom msilib import schema, sequence, text
19fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwisfrom msilib import Directory, Feature, Dialog, add_data
20fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
21fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwisclass PyDialog(Dialog):
22fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis    """Dialog class with a fixed layout: controls at the top, then a ruler,
23fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis    then a list of buttons: back, next, cancel. Optionally a bitmap at the
24fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis    left."""
25fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis    def __init__(self, *args, **kw):
26fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        """Dialog(database, name, x, y, w, h, attributes, title, first,
27fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        default, cancel, bitmap=true)"""
28fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        Dialog.__init__(self, *args)
29fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        ruler = self.h - 36
303679727939a9d25ccfe057e71e8a4b8be73d47ceTarek Ziadé        bmwidth = 152*ruler/328
31fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        #if kw.get("bitmap", True):
32fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        #    self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin")
33fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        self.line("BottomLine", 0, ruler, self.w, 0)
34fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
35fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis    def title(self, title):
36fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        "Set the title text of the dialog at the top."
37fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix,
38fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # text, in VerdanaBold10
39fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        self.text("Title", 15, 10, 320, 60, 0x30003,
40fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                  r"{\VerdanaBold10}%s" % title)
41fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
42fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis    def back(self, title, next, name = "Back", active = 1):
43fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        """Add a back button with a given title, the tab-next button,
44fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        its name in the Control table, possibly initially disabled.
45fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
46fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        Return the button, so that events can be associated"""
47fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        if active:
48fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            flags = 3 # Visible|Enabled
49fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        else:
50fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            flags = 1 # Visible
51fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        return self.pushbutton(name, 180, self.h-27 , 56, 17, flags, title, next)
52fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
53fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis    def cancel(self, title, next, name = "Cancel", active = 1):
54fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        """Add a cancel button with a given title, the tab-next button,
55fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        its name in the Control table, possibly initially disabled.
56fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
57fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        Return the button, so that events can be associated"""
58fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        if active:
59fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            flags = 3 # Visible|Enabled
60fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        else:
61fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            flags = 1 # Visible
62fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        return self.pushbutton(name, 304, self.h-27, 56, 17, flags, title, next)
63fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
64fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis    def next(self, title, next, name = "Next", active = 1):
65fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        """Add a Next button with a given title, the tab-next button,
66fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        its name in the Control table, possibly initially disabled.
67fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
68fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        Return the button, so that events can be associated"""
69fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        if active:
70fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            flags = 3 # Visible|Enabled
71fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        else:
72fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            flags = 1 # Visible
73fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        return self.pushbutton(name, 236, self.h-27, 56, 17, flags, title, next)
74fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
75fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis    def xbutton(self, name, title, next, xpos):
76fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        """Add a button with a given title, the tab-next button,
77fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        its name in the Control table, giving its x position; the
78fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        y-position is aligned with the other buttons.
79fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
80fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        Return the button, so that events can be associated"""
81fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next)
82fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
835b7e9d76f39dbf63573519c178835f72e5a5027aCollin Winterclass bdist_msi(Command):
84fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
85fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis    description = "create a Microsoft Installer (.msi) binary distribution"
86fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
87fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis    user_options = [('bdist-dir=', None,
88fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                     "temporary directory for creating the distribution"),
895e69685999c0f44af3536ac71a2a59e70b7ea185Christian Heimes                    ('plat-name=', 'p',
905e69685999c0f44af3536ac71a2a59e70b7ea185Christian Heimes                     "platform name to embed in generated filenames "
915e69685999c0f44af3536ac71a2a59e70b7ea185Christian Heimes                     "(default: %s)" % get_platform()),
92fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                    ('keep-temp', 'k',
93fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                     "keep the pseudo-installation tree around after " +
94fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                     "creating the distribution archive"),
95fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                    ('target-version=', None,
96fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                     "require a specific python version" +
97fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                     " on the target system"),
98fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                    ('no-target-compile', 'c',
99fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                     "do not compile .py to .pyc on the target system"),
100fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                    ('no-target-optimize', 'o',
101fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                     "do not compile .py to .pyo (optimized)"
102fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                     "on the target system"),
103fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                    ('dist-dir=', 'd',
104fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                     "directory to put final built distributions in"),
105fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                    ('skip-build', None,
106fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                     "skip rebuilding everything (for testing/debugging)"),
107fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                    ('install-script=', None,
108fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                     "basename of installation script to be run after"
109fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                     "installation or before deinstallation"),
110fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                    ('pre-install-script=', None,
111fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                     "Fully qualified filename of a script to be run before "
112fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                     "any files are installed.  This script need not be in the "
113fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                     "distribution"),
114fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                   ]
115fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
116fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis    boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize',
117fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                       'skip-build']
118fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
11993f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard    all_versions = ['2.0', '2.1', '2.2', '2.3', '2.4',
12093f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                    '2.5', '2.6', '2.7', '2.8', '2.9',
12193f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                    '3.0', '3.1', '3.2', '3.3', '3.4',
12293f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                    '3.5', '3.6', '3.7', '3.8', '3.9']
12393f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard    other_version = 'X'
12493f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard
1255b7e9d76f39dbf63573519c178835f72e5a5027aCollin Winter    def initialize_options(self):
126fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        self.bdist_dir = None
1275e69685999c0f44af3536ac71a2a59e70b7ea185Christian Heimes        self.plat_name = None
128fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        self.keep_temp = 0
129fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        self.no_target_compile = 0
130fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        self.no_target_optimize = 0
131fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        self.target_version = None
132fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        self.dist_dir = None
133fbe37dfffe501973b1e998bca948748097857179Éric Araujo        self.skip_build = None
134fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        self.install_script = None
135fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        self.pre_install_script = None
13693f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        self.versions = None
137fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
1385b7e9d76f39dbf63573519c178835f72e5a5027aCollin Winter    def finalize_options(self):
139fbe37dfffe501973b1e998bca948748097857179Éric Araujo        self.set_undefined_options('bdist', ('skip_build', 'skip_build'))
140fbe37dfffe501973b1e998bca948748097857179Éric Araujo
141fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        if self.bdist_dir is None:
142fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            bdist_base = self.get_finalized_command('bdist').bdist_base
143fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            self.bdist_dir = os.path.join(bdist_base, 'msi')
144fbe37dfffe501973b1e998bca948748097857179Éric Araujo
145fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        short_version = get_python_version()
146403117aa606438427aa9f9dc72271d40bc0ba0b2Martin v. Löwis        if (not self.target_version) and self.distribution.has_ext_modules():
147403117aa606438427aa9f9dc72271d40bc0ba0b2Martin v. Löwis            self.target_version = short_version
148fbe37dfffe501973b1e998bca948748097857179Éric Araujo
149fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        if self.target_version:
15093f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            self.versions = [self.target_version]
151fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            if not self.skip_build and self.distribution.has_ext_modules()\
152fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis               and self.target_version != short_version:
1535b7e9d76f39dbf63573519c178835f72e5a5027aCollin Winter                raise DistutilsOptionError(
15456be37cef2c102190b88c015ca9820821f35792aGeorg Brandl                      "target version can only be %s, or the '--skip-build'"
1555b7e9d76f39dbf63573519c178835f72e5a5027aCollin Winter                      " option must be specified" % (short_version,))
156fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        else:
15793f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            self.versions = list(self.all_versions)
158fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
1595e69685999c0f44af3536ac71a2a59e70b7ea185Christian Heimes        self.set_undefined_options('bdist',
1605e69685999c0f44af3536ac71a2a59e70b7ea185Christian Heimes                                   ('dist_dir', 'dist_dir'),
1615e69685999c0f44af3536ac71a2a59e70b7ea185Christian Heimes                                   ('plat_name', 'plat_name'),
1625e69685999c0f44af3536ac71a2a59e70b7ea185Christian Heimes                                   )
163fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
164fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        if self.pre_install_script:
1655b7e9d76f39dbf63573519c178835f72e5a5027aCollin Winter            raise DistutilsOptionError(
1665b7e9d76f39dbf63573519c178835f72e5a5027aCollin Winter                  "the pre-install-script feature is not yet implemented")
167fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
168fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        if self.install_script:
169fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            for script in self.distribution.scripts:
170fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                if self.install_script == os.path.basename(script):
171fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                    break
172fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            else:
1735b7e9d76f39dbf63573519c178835f72e5a5027aCollin Winter                raise DistutilsOptionError(
1745b7e9d76f39dbf63573519c178835f72e5a5027aCollin Winter                      "install_script '%s' not found in scripts"
1755b7e9d76f39dbf63573519c178835f72e5a5027aCollin Winter                      % self.install_script)
176fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        self.install_script_key = None
177fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
1785b7e9d76f39dbf63573519c178835f72e5a5027aCollin Winter    def run(self):
179fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        if not self.skip_build:
180fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            self.run_command('build')
181fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
182fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        install = self.reinitialize_command('install', reinit_subcommands=1)
183fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        install.prefix = self.bdist_dir
184fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        install.skip_build = self.skip_build
185fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        install.warn_dir = 0
186fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
187fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        install_lib = self.reinitialize_command('install_lib')
188fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # we do not want to include pyc or pyo files
189fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        install_lib.compile = 0
190fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        install_lib.optimize = 0
191fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
192fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        if self.distribution.has_ext_modules():
193fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            # If we are building an installer for a Python version other
194fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            # than the one we are currently running, then we need to ensure
195fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            # our build_lib reflects the other Python version rather than ours.
196fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            # Note that for target_version!=sys.version, we must have skipped the
197fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            # build step, so there is no issue with enforcing the build of this
198fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            # version.
199fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            target_version = self.target_version
200fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            if not target_version:
201fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                assert self.skip_build, "Should have already checked this"
202885bdc4946890f4bb80557fab80c3874b2cc4d39Serhiy Storchaka                target_version = '%d.%d' % sys.version_info[:2]
2035e69685999c0f44af3536ac71a2a59e70b7ea185Christian Heimes            plat_specifier = ".%s-%s" % (self.plat_name, target_version)
204fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            build = self.get_finalized_command('build')
205fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            build.build_lib = os.path.join(build.build_base,
206fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                                           'lib' + plat_specifier)
207fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
208fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        log.info("installing to %s", self.bdist_dir)
209fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        install.ensure_finalized()
210fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
211fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # avoid warning of 'install_lib' about installing
212fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # into a directory not in sys.path
213fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB'))
214fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
215fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        install.run()
216fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
217fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        del sys.path[0]
218fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
219fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        self.mkpath(self.dist_dir)
220fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        fullname = self.distribution.get_fullname()
221fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        installer_name = self.get_installer_filename(fullname)
222fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        installer_name = os.path.abspath(installer_name)
223fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        if os.path.exists(installer_name): os.unlink(installer_name)
224fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
225fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        metadata = self.distribution.metadata
226fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        author = metadata.author
227fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        if not author:
228fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            author = metadata.maintainer
229fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        if not author:
230fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            author = "UNKNOWN"
231fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        version = metadata.get_version()
232fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # ProductVersion must be strictly numeric
233fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # XXX need to deal with prerelease versions
234fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        sversion = "%d.%d.%d" % StrictVersion(version).version
235fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # Prefix ProductName with Python x.y, so that
236fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # it sorts together with the other Python packages
237fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # in Add-Remove-Programs (APR)
23893f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        fullname = self.distribution.get_fullname()
23993f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        if self.target_version:
24093f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            product_name = "Python %s %s" % (self.target_version, fullname)
24193f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        else:
24293f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            product_name = "Python %s" % (fullname)
243fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        self.db = msilib.init_database(installer_name, schema,
244e8d09e581816aa7fdb812801c79e8f6bc55dc134Tim Peters                product_name, msilib.gen_uuid(),
245fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                sversion, author)
246fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        msilib.add_tables(self.db, sequence)
247fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        props = [('DistVersion', version)]
248fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        email = metadata.author_email or metadata.maintainer_email
249fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        if email:
250fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            props.append(("ARPCONTACT", email))
251fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        if metadata.url:
252fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            props.append(("ARPURLINFOABOUT", metadata.url))
253fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        if props:
254fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            add_data(self.db, 'Property', props)
255fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
256fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        self.add_find_python()
257fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        self.add_files()
258fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        self.add_scripts()
259fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        self.add_ui()
260fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        self.db.Commit()
261fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
262fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        if hasattr(self.distribution, 'dist_files'):
263a420c820fe243cbc0d729e9c60f8a93810e973a7Éric Araujo            tup = 'bdist_msi', self.target_version or 'any', fullname
26493f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            self.distribution.dist_files.append(tup)
265fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
266fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        if not self.keep_temp:
267fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            remove_tree(self.bdist_dir, dry_run=self.dry_run)
268fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
269fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis    def add_files(self):
270fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        db = self.db
271fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        cab = msilib.CAB("distfiles")
272fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        rootdir = os.path.abspath(self.bdist_dir)
27393f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard
274fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        root = Directory(db, cab, None, rootdir, "TARGETDIR", "SourceDir")
27593f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        f = Feature(db, "Python", "Python", "Everything",
27693f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                    0, 1, directory="TARGETDIR")
27793f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard
27893f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        items = [(f, root, '')]
27993f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        for version in self.versions + [self.other_version]:
28093f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            target = "TARGETDIR" + version
28193f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            name = default = "Python" + version
28293f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            desc = "Everything"
28393f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            if version is self.other_version:
28493f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                title = "Python from another location"
28593f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                level = 2
28693f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            else:
28793f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                title = "Python %s from registry" % version
28893f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                level = 1
28993f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            f = Feature(db, name, title, desc, 1, level, directory=target)
29093f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            dir = Directory(db, cab, root, rootdir, target, default)
29193f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            items.append((f, dir, version))
292fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        db.Commit()
29393f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard
29493f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        seen = {}
29593f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        for feature, dir, version in items:
29693f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            todo = [dir]
29793f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            while todo:
29893f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                dir = todo.pop()
29993f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                for file in os.listdir(dir.absolute):
30093f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                    afile = os.path.join(dir.absolute, file)
30193f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                    if os.path.isdir(afile):
30293f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                        short = "%s|%s" % (dir.make_short(file), file)
30393f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                        default = file + version
30493f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                        newdir = Directory(db, cab, dir, file, default, short)
30593f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                        todo.append(newdir)
30693f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                    else:
30793f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                        if not dir.component:
30893f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                            dir.start_component(dir.logical, feature, 0)
30993f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                        if afile not in seen:
31093f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                            key = seen[afile] = dir.add_file(file)
31193f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                            if file==self.install_script:
31293f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                                if self.install_script_key:
31393f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                                    raise DistutilsOptionError(
31493f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                                          "Multiple files with name %s" % file)
31593f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                                self.install_script_key = '[#%s]' % key
31693f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                        else:
31793f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                            key = seen[afile]
31893f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                            add_data(self.db, "DuplicateFile",
31993f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                                [(key + version, dir.component, key, None, dir.logical)])
320ef83ab9db5bda91bd19224c28aa9267f398bb7bfSteven Bethard            db.Commit()
321fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        cab.commit(db)
322fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
323fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis    def add_find_python(self):
324fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        """Adds code to the installer to compute the location of Python.
325fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
32693f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        Properties PYTHON.MACHINE.X.Y and PYTHON.USER.X.Y will be set from the
32793f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        registry for each version of Python.
32893f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard
32993f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        Properties TARGETDIRX.Y will be set from PYTHON.USER.X.Y if defined,
33093f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        else from PYTHON.MACHINE.X.Y.
33193f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard
33293f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        Properties PYTHONX.Y will be set to TARGETDIRX.Y\\python.exe"""
33393f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard
33493f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        start = 402
33593f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        for ver in self.versions:
33693f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % ver
33793f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            machine_reg = "python.machine." + ver
33893f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            user_reg = "python.user." + ver
33993f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            machine_prop = "PYTHON.MACHINE." + ver
34093f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            user_prop = "PYTHON.USER." + ver
34193f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            machine_action = "PythonFromMachine" + ver
34293f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            user_action = "PythonFromUser" + ver
34393f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            exe_action = "PythonExe" + ver
34493f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            target_dir_prop = "TARGETDIR" + ver
34593f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            exe_prop = "PYTHON" + ver
34683eacca7a601df925a36455119633167a9093cdbMartin v. Löwis            if msilib.Win64:
34783eacca7a601df925a36455119633167a9093cdbMartin v. Löwis                # type: msidbLocatorTypeRawValue + msidbLocatorType64bit
34883eacca7a601df925a36455119633167a9093cdbMartin v. Löwis                Type = 2+16
34983eacca7a601df925a36455119633167a9093cdbMartin v. Löwis            else:
35083eacca7a601df925a36455119633167a9093cdbMartin v. Löwis                Type = 2
35193f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            add_data(self.db, "RegLocator",
35283eacca7a601df925a36455119633167a9093cdbMartin v. Löwis                    [(machine_reg, 2, install_path, None, Type),
35383eacca7a601df925a36455119633167a9093cdbMartin v. Löwis                     (user_reg, 1, install_path, None, Type)])
35493f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            add_data(self.db, "AppSearch",
35593f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                    [(machine_prop, machine_reg),
35693f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                     (user_prop, user_reg)])
357fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            add_data(self.db, "CustomAction",
35893f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                    [(machine_action, 51+256, target_dir_prop, "[" + machine_prop + "]"),
35993f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                     (user_action, 51+256, target_dir_prop, "[" + user_prop + "]"),
36093f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                     (exe_action, 51+256, exe_prop, "[" + target_dir_prop + "]\\python.exe"),
36193f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                    ])
362fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            add_data(self.db, "InstallExecuteSequence",
36393f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                    [(machine_action, machine_prop, start),
36493f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                     (user_action, user_prop, start + 1),
36593f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                     (exe_action, None, start + 2),
36693f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                    ])
36793f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            add_data(self.db, "InstallUISequence",
36893f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                    [(machine_action, machine_prop, start),
36993f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                     (user_action, user_prop, start + 1),
37093f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                     (exe_action, None, start + 2),
37193f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                    ])
37293f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            add_data(self.db, "Condition",
37393f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                    [("Python" + ver, 0, "NOT TARGETDIR" + ver)])
37493f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            start += 4
37593f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            assert start < 500
37693f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard
37793f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard    def add_scripts(self):
37893f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        if self.install_script:
37993f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            start = 6800
38093f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            for ver in self.versions + [self.other_version]:
38193f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                install_action = "install_script." + ver
38293f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                exe_prop = "PYTHON" + ver
38393f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                add_data(self.db, "CustomAction",
38493f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                        [(install_action, 50, exe_prop, self.install_script_key)])
38593f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                add_data(self.db, "InstallExecuteSequence",
38693f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                        [(install_action, "&Python%s=3" % ver, start)])
38793f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                start += 1
38893f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        # XXX pre-install scripts are currently refused in finalize_options()
38993f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        #     but if this feature is completed, it will also need to add
39093f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        #     entries for each version as the above code does
391fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        if self.pre_install_script:
392fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            scriptfn = os.path.join(self.bdist_dir, "preinstall.bat")
393fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            f = open(scriptfn, "w")
394fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            # The batch file will be executed with [PYTHON], so that %1
395fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            # is the path to the Python interpreter; %0 will be the path
396fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            # of the batch file.
397fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            # rem ="""
398fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            # %1 %0
399fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            # exit
400fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            # """
401fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            # <actual script>
402fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            f.write('rem ="""\n%1 %0\nexit\n"""\n')
403fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            f.write(open(self.pre_install_script).read())
404fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            f.close()
405fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            add_data(self.db, "Binary",
406fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                [("PreInstall", msilib.Binary(scriptfn))
407fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                ])
408fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            add_data(self.db, "CustomAction",
409fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                [("PreInstall", 2, "PreInstall", None)
410fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                ])
411fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis            add_data(self.db, "InstallExecuteSequence",
412fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                    [("PreInstall", "NOT Installed", 450)])
413fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
414fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
415fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis    def add_ui(self):
416fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        db = self.db
417fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        x = y = 50
418fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        w = 370
419fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        h = 300
420fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        title = "[ProductName] Setup"
421fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
422fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # see "Dialog Style Bits"
423fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        modal = 3      # visible | modal
424fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        modeless = 1   # visible
4253679727939a9d25ccfe057e71e8a4b8be73d47ceTarek Ziadé        track_disk_space = 32
426fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
427fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # UI customization properties
428fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        add_data(db, "Property",
429fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                 # See "DefaultUIFont Property"
430fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                 [("DefaultUIFont", "DlgFont8"),
431fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                  # See "ErrorDialog Style Bit"
432fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                  ("ErrorDialog", "ErrorDlg"),
433fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                  ("Progress1", "Install"),   # modified in maintenance type dlg
434fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                  ("Progress2", "installs"),
435fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                  ("MaintenanceForm_Action", "Repair"),
436fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                  # possible values: ALL, JUSTME
437fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                  ("WhichUsers", "ALL")
438fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                 ])
439fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
440fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # Fonts, see "TextStyle Table"
441fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        add_data(db, "TextStyle",
442fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                 [("DlgFont8", "Tahoma", 9, None, 0),
443fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                  ("DlgFontBold8", "Tahoma", 8, None, 1), #bold
444fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                  ("VerdanaBold10", "Verdana", 10, None, 1),
445fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                  ("VerdanaRed9", "Verdana", 9, 255, 0),
446fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                 ])
447fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
448fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table"
449fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # Numbers indicate sequence; see sequence.py for how these action integrate
450fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        add_data(db, "InstallUISequence",
451fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                 [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140),
452fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                  ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141),
453fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                  # In the user interface, assume all-users installation if privileged.
45493f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                  ("SelectFeaturesDlg", "Not Installed", 1230),
455fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                  # XXX no support for resume installations yet
456fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                  #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240),
457fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                  ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250),
458fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                  ("ProgressDlg", None, 1280)])
459fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
460477c8d5e70240744d24631b18341ad892c8a8e1cThomas Wouters        add_data(db, 'ActionText', text.ActionText)
461477c8d5e70240744d24631b18341ad892c8a8e1cThomas Wouters        add_data(db, 'UIText', text.UIText)
462fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        #####################################################################
463fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # Standard dialogs: FatalError, UserExit, ExitDialog
464fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title,
465fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                     "Finish", "Finish", "Finish")
466fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        fatal.title("[ProductName] Installer ended prematurely")
467fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        fatal.back("< Back", "Finish", active = 0)
468fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        fatal.cancel("Cancel", "Back", active = 0)
469fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        fatal.text("Description1", 15, 70, 320, 80, 0x30003,
470fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                   "[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.")
471fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        fatal.text("Description2", 15, 155, 320, 20, 0x30003,
472fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                   "Click the Finish button to exit the Installer.")
473fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c=fatal.next("Finish", "Cancel", name="Finish")
474fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("EndDialog", "Exit")
475fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
476fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title,
477fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                     "Finish", "Finish", "Finish")
478fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        user_exit.title("[ProductName] Installer was interrupted")
479fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        user_exit.back("< Back", "Finish", active = 0)
480fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        user_exit.cancel("Cancel", "Back", active = 0)
481fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        user_exit.text("Description1", 15, 70, 320, 80, 0x30003,
482fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                   "[ProductName] setup was interrupted.  Your system has not been modified.  "
483fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                   "To install this program at a later time, please run the installation again.")
484fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        user_exit.text("Description2", 15, 155, 320, 20, 0x30003,
485fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                   "Click the Finish button to exit the Installer.")
486fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c = user_exit.next("Finish", "Cancel", name="Finish")
487fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("EndDialog", "Exit")
488fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
489fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title,
490fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                             "Finish", "Finish", "Finish")
491fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        exit_dialog.title("Completing the [ProductName] Installer")
492fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        exit_dialog.back("< Back", "Finish", active = 0)
493fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        exit_dialog.cancel("Cancel", "Back", active = 0)
494fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        exit_dialog.text("Description", 15, 235, 320, 20, 0x30003,
495fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                   "Click the Finish button to exit the Installer.")
496fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c = exit_dialog.next("Finish", "Cancel", name="Finish")
497fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("EndDialog", "Return")
498fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
499fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        #####################################################################
500fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # Required dialog: FilesInUse, ErrorDlg
501fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        inuse = PyDialog(db, "FilesInUse",
502fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                         x, y, w, h,
503fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                         19,                # KeepModeless|Modal|Visible
504fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                         title,
505fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                         "Retry", "Retry", "Retry", bitmap=False)
506fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        inuse.text("Title", 15, 6, 200, 15, 0x30003,
507fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                   r"{\DlgFontBold8}Files in Use")
508fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        inuse.text("Description", 20, 23, 280, 20, 0x30003,
509fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis               "Some files that need to be updated are currently in use.")
510fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        inuse.text("Text", 20, 55, 330, 50, 3,
511fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                   "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.")
512fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess",
513fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                      None, None, None)
514fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c=inuse.back("Exit", "Ignore", name="Exit")
515fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("EndDialog", "Exit")
516fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c=inuse.next("Ignore", "Retry", name="Ignore")
517fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("EndDialog", "Ignore")
518fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c=inuse.cancel("Retry", "Exit", name="Retry")
519fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("EndDialog","Retry")
520fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
521fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # See "Error Dialog". See "ICE20" for the required names of the controls.
522fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        error = Dialog(db, "ErrorDlg",
523fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                       50, 10, 330, 101,
524fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                       65543,       # Error|Minimize|Modal|Visible
525fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                       title,
526fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                       "ErrorText", None, None)
527fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        error.text("ErrorText", 50,9,280,48,3, "")
528fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        #error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None)
529fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo")
530fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes")
531fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort")
532fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel")
533fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore")
534fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk")
535fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry")
536fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
537fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        #####################################################################
538fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # Global "Query Cancel" dialog
539fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title,
540fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                        "No", "No", "No")
541fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        cancel.text("Text", 48, 15, 194, 30, 3,
542fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                    "Are you sure you want to cancel [ProductName] installation?")
543fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        #cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
544fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        #               "py.ico", None, None)
545fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No")
546fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("EndDialog", "Exit")
547fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
548fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes")
549fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("EndDialog", "Return")
550fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
551fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        #####################################################################
552fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # Global "Wait for costing" dialog
553fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title,
554fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                         "Return", "Return", "Return")
555fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        costing.text("Text", 48, 15, 194, 30, 3,
556fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                     "Please wait while the installer finishes determining your disk space requirements.")
557fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None)
558fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("EndDialog", "Exit")
559fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
560fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        #####################################################################
561fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # Preparation dialog: no user input except cancellation
562fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title,
563fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                        "Cancel", "Cancel", "Cancel")
564fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        prep.text("Description", 15, 70, 320, 40, 0x30003,
565fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                  "Please wait while the Installer prepares to guide you through the installation.")
566fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        prep.title("Welcome to the [ProductName] Installer")
567fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c=prep.text("ActionText", 15, 110, 320, 20, 0x30003, "Pondering...")
568fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.mapping("ActionText", "Text")
569fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c=prep.text("ActionData", 15, 135, 320, 30, 0x30003, None)
570fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.mapping("ActionData", "Text")
571fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        prep.back("Back", None, active=0)
572fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        prep.next("Next", None, active=0)
573fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c=prep.cancel("Cancel", None)
574fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("SpawnDialog", "CancelDlg")
575fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
576fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        #####################################################################
57793f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        # Feature (Python directory) selection
57893f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        seldlg = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal, title,
579fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                        "Next", "Next", "Cancel")
58093f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        seldlg.title("Select Python Installations")
581fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
58293f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        seldlg.text("Hint", 15, 30, 300, 20, 3,
58393f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                    "Select the Python locations where %s should be installed."
58493f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                    % self.distribution.get_fullname())
585fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
586fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        seldlg.back("< Back", None, active=0)
587fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c = seldlg.next("Next >", "Cancel")
58893f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        order = 1
58993f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        c.event("[TARGETDIR]", "[SourceDir]", ordering=order)
59093f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        for version in self.versions + [self.other_version]:
59193f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            order += 1
59293f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            c.event("[TARGETDIR]", "[TARGETDIR%s]" % version,
59393f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                    "FEATURE_SELECTED AND &Python%s=3" % version,
59493f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                    ordering=order)
59593f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        c.event("SpawnWaitDialog", "WaitForCostingDlg", ordering=order + 1)
59693f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        c.event("EndDialog", "Return", ordering=order + 2)
59793f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        c = seldlg.cancel("Cancel", "Features")
598fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("SpawnDialog", "CancelDlg")
599fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
60093f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        c = seldlg.control("Features", "SelectionTree", 15, 60, 300, 120, 3,
60193f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                           "FEATURE", None, "PathEdit", None)
60293f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        c.event("[FEATURE_SELECTED]", "1")
60393f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        ver = self.other_version
60493f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        install_other_cond = "FEATURE_SELECTED AND &Python%s=3" % ver
60593f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        dont_install_other_cond = "FEATURE_SELECTED AND &Python%s<>3" % ver
60693f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard
60793f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        c = seldlg.text("Other", 15, 200, 300, 15, 3,
60893f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                        "Provide an alternate Python location")
60993f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        c.condition("Enable", install_other_cond)
61093f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        c.condition("Show", install_other_cond)
61193f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        c.condition("Disable", dont_install_other_cond)
61293f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        c.condition("Hide", dont_install_other_cond)
61393f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard
61493f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        c = seldlg.control("PathEdit", "PathEdit", 15, 215, 300, 16, 1,
61593f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                           "TARGETDIR" + ver, None, "Next", None)
61693f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        c.condition("Enable", install_other_cond)
61793f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        c.condition("Show", install_other_cond)
61893f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        c.condition("Disable", dont_install_other_cond)
61993f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        c.condition("Hide", dont_install_other_cond)
620fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
621fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        #####################################################################
622fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # Disk cost
623fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title,
624fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                        "OK", "OK", "OK", bitmap=False)
625fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        cost.text("Title", 15, 6, 200, 15, 0x30003,
62644b548dda872c0d4f30afd6b44fd74b053a55ad8R David Murray                 r"{\DlgFontBold8}Disk Space Requirements")
627fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        cost.text("Description", 20, 20, 280, 20, 0x30003,
628fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                  "The disk space required for the installation of the selected features.")
629fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        cost.text("Text", 20, 53, 330, 60, 3,
630fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                  "The highlighted volumes (if any) do not have enough disk space "
631fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis              "available for the currently selected features.  You can either "
632fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis              "remove some files from the highlighted volumes, or choose to "
633fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis              "install less features onto local drive(s), or select different "
634fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis              "destination drive(s).")
635fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223,
636fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                     None, "{120}{70}{70}{70}{70}", None, None)
637fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return")
638fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
639fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        #####################################################################
640fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # WhichUsers Dialog. Only available on NT, and for privileged users.
641fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # This must be run before FindRelatedProducts, because that will
642fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # take into account whether the previous installation was per-user
643fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # or per-machine. We currently don't support going back to this
644fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # dialog after "Next" was selected; to support this, we would need to
645fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # find how to reset the ALLUSERS property, and how to re-run
646fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # FindRelatedProducts.
647fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # On Windows9x, the ALLUSERS property is ignored on the command line
648fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # and in the Property table, but installer fails according to the documentation
649fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # if a dialog attempts to set ALLUSERS.
650fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title,
651fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                            "AdminInstall", "Next", "Cancel")
652fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        whichusers.title("Select whether to install [ProductName] for all users of this computer.")
653fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # A radio group with two options: allusers, justme
654fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        g = whichusers.radiogroup("AdminInstall", 15, 60, 260, 50, 3,
655fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                                  "WhichUsers", "", "Next")
656fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        g.add("ALL", 0, 5, 150, 20, "Install for all users")
657fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        g.add("JUSTME", 0, 25, 150, 20, "Install just for me")
658fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
659fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        whichusers.back("Back", None, active=0)
660fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
661fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c = whichusers.next("Next >", "Cancel")
662fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1)
663477c8d5e70240744d24631b18341ad892c8a8e1cThomas Wouters        c.event("EndDialog", "Return", ordering = 2)
664fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
665fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c = whichusers.cancel("Cancel", "AdminInstall")
666fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("SpawnDialog", "CancelDlg")
667fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
668fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        #####################################################################
669fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # Installation Progress dialog (modeless)
670fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title,
671fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                            "Cancel", "Cancel", "Cancel", bitmap=False)
672fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        progress.text("Title", 20, 15, 200, 15, 0x30003,
67344b548dda872c0d4f30afd6b44fd74b053a55ad8R David Murray                     r"{\DlgFontBold8}[Progress1] [ProductName]")
674fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        progress.text("Text", 35, 65, 300, 30, 3,
675fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                      "Please wait while the Installer [Progress2] [ProductName]. "
676fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                      "This may take several minutes.")
677fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:")
678fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
679fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...")
680fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.mapping("ActionText", "Text")
681fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
682fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        #c=progress.text("ActionData", 35, 140, 300, 20, 3, None)
683fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        #c.mapping("ActionData", "Text")
684fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
685fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537,
686fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                           None, "Progress done", None, None)
687fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.mapping("SetProgress", "Progress")
688fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
689fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        progress.back("< Back", "Next", active=False)
690fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        progress.next("Next >", "Cancel", active=False)
691fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg")
692fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
693fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        ###################################################################
694fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # Maintenance type: repair/uninstall
695fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title,
696fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                         "Next", "Next", "Cancel")
697fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        maint.title("Welcome to the [ProductName] Setup Wizard")
698fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        maint.text("BodyText", 15, 63, 330, 42, 3,
699fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                   "Select whether you want to repair or remove [ProductName].")
700fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        g=maint.radiogroup("RepairRadioGroup", 15, 108, 330, 60, 3,
701fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis                            "MaintenanceForm_Action", "", "Next")
702fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        #g.add("Change", 0, 0, 200, 17, "&Change [ProductName]")
703fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]")
704fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]")
705fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
706fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        maint.back("< Back", None, active=False)
707fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c=maint.next("Finish", "Cancel")
708fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # Change installation: Change progress dialog to "Change", then ask
709fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # for feature selection
710fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        #c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1)
711fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        #c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2)
712fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
713fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # Reinstall: Change progress dialog to "Repair", then invoke reinstall
714fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # Also set list of reinstalled features to "ALL"
715fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5)
716fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6)
717fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7)
718fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8)
719fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
720fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # Uninstall: Change progress to "Remove", then invoke uninstall
721fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # Also set list of removed features to "ALL"
722fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11)
723fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12)
724fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13)
725fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14)
726fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
727fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # Close dialog when maintenance action scheduled
728fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20)
729fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        #c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21)
730fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
731fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg")
732fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis
733fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis    def get_installer_filename(self, fullname):
734fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        # Factored out to allow overriding in subclasses
73593f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        if self.target_version:
73693f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            base_name = "%s.%s-py%s.msi" % (fullname, self.plat_name,
73793f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard                                            self.target_version)
73893f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard        else:
73993f65a177b36396dddd1e2938cc037288a7eb400Steven Bethard            base_name = "%s.%s.msi" % (fullname, self.plat_name)
7405e69685999c0f44af3536ac71a2a59e70b7ea185Christian Heimes        installer_name = os.path.join(self.dist_dir, base_name)
741fbab90e95bf6403269d702e8e85ef3a6eceafbb0Martin v. Löwis        return installer_name
742